diff --git a/integration/as7-eap6/adapter/pom.xml b/integration/as7-eap6/adapter/pom.xml
new file mode 100755
index 0000000000..b4ad78f447
--- /dev/null
+++ b/integration/as7-eap6/adapter/pom.xml
@@ -0,0 +1,85 @@
+
+
+
+ keycloak-parent
+ org.keycloak
+ 1.0-alpha-1
+ ../../../pom.xml
+
+ 4.0.0
+
+ keycloak-as7-adapter
+ Keycloak AS7 Integration
+
+
+
+
+ org.jboss.logging
+ jboss-logging
+ 3.1.2.GA
+ provided
+
+
+ org.keycloak
+ keycloak-core
+ ${project.version}
+ provided
+
+
+ org.jboss.resteasy
+ jose-jwt
+
+
+ org.jboss.spec.javax.servlet
+ jboss-servlet-api_3.0_spec
+ provided
+ 1.0.0.Final
+
+
+ org.jboss.resteasy
+ resteasy-jaxrs
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-client
+ provided
+
+
+
+ org.jboss.web
+ jbossweb
+ 7.0.17.Final
+ provided
+
+
+ org.jboss.as
+ jboss-as-web
+ 7.1.2.Final
+
+
+ org.picketbox
+ picketbox
+ provided
+ 4.0.7.Final
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.6
+
+
+
+
+
+
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/Actions.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/Actions.java
new file mode 100755
index 0000000000..65c9ddbc9d
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/Actions.java
@@ -0,0 +1,14 @@
+package org.keycloak.adapters.as7;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public interface Actions
+{
+ public static final String J_OAUTH_ADMIN_FORCED_LOGOUT = "j_oauth_admin_forced_logout";
+ public static final String J_OAUTH_LOGOUT = "j_oauth_logout";
+ public static final String J_OAUTH_RESOLVE_ACCESS_CODE = "j_oauth_resolve_access_code";
+ public static final String J_OAUTH_REMOTE_LOGOUT = "j_oauth_remote_logout";
+ public static final String J_OAUTH_TOKEN_GRANT = "j_oauth_token_grant";
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java
new file mode 100755
index 0000000000..2f7e95cc91
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/BearerTokenAuthenticatorValve.java
@@ -0,0 +1,87 @@
+package org.keycloak.adapters.as7;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.authenticator.AuthenticatorBase;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.deploy.LoginConfig;
+import org.jboss.logging.Logger;
+import org.keycloak.ResourceMetadata;
+import org.keycloak.adapters.as7.config.ManagedResourceConfig;
+import org.keycloak.adapters.as7.config.ManagedResourceConfigLoader;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.security.auth.login.LoginException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Uses a configured remote auth server to do Bearer token authentication only. SkeletonKeyTokens are used
+ * to provide user data and role mappings.
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class BearerTokenAuthenticatorValve extends AuthenticatorBase implements LifecycleListener
+{
+ private static final Logger log = Logger.getLogger(BearerTokenAuthenticatorValve.class);
+ protected ManagedResourceConfig remoteSkeletonKeyConfig;
+ protected ResourceMetadata resourceMetadata;
+
+ @Override
+ public void start() throws LifecycleException
+ {
+ super.start();
+ StandardContext standardContext = (StandardContext)context;
+ standardContext.addLifecycleListener(this);
+ }
+
+ @Override
+ public void lifecycleEvent(LifecycleEvent event)
+ {
+ if (event.getType() == Lifecycle.AFTER_START_EVENT) init();
+ }
+
+ protected void init()
+ {
+ ManagedResourceConfigLoader managedResourceConfigLoader = new ManagedResourceConfigLoader(context);
+ resourceMetadata = managedResourceConfigLoader.getResourceMetadata();
+ remoteSkeletonKeyConfig = managedResourceConfigLoader.getRemoteSkeletonKeyConfig();
+ }
+
+ @Override
+ public void invoke(Request request, Response response) throws IOException, ServletException
+ {
+ try
+ {
+ super.invoke(request, response);
+ }
+ finally
+ {
+ ResteasyProviderFactory.clearContextData(); // to clear push of SkeletonKeySession
+ }
+ }
+
+ @Override
+ protected boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException
+ {
+ try
+ {
+ CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(resourceMetadata, !remoteSkeletonKeyConfig.isCancelPropagation(), true);
+ if (bearer.login(request, response))
+ {
+ return true;
+ }
+ return false;
+ }
+ catch (LoginException e)
+ {
+ }
+ return false;
+ }
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java
new file mode 100755
index 0000000000..7d14c1d307
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaBearerTokenAuthenticator.java
@@ -0,0 +1,163 @@
+package org.keycloak.adapters.as7;
+
+import org.apache.catalina.connector.Request;
+import org.jboss.logging.Logger;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.ResourceMetadata;
+import org.keycloak.SkeletonKeyPrincipal;
+import org.keycloak.SkeletonKeySession;
+import org.keycloak.VerificationException;
+import org.keycloak.representations.SkeletonKeyToken;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.security.auth.login.LoginException;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.util.Set;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class CatalinaBearerTokenAuthenticator
+{
+ protected ResourceMetadata resourceMetadata;
+ protected boolean challenge;
+ protected Logger log = Logger.getLogger(CatalinaBearerTokenAuthenticator.class);
+ protected String tokenString;
+ protected SkeletonKeyToken token;
+ private Principal principal;
+ protected boolean propagateToken;
+
+ public CatalinaBearerTokenAuthenticator(ResourceMetadata resourceMetadata, boolean propagateToken, boolean challenge)
+ {
+ this.resourceMetadata = resourceMetadata;
+ this.challenge = challenge;
+ this.propagateToken = propagateToken;
+ }
+
+ public ResourceMetadata getResourceMetadata()
+ {
+ return resourceMetadata;
+ }
+
+ public String getTokenString()
+ {
+ return tokenString;
+ }
+
+ public SkeletonKeyToken getToken()
+ {
+ return token;
+ }
+
+ public Principal getPrincipal()
+ {
+ return principal;
+ }
+
+ public boolean login(Request request, HttpServletResponse response) throws LoginException, IOException
+ {
+ String authHeader = request.getHeader("Authorization");
+ if (authHeader == null)
+ {
+ if (challenge)
+ {
+ challengeResponse(response, null, null);
+ return false;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ String[] split = authHeader.trim().split("\\s+");
+ if (split == null || split.length != 2) challengeResponse(response, null, null);
+ if (!split[0].equalsIgnoreCase("Bearer")) challengeResponse(response, null, null);
+
+
+ tokenString = split[1];
+
+ try
+ {
+ token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata);
+ }
+ catch (VerificationException e)
+ {
+ log.error("Failed to verify token", e);
+ challengeResponse(response, "invalid_token", e.getMessage());
+ }
+ boolean verifyCaller = false;
+ Set roles = null;
+ if (resourceMetadata.getResourceName() != null)
+ {
+ SkeletonKeyToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
+ if (access != null) roles = access.getRoles();
+ verifyCaller = token.isVerifyCaller(resourceMetadata.getResourceName());
+ }
+ else
+ {
+ verifyCaller = token.isVerifyCaller();
+ SkeletonKeyToken.Access access = token.getRealmAccess();
+ if (access != null) roles = access.getRoles();
+ }
+ String surrogate = null;
+ if (verifyCaller)
+ {
+ if (token.getTrustedCertificates() == null || token.getTrustedCertificates().size() == 0)
+ {
+ response.sendError(400);
+ throw new LoginException("No trusted certificates in token");
+ }
+ // for now, we just make sure JBoss Web did two-way SSL
+ // assume JBoss Web verifies the client cert
+ X509Certificate[] chain = request.getCertificateChain();
+ if (chain == null || chain.length == 0)
+ {
+ response.sendError(400);
+ throw new LoginException("No certificates provided by jboss web to verify the caller");
+ }
+ surrogate = chain[0].getSubjectX500Principal().getName();
+ }
+ SkeletonKeyPrincipal skeletonKeyPrincipal = new SkeletonKeyPrincipal(token.getPrincipal(), surrogate);
+ principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skeletonKeyPrincipal, roles);
+ request.setUserPrincipal(principal);
+ request.setAuthType("OAUTH_BEARER");
+ if (propagateToken)
+ {
+ SkeletonKeySession skSession = new SkeletonKeySession(tokenString, resourceMetadata);
+ request.setAttribute(SkeletonKeySession.class.getName(), skSession);
+ ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
+ }
+
+ return true;
+ }
+
+
+ protected void challengeResponse(HttpServletResponse response, String error, String description) throws LoginException
+ {
+ StringBuilder header = new StringBuilder("Bearer realm=\"");
+ header.append(resourceMetadata.getRealm()).append("\"");
+ if (error != null)
+ {
+ header.append(", error=\"").append(error).append("\"");
+ }
+ if (description != null)
+ {
+ header.append(", error_description=\"").append(description).append("\"");
+ }
+ response.setHeader("WWW-Authenticate", header.toString());
+ try
+ {
+ response.sendError(401);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ throw new LoginException("Challenged");
+ }
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaSecurityContextHelper.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaSecurityContextHelper.java
new file mode 100755
index 0000000000..bf783bd295
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/CatalinaSecurityContextHelper.java
@@ -0,0 +1,138 @@
+package org.keycloak.adapters.as7;
+
+import org.apache.catalina.Realm;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.jboss.as.web.security.JBossGenericPrincipal;
+import org.jboss.security.NestableGroup;
+import org.jboss.security.SecurityConstants;
+import org.jboss.security.SecurityContext;
+import org.jboss.security.SecurityContextAssociation;
+import org.jboss.security.SimpleGroup;
+import org.jboss.security.SimplePrincipal;
+
+import javax.security.auth.Subject;
+import java.security.Principal;
+import java.security.acl.Group;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class CatalinaSecurityContextHelper
+{
+ public GenericPrincipal createPrincipal(Realm realm, Principal identity, Collection roleSet)
+ {
+ Subject subject = new Subject();
+ String credentials = "";
+ Set principals = subject.getPrincipals();
+ principals.add(identity);
+ Group[] roleSets = getRoleSets(roleSet);
+ for(int g = 0; g < roleSets.length; g ++)
+ {
+ Group group = roleSets[g];
+ String name = group.getName();
+ Group subjectGroup = createGroup(name, principals);
+ if( subjectGroup instanceof NestableGroup)
+ {
+ /* A NestableGroup only allows Groups to be added to it so we
+ need to add a SimpleGroup to subjectRoles to contain the roles
+ */
+ SimpleGroup tmp = new SimpleGroup("Roles");
+ subjectGroup.addMember(tmp);
+ subjectGroup = tmp;
+ }
+ // Copy the group members to the Subject group
+ Enumeration extends Principal> members = group.members();
+ while( members.hasMoreElements() )
+ {
+ Principal role = (Principal) members.nextElement();
+ subjectGroup.addMember(role);
+ }
+ }
+ // add the CallerPrincipal group if none has been added in getRoleSets
+ Group callerGroup = new SimpleGroup(SecurityConstants.CALLER_PRINCIPAL_GROUP);
+ callerGroup.addMember(identity);
+ principals.add(callerGroup);
+ SecurityContext sc = SecurityContextAssociation.getSecurityContext();
+ Principal userPrincipal = getPrincipal(subject);
+ sc.getUtil().createSubjectInfo(userPrincipal, credentials, subject);
+ List rolesAsStringList = new ArrayList();
+ rolesAsStringList.addAll(roleSet);
+ return new JBossGenericPrincipal(realm, userPrincipal.getName(), null, rolesAsStringList,
+ userPrincipal, null, credentials, null, subject);
+
+ }
+ /**
+ * Get the Principal given the authenticated Subject. Currently the first principal that is not of type {@code Group} is
+ * considered or the single principal inside the CallerPrincipal group.
+ *
+ * @param subject
+ * @return the authenticated principal
+ */
+ protected Principal getPrincipal(Subject subject) {
+ Principal principal = null;
+ Principal callerPrincipal = null;
+ if (subject != null) {
+ Set principals = subject.getPrincipals();
+ if (principals != null && !principals.isEmpty()) {
+ for (Principal p : principals) {
+ if (!(p instanceof Group) && principal == null) {
+ principal = p;
+ }
+ if (p instanceof Group) {
+ Group g = Group.class.cast(p);
+ if (g.getName().equals(SecurityConstants.CALLER_PRINCIPAL_GROUP) && callerPrincipal == null) {
+ Enumeration extends Principal> e = g.members();
+ if (e.hasMoreElements())
+ callerPrincipal = e.nextElement();
+ }
+ }
+ }
+ }
+ }
+ return callerPrincipal == null ? principal : callerPrincipal;
+ }
+
+ protected Group createGroup(String name, Set principals)
+ {
+ Group roles = null;
+ Iterator iter = principals.iterator();
+ while( iter.hasNext() )
+ {
+ Object next = iter.next();
+ if( (next instanceof Group) == false )
+ continue;
+ Group grp = (Group) next;
+ if( grp.getName().equals(name) )
+ {
+ roles = grp;
+ break;
+ }
+ }
+ // If we did not find a group create one
+ if( roles == null )
+ {
+ roles = new SimpleGroup(name);
+ principals.add(roles);
+ }
+ return roles;
+ }
+
+ protected Group[] getRoleSets(Collection roleSet)
+ {
+ SimpleGroup roles = new SimpleGroup("Roles");
+ Group[] roleSets = {roles};
+ for (String role : roleSet)
+ {
+ roles.addMember(new SimplePrincipal(role));
+ }
+ return roleSets;
+ }
+
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthAuthenticationServerValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthAuthenticationServerValve.java
new file mode 100755
index 0000000000..abc1dca240
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthAuthenticationServerValve.java
@@ -0,0 +1,1050 @@
+package org.keycloak.adapters.as7;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.authenticator.FormAuthenticator;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.bouncycastle.openssl.PEMWriter;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.ObjectWriter;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.jboss.resteasy.jose.jws.JWSBuilder;
+import org.jboss.resteasy.jose.jws.JWSInput;
+import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
+import org.jboss.resteasy.jwt.JsonSerialization;
+import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
+import org.jboss.resteasy.plugins.server.servlet.ServletUtil;
+import org.keycloak.EnvUtil;
+import org.keycloak.PemUtils;
+import org.keycloak.ResourceMetadata;
+import org.keycloak.SkeletonKeySession;
+import org.keycloak.adapters.as7.config.AuthServerConfig;
+import org.keycloak.adapters.as7.config.ManagedResourceConfig;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.SkeletonKeyToken;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.jboss.resteasy.spi.ResteasyUriInfo;
+import org.jboss.resteasy.util.BasicAuthHelper;
+
+import javax.security.auth.login.LoginException;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriBuilder;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.security.KeyStore;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Turns a web deployment into an authentication server that follwos the OAuth 2 protocol and Skeleton Key bearer tokens.
+ * Authentication store is backed by a JBoss security domain.
+ *
+ * Servlet FORM authentication that uses the local security domain to authenticate and for role mappings.
+ *
+ * Supports bearer token creation and authentication. The client asking for access must be set up as a valid user
+ * within the security domain.
+ *
+ * If no an OAuth access request, this works like normal FORM authentication and authorization.
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class OAuthAuthenticationServerValve extends FormAuthenticator implements LifecycleListener
+{
+
+
+ public static class AccessCode
+ {
+ protected String id = UUID.randomUUID().toString() + System.currentTimeMillis();
+ protected long expiration;
+ protected SkeletonKeyToken token;
+ protected String client;
+ protected boolean sso;
+ protected String redirect;
+
+ public boolean isExpired()
+ {
+ return expiration != 0 && (System.currentTimeMillis() / 1000) > expiration;
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public long getExpiration()
+ {
+ return expiration;
+ }
+
+ public void setExpiration(long expiration)
+ {
+ this.expiration = expiration;
+ }
+
+ public SkeletonKeyToken getToken()
+ {
+ return token;
+ }
+
+ public void setToken(SkeletonKeyToken token)
+ {
+ this.token = token;
+ }
+
+ public String getClient()
+ {
+ return client;
+ }
+
+ public void setClient(String client)
+ {
+ this.client = client;
+ }
+
+ public boolean isSso()
+ {
+ return sso;
+ }
+
+ public void setSso(boolean sso)
+ {
+ this.sso = sso;
+ }
+
+ public String getRedirect()
+ {
+ return redirect;
+ }
+
+ public void setRedirect(String redirect)
+ {
+ this.redirect = redirect;
+ }
+ }
+
+ protected ConcurrentHashMap accessCodeMap = new ConcurrentHashMap();
+ private static final Logger log = Logger.getLogger(OAuthAuthenticationServerValve.class);
+
+ private static AtomicLong counter = new AtomicLong(1);
+
+ private static String generateId()
+ {
+ return counter.getAndIncrement() + "." + UUID.randomUUID().toString();
+ }
+
+ protected AuthServerConfig skeletonKeyConfig;
+ protected PrivateKey realmPrivateKey;
+ protected PublicKey realmPublicKey;
+ protected String realmPublicKeyPem;
+ protected ResteasyProviderFactory providers;
+ protected ResourceMetadata resourceMetadata;
+ protected UserSessionManagement userSessionManagement = new UserSessionManagement();
+ protected ObjectMapper mapper;
+ protected ObjectWriter accessTokenResponseWriter;
+ protected ObjectWriter mapWriter;
+
+ private static KeyStore loadKeyStore(String filename, String password) throws Exception
+ {
+ KeyStore trustStore = KeyStore.getInstance(KeyStore
+ .getDefaultType());
+ File truststoreFile = new File(filename);
+ FileInputStream trustStream = new FileInputStream(truststoreFile);
+ trustStore.load(trustStream, password.toCharArray());
+ trustStream.close();
+ return trustStore;
+ }
+
+ @Override
+ public void start() throws LifecycleException
+ {
+ super.start();
+ StandardContext standardContext = (StandardContext) context;
+ standardContext.addLifecycleListener(this);
+ }
+
+ @Override
+ public void lifecycleEvent(LifecycleEvent event)
+ {
+ if (event.getType() == Lifecycle.AFTER_START_EVENT) init();
+ }
+
+ protected void init()
+ {
+ mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
+ accessTokenResponseWriter = mapper.writerWithType(AccessTokenResponse.class);
+ mapWriter = mapper.writerWithType(mapper.getTypeFactory().constructMapType(Map.class, String.class, String.class));
+
+ InputStream is = null;
+ String path = context.getServletContext().getInitParameter("skeleton.key.config.file");
+ if (path == null)
+ {
+ is = context.getServletContext().getResourceAsStream("/WEB-INF/resteasy-oauth.json");
+ }
+ else
+ {
+ try
+ {
+ is = new FileInputStream(path);
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ try
+ {
+ skeletonKeyConfig = mapper.readValue(is, AuthServerConfig.class);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ if (skeletonKeyConfig.getLoginRole() == null)
+ {
+ throw new RuntimeException("You must define the login-role in your config file");
+ }
+ if (skeletonKeyConfig.getClientRole() == null)
+ {
+ throw new RuntimeException("You must define the oauth-client-role in your config file");
+ }
+ if (skeletonKeyConfig.getRealmPrivateKey() != null)
+ {
+ try
+ {
+ realmPrivateKey = PemUtils.decodePrivateKey(skeletonKeyConfig.getRealmPrivateKey());
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ if (skeletonKeyConfig.getRealmPublicKey() != null)
+ {
+ try
+ {
+ realmPublicKey = PemUtils.decodePublicKey(skeletonKeyConfig.getRealmPublicKey());
+ realmPublicKeyPem = skeletonKeyConfig.getRealmPublicKey();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ if (skeletonKeyConfig.getRealmKeyStore() != null)
+ {
+ if (skeletonKeyConfig.getRealmKeyAlias() == null) throw new RuntimeException("Must define realm-key-alias");
+ String keystorePath = EnvUtil.replace(skeletonKeyConfig.getRealmKeyStore());
+ try
+ {
+ KeyStore ks = loadKeyStore(keystorePath, skeletonKeyConfig.getRealmKeystorePassword());
+ if (realmPrivateKey == null)
+ {
+ realmPrivateKey = (PrivateKey) ks.getKey(skeletonKeyConfig.getRealmKeyAlias(), skeletonKeyConfig.getRealmPrivateKeyPassword().toCharArray());
+ }
+ if (realmPublicKey == null)
+ {
+ Certificate cert = ks.getCertificate(skeletonKeyConfig.getRealmKeyAlias());
+ realmPublicKey = cert.getPublicKey();
+ }
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ if (realmPublicKey == null) throw new RuntimeException("You have not declared a keystore or public key");
+ if (realmPrivateKey == null) throw new RuntimeException("You have not declared a keystore or private key");
+ if (realmPublicKeyPem == null)
+ {
+ StringWriter sw = new StringWriter();
+ PEMWriter writer = new PEMWriter(sw);
+ try
+ {
+ writer.writeObject(realmPublicKey);
+ writer.flush();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ realmPublicKeyPem = sw.toString();
+ realmPublicKeyPem = PemUtils.removeBeginEnd(realmPublicKeyPem);
+ }
+ providers = new ResteasyProviderFactory();
+ ClassLoader old = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(OAuthAuthenticationServerValve.class.getClassLoader());
+ try
+ {
+ ResteasyProviderFactory.getInstance(); // initialize builtins
+ RegisterBuiltin.register(providers);
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(old);
+ }
+ resourceMetadata = new ResourceMetadata();
+ resourceMetadata.setRealm(skeletonKeyConfig.getRealm());
+ resourceMetadata.setRealmKey(realmPublicKey);
+ String truststore = skeletonKeyConfig.getTruststore();
+ if (truststore != null)
+ {
+ truststore = EnvUtil.replace(truststore);
+ String truststorePassword = skeletonKeyConfig.getTruststorePassword();
+ KeyStore trust = null;
+ try
+ {
+ trust = loadKeyStore(truststore, truststorePassword);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Failed to load truststore", e);
+ }
+ resourceMetadata.setTruststore(trust);
+ }
+ String clientKeystore = skeletonKeyConfig.getClientKeystore();
+ String clientKeyPassword = null;
+ if (clientKeystore != null)
+ {
+ clientKeystore = EnvUtil.replace(clientKeystore);
+ String clientKeystorePassword = skeletonKeyConfig.getClientKeystorePassword();
+ KeyStore serverKS = null;
+ try
+ {
+ serverKS = loadKeyStore(clientKeystore, clientKeystorePassword);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Failed to load keystore", e);
+ }
+ resourceMetadata.setClientKeystore(serverKS);
+ clientKeyPassword = skeletonKeyConfig.getClientKeyPassword();
+ resourceMetadata.setClientKeyPassword(clientKeyPassword);
+ }
+ }
+
+ @Override
+ public void invoke(Request request, Response response) throws IOException, ServletException
+ {
+ try
+ {
+ String contextPath = request.getContextPath();
+ String requestURI = request.getDecodedRequestURI();
+ log.debug("--- invoke: " + requestURI);
+ if (request.getMethod().equalsIgnoreCase("GET")
+ && context.getLoginConfig().getLoginPage().equals(request.getRequestPathMB().toString()))
+ {
+ if (handleLoginPage(request, response)) return;
+ }
+ else if (request.getMethod().equalsIgnoreCase("GET")
+ && requestURI.endsWith(Actions.J_OAUTH_LOGOUT))
+ {
+ logoutCurrentUser(request, response);
+ return;
+ }
+ else if (request.getMethod().equalsIgnoreCase("POST")
+ && requestURI.endsWith(Actions.J_OAUTH_ADMIN_FORCED_LOGOUT))
+ {
+ adminLogout(request, response);
+ return;
+ }
+ else if (request.getMethod().equalsIgnoreCase("POST")
+ && requestURI.startsWith(contextPath) &&
+ requestURI.endsWith(Constants.FORM_ACTION)
+ && request.getParameter("client_id") != null)
+ {
+ handleOAuth(request, response);
+ return;
+ }
+ else if (request.getMethod().equalsIgnoreCase("POST")
+ && requestURI.endsWith(Actions.J_OAUTH_TOKEN_GRANT))
+ {
+ tokenGrant(request, response);
+ return;
+ }
+ else if (request.getMethod().equalsIgnoreCase("POST")
+ && requestURI.startsWith(contextPath) &&
+ requestURI.endsWith(Actions.J_OAUTH_RESOLVE_ACCESS_CODE))
+ {
+ resolveAccessCode(request, response);
+ return;
+ }
+ else if (request.getMethod().equalsIgnoreCase("GET")
+ && requestURI.startsWith(contextPath) &&
+ requestURI.endsWith("j_oauth_realm_info.html"))
+ {
+ publishRealmInfoHtml(request, response);
+ return;
+ }
+ // propagate the skeleton key token string?
+ if (!skeletonKeyConfig.isCancelPropagation())
+ {
+ if (request.getAttribute(SkeletonKeySession.class.getName()) == null && request.getSessionInternal() != null)
+ {
+ SkeletonKeySession skSession = (SkeletonKeySession) request.getSessionInternal().getNote(SkeletonKeySession.class.getName());
+ if (skSession != null)
+ {
+ request.setAttribute(SkeletonKeySession.class.getName(), skSession);
+ ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
+ }
+ }
+ }
+ request.setAttribute("OAUTH_FORM_ACTION", "j_security_check");
+ super.invoke(request, response);
+ }
+ finally
+ {
+ ResteasyProviderFactory.clearContextData(); // to clear push of SkeletonKeySession
+ }
+ }
+
+ protected boolean handleLoginPage(Request request, Response response) throws IOException, ServletException
+ {
+ String client_id = request.getParameter("client_id");
+ // if this is not an OAUTH redirect, just return and let the default flow happen
+ if (client_id == null) return false;
+
+ String redirect_uri = request.getParameter("redirect_uri");
+ String state = request.getParameter("state");
+
+ if (redirect_uri == null)
+ {
+ response.sendError(400, "No oauth redirect query parameter set");
+ return true;
+ }
+ // only bypass authentication if our session is authenticated,
+ // the login query parameter is on request URL,
+ // and we have configured the login-role
+ else if (!skeletonKeyConfig.isSsoDisabled()
+ && request.getSessionInternal() != null
+ && request.getSessionInternal().getPrincipal() != null
+ && request.getParameter("login") != null)
+ {
+ log.debug("We're ALREADY LOGGED IN!!!");
+ GenericPrincipal gp = (GenericPrincipal) request.getSessionInternal().getPrincipal();
+ redirectAccessCode(true, response, redirect_uri, client_id, state, gp);
+ }
+ else
+ {
+ UriBuilder builder = UriBuilder.fromUri("j_security_check")
+ .queryParam("redirect_uri", redirect_uri)
+ .queryParam("client_id", client_id);
+ if (state != null) builder.queryParam("state", state);
+ String loginAction = builder.build().toString();
+ request.setAttribute("OAUTH_FORM_ACTION", loginAction);
+ getNext().invoke(request, response);
+ }
+ return true;
+ }
+
+ protected GenericPrincipal checkLoggedIn(Request request, HttpServletResponse response)
+ {
+ if (request.getPrincipal() != null)
+ {
+ return (GenericPrincipal) request.getPrincipal();
+ }
+ else if (request.getSessionInternal() != null && request.getSessionInternal().getPrincipal() != null)
+ {
+ return (GenericPrincipal) request.getSessionInternal().getPrincipal();
+ }
+ return null;
+ }
+
+
+ protected void adminLogout(Request request, HttpServletResponse response) throws IOException
+ {
+ log.debug("<< adminLogout");
+ GenericPrincipal gp = checkLoggedIn(request, response);
+ if (gp == null)
+ {
+ if (bearer(request, response, false))
+ {
+ gp = (GenericPrincipal) request.getPrincipal();
+ }
+ else
+ {
+ response.sendError(403);
+ return;
+ }
+ }
+ if (!gp.hasRole(skeletonKeyConfig.getAdminRole()))
+ {
+ response.sendError(403);
+ return;
+ }
+ String logoutUser = request.getParameter("user");
+ if (logoutUser != null)
+ {
+ userSessionManagement.logout(logoutUser);
+ logoutResources(logoutUser, gp.getName());
+ }
+ else
+ {
+ userSessionManagement.logoutAllBut(gp.getName());
+ logoutResources(null, gp.getName());
+ }
+ String forwardTo = request.getParameter("forward");
+ if (forwardTo == null)
+ {
+ response.setStatus(204);
+ return;
+ }
+ RequestDispatcher disp =
+ context.getServletContext().getRequestDispatcher(forwardTo);
+ try
+ {
+ disp.forward(request.getRequest(), response);
+ }
+ catch (Throwable t)
+ {
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ "failed to forward");
+ }
+
+
+ }
+
+
+ protected void logoutCurrentUser(Request request, HttpServletResponse response) throws IOException
+ {
+ if (request.getSessionInternal() == null || request.getSessionInternal().getPrincipal() == null)
+ {
+ redirectToWelcomePage(request, response);
+ return;
+ }
+ GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal();
+ String username = principal.getName();
+ String admin = username;
+ userSessionManagement.logout(username);
+ request.setUserPrincipal(null);
+ request.setAuthType(null);
+ // logout user on all declared authenticated resources
+ logoutResources(username, admin);
+ redirectToWelcomePage(request, response);
+ }
+
+ protected void logoutResources(String username, String admin)
+ {
+ if (skeletonKeyConfig.getResources().size() != 0)
+ {
+ SkeletonKeyToken token = new SkeletonKeyToken();
+ token.id(generateId());
+ token.principal(admin);
+ token.audience(skeletonKeyConfig.getRealm());
+ SkeletonKeyToken.Access realmAccess = new SkeletonKeyToken.Access();
+ realmAccess.addRole(skeletonKeyConfig.getAdminRole());
+ token.setRealmAccess(realmAccess);
+ String tokenString = buildTokenString(realmPrivateKey, token);
+ ResteasyClient client = new ResteasyClientBuilder()
+ .providerFactory(providers)
+ .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY)
+ .trustStore(resourceMetadata.getTruststore())
+ .keyStore(resourceMetadata.getClientKeystore(), resourceMetadata.getClientKeyPassword())
+ .build();
+ try
+ {
+ for (String resource : skeletonKeyConfig.getResources())
+ {
+ try
+ {
+ log.debug("logging out: " + resource);
+ WebTarget target = client.target(resource).path(Actions.J_OAUTH_REMOTE_LOGOUT);
+ if (username != null) target = target.queryParam("user", username);
+ javax.ws.rs.core.Response response = target.request()
+ .header("Authorization", "Bearer " + tokenString)
+ .put(null);
+ if (response.getStatus() != 204) log.error("Failed to log out");
+ response.close();
+ }
+ catch (Exception ignored)
+ {
+ log.error("Failed to log out", ignored);
+ }
+ }
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+ }
+
+ protected void redirectToWelcomePage(Request request, HttpServletResponse response) throws IOException
+ {
+ ResteasyUriInfo uriInfo = ServletUtil.extractUriInfo(request, null);
+ String[] welcomes = context.findWelcomeFiles();
+ if (welcomes.length > 0)
+ {
+ UriBuilder welcome = uriInfo.getBaseUriBuilder().path(welcomes[0]);
+ response.sendRedirect(welcome.toTemplate());
+ }
+ else
+ {
+ response.setStatus(204);
+ }
+ }
+
+
+ protected void publishRealmInfoHtml(Request request, HttpServletResponse response) throws IOException
+ {
+ ManagedResourceConfig rep = getRealmRepresentation(request);
+ StringWriter writer;
+ String json;
+
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
+ mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
+
+ StringBuffer html = new StringBuffer();
+ html.append("");
+ html.append("Realm: ").append(rep.getRealm()).append("
");
+
+ ManagedResourceConfig bearer = new ManagedResourceConfig();
+ bearer.setRealm(rep.getRealm());
+ bearer.setRealmKey(rep.getRealmKey());
+ writer = new StringWriter();
+ mapper.writeValue(writer, bearer);
+ json = writer.toString();
+
+ html.append("BearerTokenAuthValve Json Config
");
+ html.append("");
+
+ html.append("
");
+
+ writer = new StringWriter();
+ rep.getClientCredentials().put("password", "REQUIRED");
+ rep.setClientId("REQUIRED");
+ rep.setTruststore("REQUIRED");
+ rep.setTruststorePassword("REQUIRED");
+ mapper.writeValue(writer, rep);
+ json = writer.toString();
+ html.append("OAuthManagedResourceValve Json Config
");
+ html.append("");
+
+ html.append("");
+
+ response.setStatus(200);
+ response.setContentType("text/html");
+ response.getOutputStream().println(html.toString());
+ response.getOutputStream().flush();
+
+ }
+
+
+ protected ManagedResourceConfig getRealmRepresentation(Request request)
+ {
+ ManagedResourceConfig rep = new ManagedResourceConfig();
+ ResteasyUriInfo uriInfo = ServletUtil.extractUriInfo(request, null);
+ UriBuilder authUrl = uriInfo.getBaseUriBuilder().path(context.getLoginConfig().getLoginPage());
+ UriBuilder codeUrl = uriInfo.getBaseUriBuilder().path(Actions.J_OAUTH_RESOLVE_ACCESS_CODE);
+ rep.setRealm(skeletonKeyConfig.getRealm());
+ rep.setRealmKey(realmPublicKeyPem);
+ rep.setAuthUrl(authUrl.toTemplate());
+ rep.setCodeUrl(codeUrl.toTemplate());
+ rep.setAdminRole(skeletonKeyConfig.getAdminRole());
+ return rep;
+ }
+
+ public boolean bearer(Request request, HttpServletResponse response, boolean propagate) throws IOException
+ {
+ if (request.getHeader("Authorization") != null)
+ {
+ CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(resourceMetadata, true, false);
+ try
+ {
+ if (bearer.login(request, response))
+ {
+ return true;
+ }
+ }
+ catch (LoginException e)
+ {
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void register(Request request, HttpServletResponse response, Principal principal, String authType, String username, String password)
+ {
+ super.register(request, response, principal, authType, username, password);
+ log.debug("authenticate userSessionManage.login(): " + principal.getName());
+ userSessionManagement.login(request.getSessionInternal(), principal.getName());
+ if (!skeletonKeyConfig.isCancelPropagation())
+ {
+ GenericPrincipal gp = (GenericPrincipal) request.getPrincipal();
+ if (gp != null)
+ {
+ SkeletonKeyToken token = buildToken(gp);
+ String stringToken = buildTokenString(realmPrivateKey, token);
+ SkeletonKeySession skSession = new SkeletonKeySession(stringToken, resourceMetadata);
+ request.setAttribute(SkeletonKeySession.class.getName(), skSession);
+ ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
+ request.getSessionInternal(true).setNote(SkeletonKeySession.class.getName(), skSession);
+ }
+ }
+ }
+
+ @Override
+ public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException
+ {
+ if (bearer(request, response, true))
+ {
+ return true;
+ }
+ return super.authenticate(request, response, config);
+ }
+
+
+ protected void resolveAccessCode(Request request, Response response) throws IOException
+ {
+ if (!request.isSecure())
+ {
+ response.sendError(400);
+ return;
+ }
+ // always verify code and remove access code from map before authenticating user
+ // if user authentication fails, we want the code to be removed irreguardless just in case we're under attack
+ String code = request.getParameter("code");
+ JWSInput input = new JWSInput(code, providers);
+ boolean verifiedCode = false;
+ try
+ {
+ verifiedCode = RSAProvider.verify(input, realmPublicKey);
+ }
+ catch (Exception ignored)
+ {
+ log.error("Failed to verify signature", ignored);
+ }
+ if (!verifiedCode)
+ {
+ Map res = new HashMap();
+ res.put("error", "invalid_grant");
+ res.put("error_description", "Unable to verify code signature");
+ response.sendError(400);
+ response.setContentType("application/json");
+ mapWriter.writeValue(response.getOutputStream(), res);
+ response.getOutputStream().flush();
+ return;
+ }
+ String key = input.readContent(String.class);
+ AccessCode accessCode = accessCodeMap.remove(key);
+ String redirect = request.getParameter("redirect_uri");
+
+ GenericPrincipal gp = basicAuth(request, response);
+ if (gp == null)
+ {
+ log.error("Failed to authenticate client_id");
+ return;
+ }
+ if (accessCode == null)
+ {
+ log.error("No access code: " + code);
+ response.sendError(400);
+ return;
+ }
+ if (accessCode.isExpired())
+ {
+ log.debug("Access code expired");
+ Map res = new HashMap();
+ res.put("error", "invalid_grant");
+ res.put("error_description", "Code is expired");
+ response.setStatus(400);
+ response.setContentType("application/json");
+ mapWriter.writeValue(response.getOutputStream(), res);
+ response.getOutputStream().flush();
+ return;
+ }
+ if (!accessCode.getToken().isActive())
+ {
+ log.debug("token not active");
+ Map res = new HashMap();
+ res.put("error", "invalid_grant");
+ res.put("error_description", "Token expired");
+ response.setStatus(400);
+ response.setContentType("application/json");
+ mapWriter.writeValue(response.getOutputStream(), res);
+ response.getOutputStream().flush();
+ return;
+ }
+ if (!gp.getName().equals(accessCode.getClient()))
+ {
+ log.debug("not equal client");
+ Map res = new HashMap();
+ res.put("error", "invalid_grant");
+ res.put("error_description", "Auth error");
+ response.setStatus(400);
+ response.setContentType("application/json");
+ mapWriter.writeValue(response.getOutputStream(), res);
+ response.getOutputStream().flush();
+ return;
+ }
+ if (!accessCode.getRedirect().equals(redirect))
+ {
+ log.debug("not equal redirect");
+ Map res = new HashMap();
+ res.put("error", "invalid_grant");
+ res.put("error_description", "Auth error");
+ response.setStatus(400);
+ response.setContentType("application/json");
+ mapWriter.writeValue(response.getOutputStream(), res);
+ response.getOutputStream().flush();
+ return;
+ }
+ if (accessCode.isSso() && !gp.hasRole(skeletonKeyConfig.getLoginRole()))
+ {
+ // we did not authenticate user on an access code request because a session was already established
+ // but, the client_id does not have permission to bypass this on a simple grant. We want
+ // to always ask for credentials from a simple oath request
+
+ log.debug("does not have login permission");
+ Map res = new HashMap();
+ res.put("error", "invalid_grant");
+ res.put("error_description", "Auth error");
+ response.setStatus(400);
+ response.setContentType("application/json");
+ mapWriter.writeValue(response.getOutputStream(), res);
+ response.getOutputStream().flush();
+ return;
+ }
+ else if (!gp.hasRole(skeletonKeyConfig.getClientRole()) && !gp.hasRole(skeletonKeyConfig.getLoginRole()))
+ {
+ log.debug("does not have login or client role permission for access token request");
+ Map res = new HashMap();
+ res.put("error", "invalid_grant");
+ res.put("error_description", "Auth error");
+ response.setStatus(400);
+ response.setContentType("application/json");
+ mapWriter.writeValue(response.getOutputStream(), res);
+ response.getOutputStream().flush();
+ return;
+
+ }
+ String wildcard = skeletonKeyConfig.getWildcardRole() == null ? "*" : skeletonKeyConfig.getWildcardRole();
+ Set codeRoles = accessCode.getToken().getRealmAccess().getRoles();
+ if (codeRoles != null &&
+ (codeRoles.contains(skeletonKeyConfig.getClientRole()) || codeRoles.contains(skeletonKeyConfig.getLoginRole())))
+ {
+ // we store roles a oauth client is granted in the user role mapping, remove those roles as we don't want those clients with those
+ // permissions if they are logging in.
+ Set newRoles = new HashSet();
+ if (codeRoles.contains(skeletonKeyConfig.getClientRole())) newRoles.add(skeletonKeyConfig.getClientRole());
+ if (codeRoles.contains(skeletonKeyConfig.getLoginRole())) newRoles.add(skeletonKeyConfig.getLoginRole());
+ if (codeRoles.contains(wildcard)) newRoles.add(wildcard);
+ codeRoles.clear();
+ codeRoles.addAll(newRoles);
+ }
+
+ // is we have a login role, then we don't need to filter out roles, just grant all the roles the user has
+ // Also, if the client has the "wildcard" role, then we don't need to filter out roles
+ if (codeRoles != null
+ && !gp.hasRole(wildcard)
+ && !gp.hasRole(skeletonKeyConfig.getLoginRole()))
+ {
+ Set clientAllowed = new HashSet();
+ for (String role : gp.getRoles())
+ {
+ clientAllowed.add(role);
+ }
+ Set newRoles = new HashSet();
+ newRoles.addAll(codeRoles);
+ for (String role : newRoles)
+ {
+ if (!clientAllowed.contains(role))
+ {
+ codeRoles.remove(role);
+ }
+ }
+ }
+ AccessTokenResponse res = accessTokenResponse(realmPrivateKey, accessCode.getToken());
+ response.setStatus(200);
+ response.setContentType("application/json");
+ accessTokenResponseWriter.writeValue(response.getOutputStream(), res);
+ response.getOutputStream().flush();
+ }
+
+ protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token)
+ {
+ String encodedToken = buildTokenString(privateKey, token);
+
+ AccessTokenResponse res = new AccessTokenResponse();
+ res.setToken(encodedToken);
+ res.setTokenType("bearer");
+ if (token.getExpiration() != 0)
+ {
+ long time = token.getExpiration() - (System.currentTimeMillis() / 1000);
+ res.setExpiresIn(time);
+ }
+ return res;
+ }
+
+ protected String buildTokenString(PrivateKey privateKey, SkeletonKeyToken token)
+ {
+ byte[] tokenBytes = null;
+ try
+ {
+ tokenBytes = JsonSerialization.toByteArray(token, false);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ return new JWSBuilder()
+ .content(tokenBytes)
+ .rsa256(privateKey);
+ }
+
+
+ protected void handleOAuth(Request request, Response response) throws IOException
+ {
+ log.debug("<--- Begin oauthAuthenticate");
+ String redirect_uri = request.getParameter("redirect_uri");
+ String client_id = request.getParameter("client_id");
+ String state = request.getParameter("state");
+ String username = request.getParameter(Constants.FORM_USERNAME);
+ String password = request.getParameter(Constants.FORM_PASSWORD);
+ Principal principal = context.getRealm().authenticate(username, password);
+ if (principal == null)
+ {
+ UriBuilder builder = UriBuilder.fromUri(redirect_uri).queryParam("error", "unauthorized_client");
+ if (state != null) builder.queryParam("state", state);
+ response.sendRedirect(builder.toTemplate());
+ return;
+ }
+ GenericPrincipal gp = (GenericPrincipal) principal;
+ register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password);
+ userSessionManagement.login(request.getSessionInternal(), username);
+ redirectAccessCode(false, response, redirect_uri, client_id, state, gp);
+
+ return;
+ }
+
+ protected void tokenGrant(Request request, Response response) throws IOException
+ {
+ if (!request.isSecure())
+ {
+ response.sendError(400);
+ return;
+ }
+ GenericPrincipal gp = basicAuth(request, response);
+ if (gp == null) return;
+ SkeletonKeyToken token = buildToken(gp);
+ AccessTokenResponse res = accessTokenResponse(realmPrivateKey, token);
+ response.setStatus(200);
+ response.setContentType("application/json");
+ accessTokenResponseWriter.writeValue(response.getOutputStream(), res);
+ response.getOutputStream().flush();
+ }
+
+ protected GenericPrincipal basicAuth(Request request, Response response) throws IOException
+ {
+ String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
+ if (authHeader == null)
+ {
+ basicAuthError(response);
+ return null;
+ }
+ String[] creds = BasicAuthHelper.parseHeader(authHeader);
+ if (creds == null)
+ {
+ basicAuthError(response);
+ return null;
+ }
+ String username = creds[0];
+ String password = creds[1];
+ GenericPrincipal gp = (GenericPrincipal) context.getRealm().authenticate(username, password);
+ if (gp == null)
+ {
+ basicAuthError(response);
+ return null;
+ }
+ return gp;
+ }
+
+ protected void basicAuthError(Response response) throws IOException
+ {
+ response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"" + context.getLoginConfig().getRealmName() + "\"");
+ response.sendError(401);
+ }
+
+ protected void redirectAccessCode(boolean sso, Response response, String redirect_uri, String client_id, String state, GenericPrincipal gp) throws IOException
+ {
+ SkeletonKeyToken token = buildToken(gp);
+ AccessCode code = new AccessCode();
+ code.setToken(token);
+ code.setClient(client_id);
+ code.setSso(sso);
+ code.setRedirect(redirect_uri);
+ int expiration = skeletonKeyConfig.getAccessCodeLifetime() == 0 ? 300 : skeletonKeyConfig.getAccessCodeLifetime();
+ code.setExpiration((System.currentTimeMillis() / 1000) + expiration);
+ accessCodeMap.put(code.getId(), code);
+ log.debug("--- sign access code");
+ String accessCode = null;
+ try
+ {
+ accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realmPrivateKey);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException(e);
+ }
+ log.debug("--- build redirect");
+ UriBuilder redirectUri = UriBuilder.fromUri(redirect_uri).queryParam("code", accessCode);
+ if (state != null) redirectUri.queryParam("state", state);
+ response.sendRedirect(redirectUri.toTemplate());
+ log.debug("<--- end oauthAuthenticate");
+ }
+
+ protected SkeletonKeyToken buildToken(GenericPrincipal gp)
+ {
+ SkeletonKeyToken token = new SkeletonKeyToken();
+ token.id(generateId());
+ token.principal(gp.getName());
+ token.audience(skeletonKeyConfig.getRealm());
+ int expiration = skeletonKeyConfig.getAccessCodeLifetime() == 0 ? 3600 : skeletonKeyConfig.getAccessCodeLifetime();
+ if (skeletonKeyConfig.getTokenLifetime() > 0)
+ {
+ token.expiration((System.currentTimeMillis() / 1000) + expiration);
+ }
+ SkeletonKeyToken.Access realmAccess = new SkeletonKeyToken.Access();
+ for (String role : gp.getRoles())
+ {
+ realmAccess.addRole(role);
+ }
+ token.setRealmAccess(realmAccess);
+ return token;
+ }
+
+}
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
new file mode 100755
index 0000000000..772607c550
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthManagedResourceValve.java
@@ -0,0 +1,303 @@
+package org.keycloak.adapters.as7;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.authenticator.FormAuthenticator;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.deploy.LoginConfig;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
+import org.keycloak.RealmConfiguration;
+import org.keycloak.ResourceMetadata;
+import org.keycloak.SkeletonKeyPrincipal;
+import org.keycloak.SkeletonKeySession;
+import org.keycloak.adapters.as7.config.ManagedResourceConfig;
+import org.keycloak.adapters.as7.config.ManagedResourceConfigLoader;
+import org.keycloak.representations.SkeletonKeyToken;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.security.auth.login.LoginException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Web deployment whose security is managed by a remote OAuth Skeleton Key authentication server
+ *
+ * Redirects browser to remote authentication server if not logged in. Also allows OAuth Bearer Token requests
+ * that contain a Skeleton Key bearer tokens.
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class OAuthManagedResourceValve extends FormAuthenticator implements LifecycleListener
+{
+ protected RealmConfiguration realmConfiguration;
+ private static final Logger log = Logger.getLogger(OAuthManagedResourceValve.class);
+ protected UserSessionManagement userSessionManagement = new UserSessionManagement();
+ protected ManagedResourceConfig remoteSkeletonKeyConfig;
+ protected ResourceMetadata resourceMetadata;
+
+
+ @Override
+ public void start() throws LifecycleException
+ {
+ super.start();
+ StandardContext standardContext = (StandardContext) context;
+ standardContext.addLifecycleListener(this);
+ }
+
+ @Override
+ public void lifecycleEvent(LifecycleEvent event)
+ {
+ if (event.getType() == Lifecycle.AFTER_START_EVENT) init();
+ }
+
+ protected void init()
+ {
+ ManagedResourceConfigLoader managedResourceConfigLoader = new ManagedResourceConfigLoader(context);
+ resourceMetadata = managedResourceConfigLoader.getResourceMetadata();
+ remoteSkeletonKeyConfig = managedResourceConfigLoader.getRemoteSkeletonKeyConfig();
+ String client_id = remoteSkeletonKeyConfig.getClientId();
+ if (client_id == null)
+ {
+ throw new IllegalArgumentException("Must set client-id to use with auth server");
+ }
+ realmConfiguration = new RealmConfiguration();
+ String authUrl = remoteSkeletonKeyConfig.getAuthUrl();
+ if (authUrl == null)
+ {
+ throw new RuntimeException("You must specify auth-url");
+ }
+ String tokenUrl = remoteSkeletonKeyConfig.getCodeUrl();
+ if (tokenUrl == null)
+ {
+ throw new RuntimeException("You mut specify code-url");
+ }
+ realmConfiguration.setMetadata(resourceMetadata);
+ realmConfiguration.setClientId(client_id);
+
+ for (Map.Entry entry : managedResourceConfigLoader.getRemoteSkeletonKeyConfig().getClientCredentials().entrySet())
+ {
+ realmConfiguration.getCredentials().param(entry.getKey(), entry.getValue());
+ }
+ int size = 10;
+ if (managedResourceConfigLoader.getRemoteSkeletonKeyConfig().getConnectionPoolSize() > 0)
+ size = managedResourceConfigLoader.getRemoteSkeletonKeyConfig().getConnectionPoolSize();
+ ResteasyClientBuilder.HostnameVerificationPolicy policy = ResteasyClientBuilder.HostnameVerificationPolicy.WILDCARD;
+ if (managedResourceConfigLoader.getRemoteSkeletonKeyConfig().isAllowAnyHostname())
+ policy = ResteasyClientBuilder.HostnameVerificationPolicy.ANY;
+ ResteasyProviderFactory providerFactory = new ResteasyProviderFactory();
+ ClassLoader old = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(OAuthManagedResourceValve.class.getClassLoader());
+ try
+ {
+ ResteasyProviderFactory.getInstance(); // initialize builtins
+ RegisterBuiltin.register(providerFactory);
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(old);
+ }
+ ResteasyClient client = new ResteasyClientBuilder()
+ .providerFactory(providerFactory)
+ .connectionPoolSize(size)
+ .hostnameVerification(policy)
+ .trustStore(resourceMetadata.getTruststore())
+ .keyStore(resourceMetadata.getClientKeystore(), resourceMetadata.getClientKeyPassword())
+ .build();
+ realmConfiguration.setClient(client);
+ realmConfiguration.setAuthUrl(UriBuilder.fromUri(authUrl).queryParam("client_id", client_id));
+ realmConfiguration.setCodeUrl(client.target(tokenUrl));
+ }
+
+ @Override
+ public void invoke(Request request, Response response) throws IOException, ServletException
+ {
+ try
+ {
+ String requestURI = request.getDecodedRequestURI();
+ if (requestURI.endsWith("j_oauth_remote_logout"))
+ {
+ remoteLogout(request, response);
+ return;
+ }
+ super.invoke(request, response);
+ }
+ finally
+ {
+ ResteasyProviderFactory.clearContextData(); // to clear push of SkeletonKeySession
+ }
+ }
+
+ @Override
+ public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException
+ {
+ try
+ {
+ if (bearer(false, request, response)) return true;
+ else if (checkLoggedIn(request, response))
+ {
+ if (request.getSessionInternal().getNote(Constants.FORM_REQUEST_NOTE) != null)
+ {
+ if (restoreRequest(request, request.getSessionInternal()))
+ {
+ log.debug("restoreRequest");
+ return (true);
+ }
+ else
+ {
+ log.debug("Restore of original request failed");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+ return (false);
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ // initiate or continue oauth2 protocol
+ oauth(request, response);
+ }
+ catch (LoginException e)
+ {
+ }
+ return false;
+ }
+
+ protected void remoteLogout(Request request, HttpServletResponse response) throws IOException
+ {
+ try
+ {
+ log.debug("->> remoteLogout: ");
+ if (!bearer(true, request, response))
+ {
+ log.debug("remoteLogout: bearer auth failed");
+ return;
+ }
+ GenericPrincipal gp = (GenericPrincipal) request.getPrincipal();
+ if (!gp.hasRole(remoteSkeletonKeyConfig.getAdminRole()))
+ {
+ log.debug("remoteLogout: role failure");
+ response.sendError(403);
+ return;
+ }
+ String user = request.getParameter("user");
+ if (user != null)
+ {
+ userSessionManagement.logout(user);
+ }
+ else
+ {
+ userSessionManagement.logoutAll();
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("failed to logout", e);
+ }
+ response.setStatus(204);
+ }
+
+ protected boolean bearer(boolean challenge, Request request, HttpServletResponse response) throws LoginException, IOException
+ {
+ CatalinaBearerTokenAuthenticator bearer = new CatalinaBearerTokenAuthenticator(realmConfiguration.getMetadata(), !remoteSkeletonKeyConfig.isCancelPropagation(), challenge);
+ if (bearer.login(request, response))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ protected boolean checkLoggedIn(Request request, HttpServletResponse response)
+ {
+ if (request.getSessionInternal() == null || request.getSessionInternal().getPrincipal() == null)
+ return false;
+ log.debug("remote logged in already");
+ GenericPrincipal principal = (GenericPrincipal) request.getSessionInternal().getPrincipal();
+ request.setUserPrincipal(principal);
+ request.setAuthType("OAUTH");
+ Session session = request.getSessionInternal();
+ if (session != null && !remoteSkeletonKeyConfig.isCancelPropagation())
+ {
+ SkeletonKeySession skSession = (SkeletonKeySession) session.getNote(SkeletonKeySession.class.getName());
+ if (skSession != null)
+ {
+ request.setAttribute(SkeletonKeySession.class.getName(), skSession);
+ ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
+
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This method always set the HTTP response, so do not continue after invoking
+ */
+ protected void oauth(Request request, HttpServletResponse response) throws IOException
+ {
+ ServletOAuthLogin oauth = new ServletOAuthLogin(realmConfiguration, request, response, request.getConnector().getRedirectPort());
+ String code = oauth.getCode();
+ if (code == null)
+ {
+ String error = oauth.getError();
+ if (error != null)
+ {
+ response.sendError(400, "OAuth " + error);
+ return;
+ }
+ else
+ {
+ saveRequest(request, request.getSessionInternal(true));
+ oauth.loginRedirect();
+ }
+ return;
+ }
+ else
+ {
+ if (!oauth.resolveCode(code)) return;
+
+ SkeletonKeyToken token = oauth.getToken();
+ Set roles = null;
+ if (resourceMetadata.getResourceName() != null)
+ {
+ SkeletonKeyToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
+ if (access != null) roles = access.getRoles();
+ }
+ else
+ {
+ SkeletonKeyToken.Access access = token.getRealmAccess();
+ if (access != null) roles = access.getRoles();
+ }
+ SkeletonKeyPrincipal skp = new SkeletonKeyPrincipal(token.getPrincipal(), null);
+ GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(context.getRealm(), skp, roles);
+ Session session = request.getSessionInternal(true);
+ session.setPrincipal(principal);
+ session.setAuthType("OAUTH");
+ if (!remoteSkeletonKeyConfig.isCancelPropagation())
+ {
+ SkeletonKeySession skSession = new SkeletonKeySession(oauth.getTokenString(), realmConfiguration.getMetadata());
+ session.setNote(SkeletonKeySession.class.getName(), skSession);
+ }
+
+ String username = token.getPrincipal();
+ log.debug("userSessionManage.login: " + username);
+ userSessionManagement.login(session, username);
+ }
+ }
+
+}
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
new file mode 100755
index 0000000000..4a7d8c19b5
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java
@@ -0,0 +1,320 @@
+package org.keycloak.adapters.as7;
+
+import org.jboss.logging.Logger;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.RealmConfiguration;
+import org.keycloak.VerificationException;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.SkeletonKeyToken;
+import org.jboss.resteasy.util.BasicAuthHelper;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class ServletOAuthLogin
+{
+ private static final Logger log = Logger.getLogger(ServletOAuthLogin.class);
+ protected HttpServletRequest request;
+ protected HttpServletResponse response;
+ protected boolean codePresent;
+ protected RealmConfiguration realmInfo;
+ protected int redirectPort;
+ protected String tokenString;
+ protected SkeletonKeyToken token;
+
+ public ServletOAuthLogin(RealmConfiguration realmInfo, HttpServletRequest request, HttpServletResponse response, int redirectPort)
+ {
+ this.request = request;
+ this.response = response;
+ this.realmInfo = realmInfo;
+ this.redirectPort = redirectPort;
+ }
+
+ public String getTokenString()
+ {
+ return tokenString;
+ }
+
+ public SkeletonKeyToken getToken()
+ {
+ return token;
+ }
+
+ public RealmConfiguration getRealmInfo()
+ {
+ return realmInfo;
+ }
+
+ protected String getDefaultCookiePath()
+ {
+ String path = request.getContextPath();
+ if ("".equals(path) || path == null) path = "/";
+ return path;
+ }
+
+ protected String getRequestUrl()
+ {
+ return request.getRequestURL().toString();
+ }
+
+ protected boolean isRequestSecure()
+ {
+ return request.isSecure();
+ }
+
+ protected void sendError(int code)
+ {
+ try
+ {
+ response.sendError(code);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected void sendRedirect(String url)
+ {
+ try
+ {
+ response.sendRedirect(url);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected Cookie getCookie(String cookieName)
+ {
+ if (request.getCookies() == null) return null;
+ for (Cookie cookie : request.getCookies())
+ {
+ if (cookie.getName().equals(cookieName))
+ {
+ return cookie;
+ }
+ }
+ return null;
+ }
+
+ protected String getCookieValue(String cookieName)
+ {
+ Cookie cookie = getCookie(cookieName);
+ if (cookie == null) return null;
+ return cookie.getValue();
+ }
+
+ protected String getQueryParamValue(String paramName)
+ {
+ String query = request.getQueryString();
+ if (query == null) return null;
+ String[] params = query.split("&");
+ for (String param : params)
+ {
+ int eq = param.indexOf('=');
+ if (eq == -1) continue;
+ String name = param.substring(0, eq);
+ if (!name.equals(paramName)) continue;
+ return param.substring(eq + 1);
+ }
+ return null;
+ }
+
+ public String getError()
+ {
+ return getQueryParamValue("error");
+ }
+
+ public String getCode()
+ {
+ return getQueryParamValue("code");
+ }
+
+ protected void setCookie(String name, String value, String domain, String path, boolean secure)
+ {
+ Cookie cookie = new Cookie(name, value);
+ if (domain != null) cookie.setDomain(domain);
+ if (path != null) cookie.setPath(path);
+ if (secure) cookie.setSecure(true);
+ response.addCookie(cookie);
+ }
+
+ protected String getRedirectUri(String state)
+ {
+ String url = getRequestUrl();
+ if (!isRequestSecure() && realmInfo.isSslRequired())
+ {
+ int port = redirectPort;
+ if (port < 0)
+ {
+ // disabled?
+ return null;
+ }
+ UriBuilder secureUrl = UriBuilder.fromUri(url).scheme("https").port(-1);
+ if (port != 443) secureUrl.port(port);
+ url = secureUrl.build().toString();
+ }
+ return realmInfo.getAuthUrl().clone()
+ .queryParam("client_id", realmInfo.getClientId())
+ .queryParam("redirect_uri", url)
+ .queryParam("state", state)
+ .queryParam("login", "true")
+ .build().toString();
+ }
+
+ protected static final AtomicLong counter = new AtomicLong();
+
+ protected String getStateCode()
+ {
+ return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
+ }
+
+ public void loginRedirect()
+ {
+ String state = getStateCode();
+ String redirect = getRedirectUri(state);
+ if (redirect == null)
+ {
+ sendError(Response.Status.FORBIDDEN.getStatusCode());
+ return;
+ }
+ setCookie(realmInfo.getStateCookieName(), state, null, getDefaultCookiePath(), realmInfo.isSslRequired());
+ sendRedirect(redirect);
+ }
+
+ public boolean checkStateCookie()
+ {
+ Cookie stateCookie = getCookie(realmInfo.getStateCookieName());
+
+ if (stateCookie == null)
+ {
+ sendError(400);
+ log.warn("No state cookie");
+ return false;
+ }
+ // reset the cookie
+ Cookie reset = new Cookie(stateCookie.getName(), stateCookie.getValue());
+ reset.setPath(stateCookie.getPath());
+ reset.setMaxAge(0);
+ response.addCookie(reset);
+
+ String stateCookieValue = getCookieValue(realmInfo.getStateCookieName());
+ // its ok to call request.getParameter() because this should be a redirect
+ String state = request.getParameter("state");
+ if (state == null)
+ {
+ sendError(400);
+ log.warn("state parameter was null");
+ return false;
+ }
+ if (!state.equals(stateCookieValue))
+ {
+ sendError(400);
+ log.warn("state parameter invalid");
+ log.warn("cookie: " + stateCookieValue);
+ log.warn("queryParam: " + state);
+ return false;
+ }
+ return true;
+
+ }
+
+ /**
+ * Start or continue the oauth login process.
+ *
+ * if code query parameter is not present, then browser is redirected to authUrl. The redirect URL will be
+ * the URL of the current request.
+ *
+ * If code query parameter is present, then an access token is obtained by invoking a secure request to the codeUrl.
+ * If the access token is obtained, the browser is again redirected to the current request URL, but any OAuth
+ * protocol specific query parameters are removed.
+ *
+ * @return true if an access token was obtained
+ */
+ public boolean resolveCode(String code)
+ {
+ // abort if not HTTPS
+ if (realmInfo.isSslRequired() && !isRequestSecure())
+ {
+ log.error("SSL is required");
+ sendError(Response.Status.FORBIDDEN.getStatusCode());
+ return false;
+ }
+
+ if (!checkStateCookie()) return false;
+
+ String client_id = realmInfo.getClientId();
+ String password = realmInfo.getCredentials().asMap().getFirst("password");
+ String authHeader = BasicAuthHelper.createHeader(client_id, password);
+ String redirectUri = stripOauthParametersFromRedirect();
+ Form form = new Form();
+ form.param("grant_type", "authorization_code")
+ .param("code", code)
+ .param("redirect_uri", redirectUri);
+
+ Response res = realmInfo.getCodeUrl().request().header(HttpHeaders.AUTHORIZATION, authHeader).post(Entity.form(form));
+ AccessTokenResponse tokenResponse;
+ try
+ {
+ if (res.getStatus() != 200)
+ {
+ log.error("failed to turn code into token");
+ sendError(Response.Status.FORBIDDEN.getStatusCode());
+ return false;
+ }
+ log.debug("media type: " + res.getMediaType());
+ log.debug("Content-Type header: " + res.getHeaderString("Content-Type"));
+ tokenResponse = res.readEntity(AccessTokenResponse.class);
+ }
+ finally
+ {
+ res.close();
+ }
+
+ tokenString = tokenResponse.getToken();
+ try
+ {
+ token = RSATokenVerifier.verifyToken(tokenString, realmInfo.getMetadata());
+ log.debug("Verification succeeded!");
+ }
+ catch (VerificationException e)
+ {
+ log.error("failed verification of token");
+ sendError(Response.Status.FORBIDDEN.getStatusCode());
+ return false;
+ }
+ // redirect to URL without oauth query parameters
+ sendRedirect(redirectUri);
+ return true;
+ }
+
+ /**
+ * strip out unwanted query parameters and redirect so bookmarks don't retain oauth protocol bits
+ */
+ protected String stripOauthParametersFromRedirect()
+ {
+ StringBuffer buf = request.getRequestURL().append("?").append(request.getQueryString());
+ UriBuilder builder = UriBuilder.fromUri(buf.toString())
+ .replaceQueryParam("code", null)
+ .replaceQueryParam("state", null);
+ return builder.build().toString();
+ }
+
+
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/UserSessionManagement.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/UserSessionManagement.java
new file mode 100755
index 0000000000..081df776c6
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/UserSessionManagement.java
@@ -0,0 +1,111 @@
+package org.keycloak.adapters.as7;
+
+import org.apache.catalina.Session;
+import org.apache.catalina.SessionEvent;
+import org.apache.catalina.SessionListener;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.jboss.logging.Logger;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Manages relationship to users and sessions so that forced admin logout can be implemented
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class UserSessionManagement implements SessionListener
+{
+ private static final Logger log = Logger.getLogger(UserSessionManagement.class);
+ protected ConcurrentHashMap> userSessionMap = new ConcurrentHashMap>();
+
+ protected void login(Session session, String username)
+ {
+ Map map = userSessionMap.get(username);
+ if (map == null)
+ {
+ final Map value = new HashMap();
+ map = userSessionMap.putIfAbsent(username, value);
+ if (map == null)
+ {
+ map = value;
+ }
+ }
+ synchronized (map)
+ {
+ map.put(session.getId(), session);
+ }
+ session.addSessionListener(this);
+ }
+
+ public void logoutAll()
+ {
+ List users = new ArrayList();
+ users.addAll(userSessionMap.keySet());
+ for (String user : users) logout(user);
+ }
+
+ public void logoutAllBut(String but)
+ {
+ List users = new ArrayList();
+ users.addAll(userSessionMap.keySet());
+ for (String user : users)
+ {
+ if (!but.equals(user)) logout(user);
+ }
+ }
+
+
+ public void logout(String user)
+ {
+ log.debug("logoutUser: " + user);
+ Map map = userSessionMap.remove(user);
+ if (map == null)
+ {
+ log.debug("no session for user: " + user);
+ return;
+ }
+ log.debug("found session for user");
+ synchronized (map)
+ {
+ for (Session session : map.values())
+ {
+ log.debug("invalidating session for user: " + user);
+ session.setPrincipal(null);
+ session.setAuthType(null);
+ session.getSession().invalidate();
+ }
+ }
+
+ }
+
+ public void sessionEvent(SessionEvent event)
+ {
+ // We only care about session destroyed events
+ if (!Session.SESSION_DESTROYED_EVENT.equals(event.getType())
+ && (!Session.SESSION_PASSIVATED_EVENT.equals(event.getType())))
+ return;
+
+ // Look up the single session id associated with this session (if any)
+ Session session = event.getSession();
+ GenericPrincipal principal = (GenericPrincipal) session.getPrincipal();
+ if (principal == null) return;
+ session.setPrincipal(null);
+ session.setAuthType(null);
+
+ String username = principal.getUserPrincipal().getName();
+ Map map = userSessionMap.get(username);
+ if (map == null) return;
+ synchronized (map)
+ {
+ map.remove(session.getId());
+ if (map.isEmpty()) userSessionMap.remove(username);
+ }
+
+
+ }
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/AuthServerConfig.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/AuthServerConfig.java
new file mode 100755
index 0000000000..73372e3c83
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/AuthServerConfig.java
@@ -0,0 +1,279 @@
+package org.keycloak.adapters.as7.config;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class AuthServerConfig
+{
+ @JsonProperty("realm")
+ protected String realm;
+
+ @JsonProperty("realm-private-key")
+ protected String realmPrivateKey;
+
+ @JsonProperty("realm-public-key")
+ protected String realmPublicKey;
+
+ @JsonProperty("realm-keystore")
+ protected String realmKeyStore;
+
+ @JsonProperty("realm-keystore-password")
+ protected String realmKeystorePassword;
+
+ @JsonProperty("realm-key-alias")
+ protected String realmKeyAlias;
+
+ @JsonProperty("realm-private-key-password")
+ protected String realmPrivateKeyPassword;
+
+ @JsonProperty("access-code-lifetime")
+ protected int accessCodeLifetime;
+
+ @JsonProperty("token-lifetime")
+ protected int tokenLifetime;
+
+ @JsonProperty("admin-role")
+ protected String adminRole;
+
+ @JsonProperty("login-role")
+ protected String loginRole;
+
+ @JsonProperty("oauth-client-role")
+ protected String clientRole;
+
+ @JsonProperty("wildcard-role")
+ protected String wildcardRole;
+
+ @JsonProperty("cancel-propagation")
+ protected boolean cancelPropagation;
+
+ @JsonProperty("sso-disabled")
+ protected boolean ssoDisabled;
+
+ // these properties are optional and used to provide connection metadata when the server wants to make
+ // remote SSL connections
+
+ protected String truststore;
+ @JsonProperty("truststore-password")
+ protected String truststorePassword;
+ @JsonProperty("client-keystore")
+ protected String clientKeystore;
+ @JsonProperty("client-keystore-password")
+ protected String clientKeystorePassword;
+ @JsonProperty("client-key-password")
+ protected String clientKeyPassword;
+
+ protected List resources = new ArrayList();
+
+
+ public String getRealm()
+ {
+ return realm;
+ }
+
+ public void setRealm(String realm)
+ {
+ this.realm = realm;
+ }
+
+ public String getRealmPrivateKey()
+ {
+ return realmPrivateKey;
+ }
+
+ public void setRealmPrivateKey(String realmPrivateKey)
+ {
+ this.realmPrivateKey = realmPrivateKey;
+ }
+
+ public String getRealmPublicKey()
+ {
+ return realmPublicKey;
+ }
+
+ public void setRealmPublicKey(String realmPublicKey)
+ {
+ this.realmPublicKey = realmPublicKey;
+ }
+
+ public int getAccessCodeLifetime()
+ {
+ return accessCodeLifetime;
+ }
+
+ public void setAccessCodeLifetime(int accessCodeLifetime)
+ {
+ this.accessCodeLifetime = accessCodeLifetime;
+ }
+
+ public String getTruststore()
+ {
+ return truststore;
+ }
+
+ public void setTruststore(String truststore)
+ {
+ this.truststore = truststore;
+ }
+
+ public String getTruststorePassword()
+ {
+ return truststorePassword;
+ }
+
+ public void setTruststorePassword(String truststorePassword)
+ {
+ this.truststorePassword = truststorePassword;
+ }
+
+ public String getClientKeystore()
+ {
+ return clientKeystore;
+ }
+
+ public void setClientKeystore(String clientKeystore)
+ {
+ this.clientKeystore = clientKeystore;
+ }
+
+ public String getClientKeystorePassword()
+ {
+ return clientKeystorePassword;
+ }
+
+ public void setClientKeystorePassword(String clientKeystorePassword)
+ {
+ this.clientKeystorePassword = clientKeystorePassword;
+ }
+
+ public String getClientKeyPassword()
+ {
+ return clientKeyPassword;
+ }
+
+ public void setClientKeyPassword(String clientKeyPassword)
+ {
+ this.clientKeyPassword = clientKeyPassword;
+ }
+
+ public boolean isCancelPropagation()
+ {
+ return cancelPropagation;
+ }
+
+ public void setCancelPropagation(boolean cancelPropagation)
+ {
+ this.cancelPropagation = cancelPropagation;
+ }
+
+ public boolean isSsoDisabled()
+ {
+ return ssoDisabled;
+ }
+
+ public void setSsoDisabled(boolean ssoDisabled)
+ {
+ this.ssoDisabled = ssoDisabled;
+ }
+
+ public List getResources()
+ {
+ return resources;
+ }
+
+ public String getAdminRole()
+ {
+ return adminRole;
+ }
+
+ public void setAdminRole(String adminRole)
+ {
+ this.adminRole = adminRole;
+ }
+
+ public String getLoginRole()
+ {
+ return loginRole;
+ }
+
+ public void setLoginRole(String loginRole)
+ {
+ this.loginRole = loginRole;
+ }
+
+ public String getClientRole()
+ {
+ return clientRole;
+ }
+
+ public void setClientRole(String clientRole)
+ {
+ this.clientRole = clientRole;
+ }
+
+ public String getWildcardRole()
+ {
+ return wildcardRole;
+ }
+
+ public void setWildcardRole(String wildcardRole)
+ {
+ this.wildcardRole = wildcardRole;
+ }
+
+ public String getRealmKeyStore()
+ {
+ return realmKeyStore;
+ }
+
+ public void setRealmKeyStore(String realmKeyStore)
+ {
+ this.realmKeyStore = realmKeyStore;
+ }
+
+ public String getRealmKeystorePassword()
+ {
+ return realmKeystorePassword;
+ }
+
+ public void setRealmKeystorePassword(String realmKeystorePassword)
+ {
+ this.realmKeystorePassword = realmKeystorePassword;
+ }
+
+ public String getRealmKeyAlias()
+ {
+ return realmKeyAlias;
+ }
+
+ public void setRealmKeyAlias(String realmKeyAlias)
+ {
+ this.realmKeyAlias = realmKeyAlias;
+ }
+
+ public String getRealmPrivateKeyPassword()
+ {
+ return realmPrivateKeyPassword;
+ }
+
+ public void setRealmPrivateKeyPassword(String realmPrivateKeyPassword)
+ {
+ this.realmPrivateKeyPassword = realmPrivateKeyPassword;
+ }
+
+ public int getTokenLifetime()
+ {
+ return tokenLifetime;
+ }
+
+ public void setTokenLifetime(int tokenLifetime)
+ {
+ this.tokenLifetime = tokenLifetime;
+ }
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfig.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfig.java
new file mode 100755
index 0000000000..e1bef4bbec
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfig.java
@@ -0,0 +1,213 @@
+package org.keycloak.adapters.as7.config;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.annotate.JsonPropertyOrder;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+@JsonPropertyOrder({"realm", "resource", "realm-public-key", "admin-role", "auth-url", "code-url", "truststore", "truststore-password", "client-id", "client-credentials"})
+public class ManagedResourceConfig
+{
+ @JsonProperty("realm")
+ protected String realm;
+ @JsonProperty("resource")
+ protected String resource;
+
+ @JsonProperty("realm-public-key")
+ protected String realmKey;
+
+ @JsonProperty("admin-role")
+ protected String adminRole;
+
+ @JsonProperty("auth-url")
+ protected String authUrl;
+ @JsonProperty("code-url")
+ protected String codeUrl;
+
+ @JsonProperty("allow-any-hostname")
+ protected boolean allowAnyHostname;
+
+ @JsonProperty("truststore")
+ protected String truststore;
+
+ @JsonProperty("truststore-password")
+ protected String truststorePassword;
+ @JsonProperty("client-id")
+ protected String clientId;
+ @JsonProperty("client-keystore")
+ protected String clientKeystore;
+ @JsonProperty("client-keystore-password")
+ protected String clientKeystorePassword;
+ @JsonProperty("client-key-password")
+ protected String clientKeyPassword;
+
+ @JsonProperty("client-credentials")
+ protected Map clientCredentials = new HashMap();
+
+ @JsonProperty("connection-pool-size")
+ protected int connectionPoolSize;
+
+ @JsonProperty("cancel-propagation")
+ protected boolean cancelPropagation;
+
+
+ public String getRealm()
+ {
+ return realm;
+ }
+
+ public void setRealm(String realm)
+ {
+ this.realm = realm;
+ }
+
+ public String getResource()
+ {
+ return resource;
+ }
+
+ public void setResource(String resource)
+ {
+ this.resource = resource;
+ }
+
+ public String getRealmKey()
+ {
+ return realmKey;
+ }
+
+ public void setRealmKey(String realmKey)
+ {
+ this.realmKey = realmKey;
+ }
+
+ public String getAuthUrl()
+ {
+ return authUrl;
+ }
+
+ public void setAuthUrl(String authUrl)
+ {
+ this.authUrl = authUrl;
+ }
+
+ public String getCodeUrl()
+ {
+ return codeUrl;
+ }
+
+ public void setCodeUrl(String codeUrl)
+ {
+ this.codeUrl = codeUrl;
+ }
+
+ public boolean isAllowAnyHostname()
+ {
+ return allowAnyHostname;
+ }
+
+ public void setAllowAnyHostname(boolean allowAnyHostname)
+ {
+ this.allowAnyHostname = allowAnyHostname;
+ }
+
+ public String getTruststore()
+ {
+ return truststore;
+ }
+
+ public void setTruststore(String truststore)
+ {
+ this.truststore = truststore;
+ }
+
+ public String getTruststorePassword()
+ {
+ return truststorePassword;
+ }
+
+ public void setTruststorePassword(String truststorePassword)
+ {
+ this.truststorePassword = truststorePassword;
+ }
+
+ public String getClientId()
+ {
+ return clientId;
+ }
+
+ public void setClientId(String clientId)
+ {
+ this.clientId = clientId;
+ }
+
+ public Map getClientCredentials()
+ {
+ return clientCredentials;
+ }
+
+ public String getClientKeystore()
+ {
+ return clientKeystore;
+ }
+
+ public void setClientKeystore(String clientKeystore)
+ {
+ this.clientKeystore = clientKeystore;
+ }
+
+ public String getClientKeystorePassword()
+ {
+ return clientKeystorePassword;
+ }
+
+ public void setClientKeystorePassword(String clientKeystorePassword)
+ {
+ this.clientKeystorePassword = clientKeystorePassword;
+ }
+
+ public String getClientKeyPassword()
+ {
+ return clientKeyPassword;
+ }
+
+ public void setClientKeyPassword(String clientKeyPassword)
+ {
+ this.clientKeyPassword = clientKeyPassword;
+ }
+
+ public int getConnectionPoolSize()
+ {
+ return connectionPoolSize;
+ }
+
+ public void setConnectionPoolSize(int connectionPoolSize)
+ {
+ this.connectionPoolSize = connectionPoolSize;
+ }
+
+ public boolean isCancelPropagation()
+ {
+ return cancelPropagation;
+ }
+
+ public void setCancelPropagation(boolean cancelPropagation)
+ {
+ this.cancelPropagation = cancelPropagation;
+ }
+
+ public String getAdminRole()
+ {
+ return adminRole;
+ }
+
+ public void setAdminRole(String adminRole)
+ {
+ this.adminRole = adminRole;
+ }
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfigLoader.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfigLoader.java
new file mode 100755
index 0000000000..75339d1054
--- /dev/null
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/ManagedResourceConfigLoader.java
@@ -0,0 +1,139 @@
+package org.keycloak.adapters.as7.config;
+
+import org.apache.catalina.Context;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.jboss.logging.Logger;
+import org.keycloak.EnvUtil;
+import org.keycloak.PemUtils;
+import org.keycloak.ResourceMetadata;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.PublicKey;
+
+public class ManagedResourceConfigLoader
+{
+ static final Logger log = Logger.getLogger(ManagedResourceConfigLoader.class);
+ protected ManagedResourceConfig remoteSkeletonKeyConfig;
+ protected ResourceMetadata resourceMetadata;
+
+ public ManagedResourceConfigLoader(Context context)
+ {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_DEFAULT);
+ InputStream is = null;
+ String path = context.getServletContext().getInitParameter("skeleton.key.config.file");
+ if (path == null)
+ {
+ is = context.getServletContext().getResourceAsStream("/WEB-INF/resteasy-oauth.json");
+ }
+ else
+ {
+ try
+ {
+ is = new FileInputStream(path);
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ remoteSkeletonKeyConfig = null;
+ try
+ {
+ remoteSkeletonKeyConfig = mapper.readValue(is, ManagedResourceConfig.class);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ String name = remoteSkeletonKeyConfig.getResource();
+ String realm = remoteSkeletonKeyConfig.getRealm();
+ if (realm == null) throw new RuntimeException("Must set 'realm' in config");
+
+ String realmKeyPem = remoteSkeletonKeyConfig.getRealmKey();
+ if (realmKeyPem == null)
+ {
+ throw new IllegalArgumentException("You must set the realm-public-key");
+ }
+
+ PublicKey realmKey = null;
+ try
+ {
+ realmKey = PemUtils.decodePublicKey(realmKeyPem);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ resourceMetadata = new ResourceMetadata();
+ resourceMetadata.setRealm(realm);
+ resourceMetadata.setResourceName(name);
+ resourceMetadata.setRealmKey(realmKey);
+
+
+ String truststore = remoteSkeletonKeyConfig.getTruststore();
+ if (truststore != null)
+ {
+ truststore = EnvUtil.replace(truststore);
+ String truststorePassword = remoteSkeletonKeyConfig.getTruststorePassword();
+ KeyStore trust = null;
+ try
+ {
+ trust = loadKeyStore(truststore, truststorePassword);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Failed to load truststore", e);
+ }
+ resourceMetadata.setTruststore(trust);
+ }
+ String clientKeystore = remoteSkeletonKeyConfig.getClientKeystore();
+ String clientKeyPassword = null;
+ if (clientKeystore != null)
+ {
+ clientKeystore = EnvUtil.replace(clientKeystore);
+ String clientKeystorePassword = remoteSkeletonKeyConfig.getClientKeystorePassword();
+ KeyStore serverKS = null;
+ try
+ {
+ serverKS = loadKeyStore(clientKeystore, clientKeystorePassword);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Failed to load keystore", e);
+ }
+ resourceMetadata.setClientKeystore(serverKS);
+ clientKeyPassword = remoteSkeletonKeyConfig.getClientKeyPassword();
+ resourceMetadata.setClientKeyPassword(clientKeyPassword);
+ }
+
+ }
+ public static KeyStore loadKeyStore(String filename, String password) throws Exception
+ {
+ KeyStore trustStore = KeyStore.getInstance(KeyStore
+ .getDefaultType());
+ File truststoreFile = new File(filename);
+ FileInputStream trustStream = new FileInputStream(truststoreFile);
+ trustStore.load(trustStream, password.toCharArray());
+ trustStream.close();
+ return trustStore;
+ }
+
+ public ManagedResourceConfig getRemoteSkeletonKeyConfig()
+ {
+ return remoteSkeletonKeyConfig;
+ }
+
+ public ResourceMetadata getResourceMetadata()
+ {
+ return resourceMetadata;
+ }
+
+}
\ No newline at end of file
diff --git a/integration/pom.xml b/integration/pom.xml
new file mode 100755
index 0000000000..5028c1d59e
--- /dev/null
+++ b/integration/pom.xml
@@ -0,0 +1,21 @@
+
+
+ keycloak-parent
+ org.keycloak
+ 1.0-alpha-1
+ ../pom.xml
+
+ Keycloak Integration
+
+ 4.0.0
+
+ org.keycloak
+ integration-pom
+ pom
+
+
+ as7-eap6/adapter
+
+
+
diff --git a/pom.xml b/pom.xml
index 53b9656504..bcb889c1f5 100755
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,7 @@
core
services
+ integration
diff --git a/services/src/main/java/org/keycloak/services/managers/InstallationManager.java b/services/src/main/java/org/keycloak/services/managers/InstallationManager.java
index 4550ce6ec7..61545475a8 100755
--- a/services/src/main/java/org/keycloak/services/managers/InstallationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/InstallationManager.java
@@ -20,10 +20,14 @@ public class InstallationManager {
defaultRealm.setAccessCodeLifespan(60);
defaultRealm.setSslNotRequired(false);
defaultRealm.setCookieLoginAllowed(true);
+ defaultRealm.setRegistrationAllowed(true);
manager.generateRealmKeys(defaultRealm);
defaultRealm.updateRealm();
defaultRealm.addRequiredCredential(RequiredCredentialModel.PASSWORD);
defaultRealm.getIdm().add(new SimpleRole(RegistrationService.REALM_CREATOR_ROLE));
+ }
+ public boolean isInstalled(RealmManager manager) {
+ return manager.defaultRealm() != null;
}
}
diff --git a/services/src/main/java/org/keycloak/services/models/RealmModel.java b/services/src/main/java/org/keycloak/services/models/RealmModel.java
index 235bba19cc..0cdf45ca01 100755
--- a/services/src/main/java/org/keycloak/services/models/RealmModel.java
+++ b/services/src/main/java/org/keycloak/services/models/RealmModel.java
@@ -17,7 +17,6 @@ import org.picketlink.idm.model.Attribute;
import org.picketlink.idm.model.Grant;
import org.picketlink.idm.model.Realm;
import org.picketlink.idm.model.Role;
-import org.picketlink.idm.model.SimpleAgent;
import org.picketlink.idm.model.Tier;
import org.picketlink.idm.model.User;
import org.picketlink.idm.query.IdentityQuery;
@@ -48,21 +47,22 @@ public class RealmModel {
public static final String REALM_PUBLIC_KEY = "publicKey";
public static final String REALM_IS_SSL_NOT_REQUIRED = "isSSLNotRequired";
public static final String REALM_IS_COOKIE_LOGIN_ALLOWED = "isCookieLoginAllowed";
+ public static final String REALM_IS_REGISTRATION_ALLOWED = "isRegistrationAllowed";
protected Realm realm;
protected Agent realmAgent;
- protected IdentitySession IdentitySession;
+ protected IdentitySession identitySession;
protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey;
- public RealmModel(Realm realm, IdentitySession factory) {
+ public RealmModel(Realm realm, IdentitySession session) {
this.realm = realm;
- this.IdentitySession = factory;
+ this.identitySession = session;
realmAgent = getIdm().getAgent(REALM_AGENT_ID);
}
public IdentityManager getIdm() {
- return IdentitySession.createIdentityManager(realm);
+ return identitySession.createIdentityManager(realm);
}
public void updateRealm() {
@@ -105,6 +105,14 @@ public class RealmModel {
realmAgent.setAttribute(new Attribute(REALM_IS_COOKIE_LOGIN_ALLOWED, cookieLoginAllowed));
}
+ public boolean isRegistrationAllowed() {
+ return (Boolean) realmAgent.getAttribute(REALM_IS_REGISTRATION_ALLOWED).getValue();
+ }
+
+ public void setRegistrationAllowed(boolean registrationAllowed) {
+ realmAgent.setAttribute(new Attribute(REALM_IS_REGISTRATION_ALLOWED, registrationAllowed));
+ }
+
public long getTokenLifespan() {
return (Long) realmAgent.getAttribute(REALM_TOKEN_LIFESPAN).getValue();
}
@@ -269,8 +277,8 @@ public class RealmModel {
List results = query.getResultList();
List resources = new ArrayList();
for (ResourceRelationship relationship : results) {
- Tier resourceTier = IdentitySession.findTier(relationship.getResourceId());
- ResourceModel model = new ResourceModel(resourceTier,relationship, this, IdentitySession);
+ Tier resourceTier = identitySession.findTier(relationship.getResourceId());
+ ResourceModel model = new ResourceModel(resourceTier,relationship, this, identitySession);
resources.add(model);
}
@@ -278,14 +286,14 @@ public class RealmModel {
}
public ResourceModel addResource(String name) {
- Tier newTier = IdentitySession.createTier(RealmManager.generateId());
+ Tier newTier = identitySession.createTier(RealmManager.generateId());
IdentityManager idm = getIdm();
ResourceRelationship relationship = new ResourceRelationship();
relationship.setResourceName(name);
relationship.setRealmAgent(realmAgent);
relationship.setResourceId(newTier.getId());
idm.add(relationship);
- return new ResourceModel(newTier, relationship, this, IdentitySession);
+ return new ResourceModel(newTier, relationship, this, identitySession);
}
public Set getRoleMappings(User user) {
@@ -322,7 +330,7 @@ public class RealmModel {
}
public boolean isRealmAdmin(Agent agent) {
- IdentityManager idm = new RealmManager(IdentitySession).defaultRealm().getIdm();
+ IdentityManager idm = new RealmManager(identitySession).defaultRealm().getIdm();
RelationshipQuery query = idm.createRelationshipQuery(RealmAdminRelationship.class);
query.setParameter(RealmAdminRelationship.REALM, realm.getId());
query.setParameter(RealmAdminRelationship.ADMIN, agent);
@@ -331,7 +339,7 @@ public class RealmModel {
}
public void addRealmAdmin(Agent agent) {
- IdentityManager idm = new RealmManager(IdentitySession).defaultRealm().getIdm();
+ IdentityManager idm = new RealmManager(identitySession).defaultRealm().getIdm();
RealmAdminRelationship relationship = new RealmAdminRelationship();
relationship.setAdmin(agent);
relationship.setRealm(realm.getId());
diff --git a/services/src/main/java/org/keycloak/services/resources/RegistrationService.java b/services/src/main/java/org/keycloak/services/resources/RegistrationService.java
index 3718f76aa5..55c576c3f8 100755
--- a/services/src/main/java/org/keycloak/services/resources/RegistrationService.java
+++ b/services/src/main/java/org/keycloak/services/resources/RegistrationService.java
@@ -11,6 +11,7 @@ import org.picketlink.idm.model.SimpleUser;
import org.picketlink.idm.model.User;
import javax.ws.rs.Consumes;
+import javax.ws.rs.ForbiddenException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
@@ -32,15 +33,21 @@ public class RegistrationService {
protected UriInfo uriInfo;
@Context
- protected IdentitySession IdentitySession;
+ protected IdentitySession identitySession;
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response register(UserRepresentation newUser) {
- IdentitySession.getTransaction().begin();
+ identitySession.getTransaction().begin();
try {
- RealmManager realmManager = new RealmManager(IdentitySession);
+ RealmManager realmManager = new RealmManager(identitySession);
RealmModel defaultRealm = realmManager.defaultRealm();
+ if (!defaultRealm.isEnabled()) {
+ throw new ForbiddenException();
+ }
+ if (!defaultRealm.isRegistrationAllowed()) {
+ throw new ForbiddenException();
+ }
User user = defaultRealm.getIdm().getUser(newUser.getUsername());
if (user != null) {
return Response.status(400).type("text/plain").entity("user exists").build();
@@ -56,12 +63,12 @@ public class RegistrationService {
}
Role realmCreator = defaultRealm.getIdm().getRole(REALM_CREATOR_ROLE);
defaultRealm.getIdm().grantRole(user, realmCreator);
- IdentitySession.getTransaction().commit();
+ identitySession.getTransaction().commit();
URI uri = uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(user.getLoginName()).build();
return Response.created(uri).build();
} catch (RuntimeException e) {
logger.error("Failed to register", e);
- IdentitySession.getTransaction().rollback();
+ identitySession.getTransaction().rollback();
throw e;
}
}