diff --git a/core/src/main/java/org/keycloak/SkeletonKeyContextResolver.java b/core/src/main/java/org/keycloak/SkeletonKeyContextResolver.java index 3bca480c5d..9b83907d35 100755 --- a/core/src/main/java/org/keycloak/SkeletonKeyContextResolver.java +++ b/core/src/main/java/org/keycloak/SkeletonKeyContextResolver.java @@ -36,7 +36,7 @@ public class SkeletonKeyContextResolver implements ContextResolver @Override public ObjectMapper getContext(Class type) { - if (type.getPackage().getName().startsWith("org.jboss.resteasy.skeleton.key")) return mapper; + if (type.getPackage().getName().startsWith(getClass().getPackage().getName())) return mapper; return null; } } 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 + 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 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 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 2225e9be9c..bcb889c1f5 100755 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,7 @@ core services + integration @@ -108,6 +109,12 @@ jose-jwt ${resteasy.version} + + org.jboss.resteasy + tjws + ${resteasy.version} + provided + org.codehaus.jackson jackson-core-asl @@ -163,6 +170,11 @@ junit 4.11 + + org.hibernate.javax.persistence + hibernate-jpa-2.0-api + 1.0.1.Final + diff --git a/services/pom.xml b/services/pom.xml index 8c097aeb36..5daecdd8c9 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -43,6 +43,25 @@ org.jboss.resteasy resteasy-jaxrs provided + + + log4j + log4j + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-simple + + + + + org.jboss.resteasy + tjws + test org.jboss.resteasy @@ -94,6 +113,23 @@ junit test + + org.hibernate.javax.persistence + hibernate-jpa-2.0-api + provided + + + com.h2database + h2 + 1.3.161 + test + + + org.hibernate + hibernate-entitymanager + 3.6.6.Final + test + diff --git a/services/src/main/java/org/keycloak/services/filters/IdentitySessionFilter.java b/services/src/main/java/org/keycloak/services/filters/IdentitySessionFilter.java new file mode 100755 index 0000000000..dbe8461af8 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/filters/IdentitySessionFilter.java @@ -0,0 +1,38 @@ +package org.keycloak.services.filters; + +import org.jboss.resteasy.spi.ResteasyProviderFactory; +import org.picketlink.idm.IdentitySession; +import org.picketlink.idm.IdentitySessionFactory; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.container.PreMatching; +import java.io.IOException; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@PreMatching +public class IdentitySessionFilter implements ContainerRequestFilter, ContainerResponseFilter { + protected IdentitySessionFactory factory; + + public IdentitySessionFilter(IdentitySessionFactory factory) { + this.factory = factory; + } + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + IdentitySession ctx = factory.createIdentitySession(); + requestContext.setProperty(IdentitySession.class.getName(), ctx); + ResteasyProviderFactory.pushContext(IdentitySession.class, ctx); + } + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { + IdentitySession ctx = (IdentitySession)requestContext.getProperty(IdentitySession.class.getName()); + if (ctx != null) ctx.close(); + } +} diff --git a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java new file mode 100755 index 0000000000..d96b32ba95 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java @@ -0,0 +1,49 @@ +package org.keycloak.services.managers; + +import org.keycloak.representations.SkeletonKeyToken; +import org.picketlink.idm.model.User; + +import java.util.UUID; + +/** +* @author Bill Burke +* @version $Revision: 1 $ +*/ +public class AccessCodeEntry { + protected String id = UUID.randomUUID().toString() + System.currentTimeMillis(); + protected long expiration; + protected SkeletonKeyToken token; + protected User client; + + 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 User getClient() { + return client; + } + + public void setClient(User client) { + this.client = client; + } +} diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index 74fc03fd74..7b725da747 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -24,107 +24,91 @@ import java.util.Set; * @author Bill Burke * @version $Revision: 1 $ */ -public class AuthenticationManager -{ - protected Logger logger = Logger.getLogger(AuthenticationManager.class); - public static final String FORM_USERNAME = "username"; - protected RealmManager adapter; +public class AuthenticationManager { + protected Logger logger = Logger.getLogger(AuthenticationManager.class); + public static final String FORM_USERNAME = "username"; - public AuthenticationManager(RealmManager adapter) - { - this.adapter = adapter; - } + /** + * Grabs token from headers, authenticates, authorizes + * + * @param realm + * @param headers + * @return + */ + public boolean isRealmAdmin(RealmModel realm, HttpHeaders headers) { + User user = authenticateToken(realm, headers); + return realm.isRealmAdmin(user); + } - public User authenticateToken(RealmModel realm, HttpHeaders headers) - { - String tokenString = null; - String authHeader = headers.getHeaderString(HttpHeaders.AUTHORIZATION); - if (authHeader == null) - { - throw new NotAuthorizedException("Bearer"); - } - else - { - String[] split = authHeader.trim().split("\\s+"); - if (split == null || split.length != 2) throw new NotAuthorizedException("Bearer"); - if (!split[0].equalsIgnoreCase("Bearer")) throw new NotAuthorizedException("Bearer"); - tokenString = split[1]; - } + public User authenticateToken(RealmModel realm, HttpHeaders headers) { + String tokenString = null; + String authHeader = headers.getHeaderString(HttpHeaders.AUTHORIZATION); + if (authHeader == null) { + throw new NotAuthorizedException("Bearer"); + } else { + String[] split = authHeader.trim().split("\\s+"); + if (split == null || split.length != 2) throw new NotAuthorizedException("Bearer"); + if (!split[0].equalsIgnoreCase("Bearer")) throw new NotAuthorizedException("Bearer"); + tokenString = split[1]; + } - try - { - SkeletonKeyToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getId()); - if (!token.isActive()) - { - throw new NotAuthorizedException("token_expired"); - } - User user = realm.getIdm().getUser(token.getPrincipal()); - if (user == null || !user.isEnabled()) - { - throw new NotAuthorizedException("invalid_user"); - } - return user; - } - catch (VerificationException e) - { - logger.error("Failed to verify token", e); - throw new NotAuthorizedException("invalid_token"); - } - } + try { + SkeletonKeyToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getId()); + if (!token.isActive()) { + throw new NotAuthorizedException("token_expired"); + } + User user = realm.getIdm().getUser(token.getPrincipal()); + if (user == null || !user.isEnabled()) { + throw new NotAuthorizedException("invalid_user"); + } + return user; + } catch (VerificationException e) { + logger.error("Failed to verify token", e); + throw new NotAuthorizedException("invalid_token"); + } + } - public boolean authenticateForm(RealmModel realm, User user, MultivaluedMap formData) - { - String username = user.getLoginName(); - Set types = new HashSet(); + public boolean authenticateForm(RealmModel realm, User user, MultivaluedMap formData) { + String username = user.getLoginName(); + Set types = new HashSet(); - for (RequiredCredentialModel credential : realm.getRequiredCredentials()) - { - types.add(credential.getType()); - } + for (RequiredCredentialModel credential : realm.getRequiredCredentials()) { + types.add(credential.getType()); + } - if (types.contains(RequiredCredentialRepresentation.PASSWORD)) - { - String password = formData.getFirst(RequiredCredentialRepresentation.PASSWORD); - if (password == null) - { - logger.warn("Password not provided"); + if (types.contains(RequiredCredentialRepresentation.PASSWORD)) { + String password = formData.getFirst(RequiredCredentialRepresentation.PASSWORD); + if (password == null) { + logger.warn("Password not provided"); + return false; + } + + if (types.contains(RequiredCredentialRepresentation.TOTP)) { + String token = formData.getFirst(RequiredCredentialRepresentation.TOTP); + if (token == null) { + logger.warn("TOTP token not provided"); + return false; + } + TOTPCredentials creds = new TOTPCredentials(); + creds.setToken(token); + creds.setUsername(username); + creds.setPassword(new Password(password)); + realm.getIdm().validateCredentials(creds); + if (creds.getStatus() != Credentials.Status.VALID) { + return false; + } + } else { + UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, new Password(password)); + realm.getIdm().validateCredentials(creds); + if (creds.getStatus() != Credentials.Status.VALID) { + return false; + } + } + } else { + logger.warn("Do not know how to authenticate user"); return false; - } - - if (types.contains(RequiredCredentialRepresentation.TOTP)) - { - String token = formData.getFirst(RequiredCredentialRepresentation.TOTP); - if (token == null) - { - logger.warn("TOTP token not provided"); - return false; - } - TOTPCredentials creds = new TOTPCredentials(); - creds.setToken(token); - creds.setUsername(username); - creds.setPassword(new Password(password)); - realm.getIdm().validateCredentials(creds); - if (creds.getStatus() != Credentials.Status.VALID) - { - return false; - } - } - else - { - UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, new Password(password)); - realm.getIdm().validateCredentials(creds); - if (creds.getStatus() != Credentials.Status.VALID) - { - return false; - } - } - } - else - { - logger.warn("Do not know how to authenticate user"); - return false; - } - return true; - } + } + return true; + } } diff --git a/services/src/main/java/org/keycloak/services/managers/InstallationManager.java b/services/src/main/java/org/keycloak/services/managers/InstallationManager.java new file mode 100755 index 0000000000..61545475a8 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/managers/InstallationManager.java @@ -0,0 +1,33 @@ +package org.keycloak.services.managers; + +import org.keycloak.services.models.RealmManager; +import org.keycloak.services.models.RealmModel; +import org.keycloak.services.models.RequiredCredentialModel; +import org.keycloak.services.resources.RegistrationService; +import org.picketlink.idm.model.Realm; +import org.picketlink.idm.model.SimpleRole; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class InstallationManager { + public void install(RealmManager manager) { + RealmModel defaultRealm = manager.createRealm(Realm.DEFAULT_REALM, Realm.DEFAULT_REALM); + defaultRealm.setName(Realm.DEFAULT_REALM); + defaultRealm.setEnabled(true); + defaultRealm.setTokenLifespan(300); + 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/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java index 4d1428c6e3..6c015048f5 100755 --- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java +++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java @@ -21,156 +21,123 @@ import java.util.Set; * @author Bill Burke * @version $Revision: 1 $ */ -public class TokenManager -{ - protected RealmManager adapter; +public class TokenManager { - public TokenManager(RealmManager adapter) - { - this.adapter = adapter; - } + public SkeletonKeyToken createScopedToken(SkeletonKeyScope scope, RealmModel realm, User client, User user) { + SkeletonKeyToken token = new SkeletonKeyToken(); + token.id(RealmManager.generateId()); + token.principal(user.getLoginName()); + token.audience(realm.getName()); + token.issuedNow(); + token.issuedFor(client.getLoginName()); + if (realm.getTokenLifespan() > 0) { + token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); + } + Map resourceMap = realm.getResourceMap(); - public SkeletonKeyToken createScopedToken(SkeletonKeyScope scope, RealmModel realm, User client, User user) - { - SkeletonKeyToken token = new SkeletonKeyToken(); - token.id(adapter.generateId()); - token.principal(user.getLoginName()); - token.audience(realm.getName()); - token.issuedNow(); - token.issuedFor(client.getLoginName()); - if (realm.getTokenLifespan() > 0) - { - token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); - } - Map resourceMap = realm.getResourceMap(); + for (String res : scope.keySet()) { + ResourceModel resource = resourceMap.get(res); + Set scopeMapping = resource.getScope(client); + Set roleMapping = resource.getRoleMappings(user); + SkeletonKeyToken.Access access = token.addAccess(resource.getName()); + for (String role : scope.get(res)) { + if (!scopeMapping.contains("*") && !scopeMapping.contains(role)) { + throw new ForbiddenException(Response.status(403).entity("

Security Alert

Known client not authorized for the requested scope.

").type("text/html").build()); + } + if (!roleMapping.contains(role)) { + throw new ForbiddenException(Response.status(403).entity("

Security Alert

Known client not authorized for the requested scope.

").type("text/html").build()); - for (String res : scope.keySet()) - { - ResourceModel resource = resourceMap.get(res); - Set scopeMapping = resource.getScope(client); - Set roleMapping = resource.getRoleMappings(user); - SkeletonKeyToken.Access access = token.addAccess(resource.getName()); - for (String role : scope.get(res)) - { - if (!scopeMapping.contains("*") && !scopeMapping.contains(role)) - { - throw new ForbiddenException(Response.status(403).entity("

Security Alert

Known client not authorized for the requested scope.

").type("text/html").build()); + } + access.addRole(role); } - if (!roleMapping.contains(role)) - { - throw new ForbiddenException(Response.status(403).entity("

Security Alert

Known client not authorized for the requested scope.

").type("text/html").build()); + } + return token; + } + public SkeletonKeyToken createScopedToken(String scopeParam, RealmModel realm, User client, User user) { + SkeletonKeyScope scope = decodeScope(scopeParam); + return createScopedToken(scope, realm, client, user); + } + + public SkeletonKeyToken createLoginToken(RealmModel realm, User client, User user) { + Set mapping = realm.getScope(client); + if (!mapping.contains("*")) { + throw new ForbiddenException(Response.status(403).entity("

Security Alert

Known client not authorized to request a user login.

").type("text/html").build()); + } + SkeletonKeyToken token = createAccessToken(realm, user); + token.issuedFor(client.getLoginName()); + return token; + + } + + public SkeletonKeyScope decodeScope(String scopeParam) { + SkeletonKeyScope scope = null; + byte[] bytes = Base64Url.decode(scopeParam); + try { + scope = JsonSerialization.fromBytes(SkeletonKeyScope.class, bytes); + } catch (IOException e) { + throw new RuntimeException(e); + } + return scope; + } + + + public SkeletonKeyToken createAccessToken(RealmModel realm, User user) { + List resources = realm.getResources(); + SkeletonKeyToken token = new SkeletonKeyToken(); + token.id(RealmManager.generateId()); + token.issuedNow(); + token.principal(user.getLoginName()); + token.audience(realm.getId()); + if (realm.getTokenLifespan() > 0) { + token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); + } + + Set realmMapping = realm.getRoleMappings(user); + + if (realmMapping != null && realmMapping.size() > 0) { + SkeletonKeyToken.Access access = new SkeletonKeyToken.Access(); + for (String role : realmMapping) { + access.addRole(role); } - access.addRole(role); - } - } - return token; - } - - public SkeletonKeyToken createScopedToken(String scopeParam, RealmModel realm, User client, User user) - { - SkeletonKeyScope scope = decodeScope(scopeParam); - return createScopedToken(scope, realm, client, user); - } - - public SkeletonKeyToken createLoginToken(RealmModel realm, User client, User user) - { - Set mapping = realm.getScope(client); - if (!mapping.contains("*")) - { - throw new ForbiddenException(Response.status(403).entity("

Security Alert

Known client not authorized to request a user login.

").type("text/html").build()); - } - SkeletonKeyToken token = createAccessToken(realm, user); - token.issuedFor(client.getLoginName()); - return token; - - } - - public SkeletonKeyScope decodeScope(String scopeParam) - { - SkeletonKeyScope scope = null; - byte[] bytes = Base64Url.decode(scopeParam); - try - { - scope = JsonSerialization.fromBytes(SkeletonKeyScope.class, bytes); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - return scope; - } - - - public SkeletonKeyToken createAccessToken(RealmModel realm, User user) - { - List resources = realm.getResources(); - SkeletonKeyToken token = new SkeletonKeyToken(); - token.id(adapter.generateId()); - token.issuedNow(); - token.principal(user.getLoginName()); - token.audience(realm.getId()); - if (realm.getTokenLifespan() > 0) - { - token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); - } - - Set realmMapping = realm.getRoleMappings(user); - - if (realmMapping != null && realmMapping.size() > 0) - { - SkeletonKeyToken.Access access = new SkeletonKeyToken.Access(); - for (String role : realmMapping) - { - access.addRole(role); - } - token.setRealmAccess(access); - } - if (resources != null) - { - for (ResourceModel resource : resources) - { - Set mapping = resource.getRoleMappings(user); - if (mapping == null) continue; - SkeletonKeyToken.Access access = token.addAccess(resource.getName()) - .verifyCaller(resource.isSurrogateAuthRequired()); - for (String role : mapping) - { - access.addRole(role); + token.setRealmAccess(access); + } + if (resources != null) { + for (ResourceModel resource : resources) { + Set mapping = resource.getRoleMappings(user); + if (mapping == null) continue; + SkeletonKeyToken.Access access = token.addAccess(resource.getName()) + .verifyCaller(resource.isSurrogateAuthRequired()); + for (String role : mapping) { + access.addRole(role); + } } - } - } - return token; - } + } + return token; + } - public SkeletonKeyToken createIdentityToken(RealmModel realm, String username) - { - SkeletonKeyToken token = new SkeletonKeyToken(); - token.id(adapter.generateId()); - token.issuedNow(); - token.principal(username); - token.audience(realm.getId()); - if (realm.getTokenLifespan() > 0) - { - token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); - } - return token; - } + public SkeletonKeyToken createIdentityToken(RealmModel realm, String username) { + SkeletonKeyToken token = new SkeletonKeyToken(); + token.id(RealmManager.generateId()); + token.issuedNow(); + token.principal(username); + token.audience(realm.getId()); + if (realm.getTokenLifespan() > 0) { + token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan()); + } + return token; + } - public String encodeToken(RealmModel realm, SkeletonKeyToken token) - { - byte[] tokenBytes = null; - try - { - tokenBytes = JsonSerialization.toByteArray(token, false); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - String encodedToken = new JWSBuilder() - .content(tokenBytes) - .rsa256(realm.getPrivateKey()); - return encodedToken; - } + public String encodeToken(RealmModel realm, SkeletonKeyToken token) { + byte[] tokenBytes = null; + try { + tokenBytes = JsonSerialization.toByteArray(token, false); + } catch (Exception e) { + throw new RuntimeException(e); + } + String encodedToken = new JWSBuilder() + .content(tokenBytes) + .rsa256(realm.getPrivateKey()); + return encodedToken; + } } diff --git a/services/src/main/java/org/keycloak/services/models/RealmManager.java b/services/src/main/java/org/keycloak/services/models/RealmManager.java index 02485bf163..6ee87cea25 100755 --- a/services/src/main/java/org/keycloak/services/models/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/models/RealmManager.java @@ -1,49 +1,71 @@ package org.keycloak.services.models; +import org.keycloak.representations.idm.UserRepresentation; +import org.picketlink.idm.IdentitySession; import org.picketlink.idm.IdentityManager; -import org.picketlink.idm.internal.IdentityManagerFactory; import org.picketlink.idm.model.Realm; +import org.picketlink.idm.model.Role; import org.picketlink.idm.model.SimpleAgent; +import org.picketlink.idm.model.SimpleUser; +import org.picketlink.idm.model.User; +import javax.ws.rs.core.Response; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; import java.util.concurrent.atomic.AtomicLong; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class RealmManager -{ - private static AtomicLong counter = new AtomicLong(1); +public class RealmManager { + private static AtomicLong counter = new AtomicLong(1); - public static String generateId() - { - return counter.getAndIncrement() + "-" + System.currentTimeMillis(); - } + public static String generateId() { + return counter.getAndIncrement() + "-" + System.currentTimeMillis(); + } - protected IdentityManagerFactory factory; + protected IdentitySession IdentitySession; - public RealmManager(IdentityManagerFactory factory) - { - this.factory = factory; - } + public RealmManager(IdentitySession IdentitySession) { + this.IdentitySession = IdentitySession; + } - public RealmModel getRealm(String id) - { - Realm existing = factory.findRealm(id); - if (existing == null) - { - return null; - } - return new RealmModel(existing, factory); - } + public RealmModel defaultRealm() { + return getRealm(Realm.DEFAULT_REALM); + } - public RealmModel create(String name) - { - Realm newRealm = factory.createRealm(generateId()); - IdentityManager idm = factory.createIdentityManager(newRealm); - SimpleAgent agent = new SimpleAgent(RealmModel.REALM_AGENT_ID); - idm.add(agent); - return new RealmModel(newRealm, factory); - } + public RealmModel getRealm(String id) { + Realm existing = IdentitySession.findRealm(id); + if (existing == null) { + return null; + } + return new RealmModel(existing, IdentitySession); + } -} + public RealmModel createRealm(String name) { + return createRealm(generateId(), name); + } + + public RealmModel createRealm(String id, String name) { + Realm newRealm = IdentitySession.createRealm(id); + IdentityManager idm = IdentitySession.createIdentityManager(newRealm); + SimpleAgent agent = new SimpleAgent(RealmModel.REALM_AGENT_ID); + idm.add(agent); + RealmModel realm = new RealmModel(newRealm, IdentitySession); + return realm; + } + + public void generateRealmKeys(RealmModel realm) { + KeyPair keyPair = null; + try { + keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + realm.setPrivateKey(keyPair.getPrivate()); + realm.setPublicKey(keyPair.getPublic()); + realm.updateRealm(); + } + } 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 6028bbf65d..0cdf45ca01 100755 --- a/services/src/main/java/org/keycloak/services/models/RealmModel.java +++ b/services/src/main/java/org/keycloak/services/models/RealmModel.java @@ -4,20 +4,19 @@ import org.bouncycastle.openssl.PEMWriter; import org.jboss.resteasy.security.PemUtils; import org.keycloak.representations.idm.RequiredCredentialRepresentation; import org.keycloak.services.models.relationships.RealmAdminRelationship; -import org.keycloak.services.models.relationships.RealmResourceRelationship; +import org.keycloak.services.models.relationships.ResourceRelationship; import org.keycloak.services.models.relationships.RequiredCredentialRelationship; import org.keycloak.services.models.relationships.ScopeRelationship; +import org.picketlink.idm.IdentitySession; import org.picketlink.idm.IdentityManager; import org.picketlink.idm.credential.Password; import org.picketlink.idm.credential.TOTPCredential; import org.picketlink.idm.credential.X509CertificateCredentials; -import org.picketlink.idm.internal.IdentityManagerFactory; import org.picketlink.idm.model.Agent; 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; @@ -39,374 +38,311 @@ import java.util.Set; * @author Bill Burke * @version $Revision: 1 $ */ -public class RealmModel -{ - public static final String REALM_AGENT_ID = "_realm_"; - public static final String REALM_NAME = "name"; - public static final String REALM_ACCESS_CODE_LIFESPAN = "accessCodeLifespan"; - public static final String REALM_TOKEN_LIFESPAN = "tokenLifespan"; - public static final String REALM_PRIVATE_KEY = "privateKey"; - 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 class RealmModel { + public static final String REALM_AGENT_ID = "_realm_"; + public static final String REALM_NAME = "name"; + public static final String REALM_ACCESS_CODE_LIFESPAN = "accessCodeLifespan"; + public static final String REALM_TOKEN_LIFESPAN = "tokenLifespan"; + public static final String REALM_PRIVATE_KEY = "privateKey"; + 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 IdentityManagerFactory factory; - protected volatile transient PublicKey publicKey; - protected volatile transient PrivateKey privateKey; + protected Realm realm; + protected Agent realmAgent; + protected IdentitySession identitySession; + protected volatile transient PublicKey publicKey; + protected volatile transient PrivateKey privateKey; - public RealmModel(Realm realm, IdentityManagerFactory factory) - { - this.realm = realm; - this.factory = factory; - realmAgent = getIdm().getAgent(REALM_AGENT_ID); - } + public RealmModel(Realm realm, IdentitySession session) { + this.realm = realm; + this.identitySession = session; + realmAgent = getIdm().getAgent(REALM_AGENT_ID); + } - public IdentityManager getIdm() - { - return factory.createIdentityManager(realm); - } + public IdentityManager getIdm() { + return identitySession.createIdentityManager(realm); + } - public void updateRealm() - { - getIdm().update(realmAgent); - } + public void updateRealm() { + getIdm().update(realmAgent); + } - public String getId() - { - return realm.getId(); - } + public String getId() { + return realm.getId(); + } - public String getName() - { - return (String)realmAgent.getAttribute(REALM_NAME).getValue(); - } + public String getName() { + return (String) realmAgent.getAttribute(REALM_NAME).getValue(); + } - public void setName(String name) - { - realmAgent.setAttribute(new Attribute(REALM_NAME, name)); - } + public void setName(String name) { + realmAgent.setAttribute(new Attribute(REALM_NAME, name)); + } - public boolean isEnabled() - { - return realmAgent.isEnabled(); - } + public boolean isEnabled() { + return realmAgent.isEnabled(); + } - public void setEnabled(boolean enabled) - { - realmAgent.setEnabled(enabled); - } + public void setEnabled(boolean enabled) { + realmAgent.setEnabled(enabled); + } - public boolean isSslNotRequired() - { - return (Boolean)realmAgent.getAttribute(REALM_IS_SSL_NOT_REQUIRED).getValue(); - } + public boolean isSslNotRequired() { + return (Boolean) realmAgent.getAttribute(REALM_IS_SSL_NOT_REQUIRED).getValue(); + } - public void setSslNotRequired(boolean sslNotRequired) - { - realmAgent.setAttribute(new Attribute(REALM_IS_SSL_NOT_REQUIRED, sslNotRequired)); - } + public void setSslNotRequired(boolean sslNotRequired) { + realmAgent.setAttribute(new Attribute(REALM_IS_SSL_NOT_REQUIRED, sslNotRequired)); + } - public boolean isCookieLoginAllowed() - { - return (Boolean)realmAgent.getAttribute(REALM_IS_COOKIE_LOGIN_ALLOWED).getValue(); - } + public boolean isCookieLoginAllowed() { + return (Boolean) realmAgent.getAttribute(REALM_IS_COOKIE_LOGIN_ALLOWED).getValue(); + } - public void setCookieLoginAllowed(boolean cookieLoginAllowed) - { - realmAgent.setAttribute(new Attribute(REALM_IS_COOKIE_LOGIN_ALLOWED, cookieLoginAllowed)); - } + public void setCookieLoginAllowed(boolean cookieLoginAllowed) { + realmAgent.setAttribute(new Attribute(REALM_IS_COOKIE_LOGIN_ALLOWED, cookieLoginAllowed)); + } - public long getTokenLifespan() - { - return (Long) realmAgent.getAttribute(REALM_TOKEN_LIFESPAN).getValue(); - } + public boolean isRegistrationAllowed() { + return (Boolean) realmAgent.getAttribute(REALM_IS_REGISTRATION_ALLOWED).getValue(); + } - public void setTokenLifespan(long tokenLifespan) - { - realmAgent.setAttribute(new Attribute(REALM_TOKEN_LIFESPAN,tokenLifespan)); - } + public void setRegistrationAllowed(boolean registrationAllowed) { + realmAgent.setAttribute(new Attribute(REALM_IS_REGISTRATION_ALLOWED, registrationAllowed)); + } - public long getAccessCodeLifespan() - { - return (Long) realmAgent.getAttribute(REALM_ACCESS_CODE_LIFESPAN).getValue(); - } + public long getTokenLifespan() { + return (Long) realmAgent.getAttribute(REALM_TOKEN_LIFESPAN).getValue(); + } - public void setAccessCodeLifespan(long accessCodeLifespan) - { - realmAgent.setAttribute(new Attribute(REALM_ACCESS_CODE_LIFESPAN, accessCodeLifespan)); - } + public void setTokenLifespan(long tokenLifespan) { + realmAgent.setAttribute(new Attribute(REALM_TOKEN_LIFESPAN, tokenLifespan)); + } - public String getPublicKeyPem() - { - return (String) realmAgent.getAttribute(REALM_PUBLIC_KEY).getValue(); - } + public long getAccessCodeLifespan() { + return (Long) realmAgent.getAttribute(REALM_ACCESS_CODE_LIFESPAN).getValue(); + } - public void setPublicKeyPem(String publicKeyPem) - { - realmAgent.setAttribute(new Attribute(REALM_PUBLIC_KEY, publicKeyPem)); - this.publicKey = null; - } + public void setAccessCodeLifespan(long accessCodeLifespan) { + realmAgent.setAttribute(new Attribute(REALM_ACCESS_CODE_LIFESPAN, accessCodeLifespan)); + } - public String getPrivateKeyPem() - { - return (String) realmAgent.getAttribute(REALM_PRIVATE_KEY).getValue(); - } + public String getPublicKeyPem() { + return (String) realmAgent.getAttribute(REALM_PUBLIC_KEY).getValue(); + } - public void setPrivateKeyPem(String privateKeyPem) - { - realmAgent.setAttribute(new Attribute(REALM_PRIVATE_KEY, privateKeyPem)); - this.privateKey = null; - } + public void setPublicKeyPem(String publicKeyPem) { + realmAgent.setAttribute(new Attribute(REALM_PUBLIC_KEY, publicKeyPem)); + this.publicKey = null; + } - public PublicKey getPublicKey() - { - if (publicKey != null) return publicKey; - String pem = getPublicKeyPem(); - if (pem != null) - { - try - { - publicKey = PemUtils.decodePublicKey(pem); - } - catch (Exception e) - { + public String getPrivateKeyPem() { + return (String) realmAgent.getAttribute(REALM_PRIVATE_KEY).getValue(); + } + + public void setPrivateKeyPem(String privateKeyPem) { + realmAgent.setAttribute(new Attribute(REALM_PRIVATE_KEY, privateKeyPem)); + this.privateKey = null; + } + + public PublicKey getPublicKey() { + if (publicKey != null) return publicKey; + String pem = getPublicKeyPem(); + if (pem != null) { + try { + publicKey = PemUtils.decodePublicKey(pem); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return publicKey; + } + + public void setPublicKey(PublicKey publicKey) { + this.publicKey = publicKey; + StringWriter writer = new StringWriter(); + PEMWriter pemWriter = new PEMWriter(writer); + try { + pemWriter.writeObject(publicKey); + pemWriter.flush(); + } catch (IOException e) { throw new RuntimeException(e); - } - } - return publicKey; - } + } + String s = writer.toString(); + setPublicKeyPem(PemUtils.removeBeginEnd(s)); + } - public void setPublicKey(PublicKey publicKey) - { - this.publicKey = publicKey; - StringWriter writer = new StringWriter(); - PEMWriter pemWriter = new PEMWriter(writer); - try - { - pemWriter.writeObject(publicKey); - pemWriter.flush(); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - String s = writer.toString(); - setPublicKeyPem(PemUtils.removeBeginEnd(s)); - } + public PrivateKey getPrivateKey() { + if (privateKey != null) return privateKey; + String pem = getPrivateKeyPem(); + if (pem != null) { + try { + privateKey = PemUtils.decodePrivateKey(pem); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return privateKey; + } - public PrivateKey getPrivateKey() - { - if (privateKey != null) return privateKey; - String pem = getPrivateKeyPem(); - if (pem != null) - { - try - { - privateKey = PemUtils.decodePrivateKey(pem); - } - catch (Exception e) - { + public void setPrivateKey(PrivateKey privateKey) { + this.privateKey = privateKey; + StringWriter writer = new StringWriter(); + PEMWriter pemWriter = new PEMWriter(writer); + try { + pemWriter.writeObject(privateKey); + pemWriter.flush(); + } catch (IOException e) { throw new RuntimeException(e); - } - } - return privateKey; - } + } + String s = writer.toString(); + setPrivateKeyPem(PemUtils.removeBeginEnd(s)); + } - public void setPrivateKey(PrivateKey privateKey) - { - this.privateKey = privateKey; - StringWriter writer = new StringWriter(); - PEMWriter pemWriter = new PEMWriter(writer); - try - { - pemWriter.writeObject(privateKey); - pemWriter.flush(); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - String s = writer.toString(); - setPrivateKeyPem(PemUtils.removeBeginEnd(s)); - } + public List getRequiredCredentials() { + IdentityManager idm = getIdm(); + Agent realmAgent = idm.getAgent(REALM_AGENT_ID); + RelationshipQuery query = idm.createRelationshipQuery(RequiredCredentialRelationship.class); + query.setParameter(RequiredCredentialRelationship.REALM_AGENT, realmAgent); + List results = query.getResultList(); + List rtn = new ArrayList(); + for (RequiredCredentialRelationship relationship : results) { + RequiredCredentialModel model = new RequiredCredentialModel(); + model.setInput(relationship.isInput()); + model.setSecret(relationship.isSecret()); + model.setType(relationship.getCredentialType()); + rtn.add(model); + } + return rtn; + } - public List getRequiredCredentials() - { - IdentityManager idm = getIdm(); - Agent realmAgent = idm.getAgent(REALM_AGENT_ID); - RelationshipQuery query = idm.createRelationshipQuery(RequiredCredentialRelationship.class); - query.setParameter(RequiredCredentialRelationship.REALM_AGENT, realmAgent); - List results = query.getResultList(); - List rtn = new ArrayList(); - for (RequiredCredentialRelationship relationship : results) - { - RequiredCredentialModel model = new RequiredCredentialModel(); - model.setInput(relationship.isInput()); - model.setSecret(relationship.isSecret()); - model.setType(relationship.getCredentialType()); - rtn.add(model); - } - return rtn; - } + public void addRequiredCredential(RequiredCredentialModel cred) { + IdentityManager idm = getIdm(); + Agent realmAgent = idm.getAgent(REALM_AGENT_ID); + RequiredCredentialRelationship relationship = new RequiredCredentialRelationship(); + relationship.setCredentialType(cred.getType()); + relationship.setInput(cred.isInput()); + relationship.setSecret(cred.isSecret()); + relationship.setRealmAgent(realmAgent); + idm.add(relationship); + } - public void addRequiredCredential(RequiredCredentialModel cred) - { - IdentityManager idm = getIdm(); - Agent realmAgent = idm.getAgent(REALM_AGENT_ID); - RequiredCredentialRelationship relationship = new RequiredCredentialRelationship(); - relationship.setCredentialType(cred.getType()); - relationship.setInput(cred.isInput()); - relationship.setSecret(cred.isSecret()); - relationship.setRealmAgent(realmAgent); - idm.add(relationship); - } + public void updateCredential(User user, UserCredentialModel cred) { + IdentityManager idm = getIdm(); + if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) { + Password password = new Password(cred.getValue()); + idm.updateCredential(user, password); + } else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) { + TOTPCredential totp = new TOTPCredential(cred.getValue()); + idm.updateCredential(user, totp); + } else if (cred.getType().equals(RequiredCredentialRepresentation.CLIENT_CERT)) { + X509Certificate cert = null; + try { + cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue()); + } catch (Exception e) { + throw new RuntimeException(e); + } + X509CertificateCredentials creds = new X509CertificateCredentials(cert); + idm.updateCredential(user, creds); + } + } - public void updateCredential(User user, UserCredentialModel cred) - { - IdentityManager idm = getIdm(); - if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) - { - Password password = new Password(cred.getValue()); - idm.updateCredential(user, password); - } - else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) - { - TOTPCredential totp = new TOTPCredential(cred.getValue()); - idm.updateCredential(user, totp); - } - else if (cred.getType().equals(RequiredCredentialRepresentation.CLIENT_CERT)) - { - X509Certificate cert = null; - try - { - cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue()); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - X509CertificateCredentials creds = new X509CertificateCredentials(cert); - idm.updateCredential(user, creds); - } - } - - public List getRoles() - { - IdentityManager idm = getIdm(); - IdentityQuery query = idm.createIdentityQuery(Role.class); - query.setParameter(Role.PARTITION, realm); - return query.getResultList(); - } + public List getRoles() { + IdentityManager idm = getIdm(); + IdentityQuery query = idm.createIdentityQuery(Role.class); + query.setParameter(Role.PARTITION, realm); + return query.getResultList(); + } - protected ResourceModel loadResource(Agent resource) - { - Tier tier = factory.findTier(resource.getPartition().getId()); - return new ResourceModel(tier, resource, this, factory); - } + /** + * Key name, value resource + * + * @return + */ + public Map getResourceMap() { + Map resourceMap = new HashMap(); + for (ResourceModel resource : getResources()) { + resourceMap.put(resource.getName(), resource); + } + return resourceMap; + } - /** - * Key name, value resource - * - * @return - */ - public Map getResourceMap() - { - Map resourceMap = new HashMap(); - for (ResourceModel resource : getResources()) - { - resourceMap.put(resource.getName(), resource); - } - return resourceMap; - } + public List getResources() { + IdentityManager idm = getIdm(); + RelationshipQuery query = idm.createRelationshipQuery(ResourceRelationship.class); + query.setParameter(ResourceRelationship.REALM_AGENT, realmAgent); + 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); + resources.add(model); + } - public List getResources() - { - IdentityManager idm = getIdm(); - RelationshipQuery query = idm.createRelationshipQuery(RealmResourceRelationship.class); - query.setParameter(RealmResourceRelationship.REALM_AGENT, realmAgent); - List results = query.getResultList(); - List resources = new ArrayList(); - for (RealmResourceRelationship relationship : results) - { - ResourceModel model = loadResource(relationship.getResourceAgent()); - resources.add(model); - } + return resources; + } - return resources; - } + public ResourceModel addResource(String name) { + 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); + } - public ResourceModel addResource(String name) - { - Tier newTier = factory.createTier(RealmManager.generateId()); - IdentityManager idm = factory.createIdentityManager(newTier); - SimpleAgent resourceAgent = new SimpleAgent(ResourceModel.RESOURCE_AGENT_ID); - resourceAgent.setAttribute(new Attribute(ResourceModel.RESOURCE_NAME, name)); - idm.add(resourceAgent); - idm = getIdm(); - RealmResourceRelationship relationship = new RealmResourceRelationship(); - relationship.setRealmAgent(realmAgent); - relationship.setResourceAgent(resourceAgent); - idm.add(relationship); - return new ResourceModel(newTier, resourceAgent, this, factory); - } + public Set getRoleMappings(User user) { + RelationshipQuery query = getIdm().createRelationshipQuery(Grant.class); + query.setParameter(Grant.ASSIGNEE, user); + List grants = query.getResultList(); + HashSet set = new HashSet(); + for (Grant grant : grants) { + if (grant.getRole().getPartition().getId().equals(realm.getId())) set.add(grant.getRole().getName()); + } + return set; + } - public Set getRoleMappings(User user) - { - RelationshipQuery query = getIdm().createRelationshipQuery(Grant.class); - query.setParameter(Grant.ASSIGNEE, user); - List grants = query.getResultList(); - HashSet set = new HashSet(); - for (Grant grant : grants) - { - if (grant.getRole().getPartition().getId().equals(realm.getId()))set.add(grant.getRole().getName()); - } - return set; - } + public void addScope(Agent agent, String roleName) { + IdentityManager idm = getIdm(); + Role role = idm.getRole(roleName); + if (role == null) throw new RuntimeException("role not found"); + ScopeRelationship scope = new ScopeRelationship(); + scope.setClient(agent); + scope.setScope(role); - public void addScope(Agent agent, String roleName) - { - IdentityManager idm = getIdm(); - Role role = idm.getRole(roleName); - if (role == null) throw new RuntimeException("role not found"); - ScopeRelationship scope = new ScopeRelationship(); - scope.setClient(agent); - scope.setScope(role); - - } + } - public Set getScope(Agent agent) - { - RelationshipQuery query = getIdm().createRelationshipQuery(ScopeRelationship.class); - query.setParameter(ScopeRelationship.CLIENT, agent); - List scope = query.getResultList(); - HashSet set = new HashSet(); - for (ScopeRelationship rel : scope) - { - if (rel.getScope().getPartition().getId().equals(realm.getId())) set.add(rel.getScope().getName()); - } - return set; - } + public Set getScope(Agent agent) { + RelationshipQuery query = getIdm().createRelationshipQuery(ScopeRelationship.class); + query.setParameter(ScopeRelationship.CLIENT, agent); + List scope = query.getResultList(); + HashSet set = new HashSet(); + for (ScopeRelationship rel : scope) { + if (rel.getScope().getPartition().getId().equals(realm.getId())) set.add(rel.getScope().getName()); + } + return set; + } - public boolean isRealmAdmin(Agent agent) - { - IdentityManager idm = getIdm(); - RelationshipQuery query = idm.createRelationshipQuery(RealmAdminRelationship.class); - query.setParameter(RealmAdminRelationship.REALM, realm); - query.setParameter(RealmAdminRelationship.ADMIN, agent); - List results = query.getResultList(); - return results.size() > 0; - } + public boolean isRealmAdmin(Agent agent) { + IdentityManager idm = new RealmManager(identitySession).defaultRealm().getIdm(); + RelationshipQuery query = idm.createRelationshipQuery(RealmAdminRelationship.class); + query.setParameter(RealmAdminRelationship.REALM, realm.getId()); + query.setParameter(RealmAdminRelationship.ADMIN, agent); + List results = query.getResultList(); + return results.size() > 0; + } - public void addRealmAdmin(Agent agent) - { - IdentityManager idm = getIdm(); - RealmAdminRelationship relationship = new RealmAdminRelationship(); - relationship.setAdmin(agent); - relationship.setRealm(realm); - idm.add(relationship); - } + public void addRealmAdmin(Agent agent) { + IdentityManager idm = new RealmManager(identitySession).defaultRealm().getIdm(); + RealmAdminRelationship relationship = new RealmAdminRelationship(); + relationship.setAdmin(agent); + relationship.setRealm(realm.getId()); + idm.add(relationship); + } } diff --git a/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java b/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java index 6476ef70e6..c8ca58490d 100755 --- a/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java +++ b/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java @@ -1,42 +1,48 @@ package org.keycloak.services.models; +import org.keycloak.representations.idm.RequiredCredentialRepresentation; + /** * @author Bill Burke * @version $Revision: 1 $ */ -public class RequiredCredentialModel -{ - protected String type; - protected boolean input; - protected boolean secret; +public class RequiredCredentialModel { + protected String type; + protected boolean input; + protected boolean secret; - public String getType() - { - return type; - } + public RequiredCredentialModel() { + } - public void setType(String type) - { - this.type = type; - } + public RequiredCredentialModel(String type, boolean input, boolean secret) { + this.type = type; + this.input = input; + this.secret = secret; + } - public boolean isInput() - { - return input; - } + public String getType() { + return type; + } - public void setInput(boolean input) - { - this.input = input; - } + public void setType(String type) { + this.type = type; + } - public boolean isSecret() - { - return secret; - } + public boolean isInput() { + return input; + } - public void setSecret(boolean secret) - { - this.secret = secret; - } + public void setInput(boolean input) { + this.input = input; + } + + public boolean isSecret() { + return secret; + } + + public void setSecret(boolean secret) { + this.secret = secret; + } + + public static final RequiredCredentialModel PASSWORD = new RequiredCredentialModel(RequiredCredentialRepresentation.PASSWORD, true, true); } diff --git a/services/src/main/java/org/keycloak/services/models/ResourceModel.java b/services/src/main/java/org/keycloak/services/models/ResourceModel.java index b41edd6c6c..cb343b0989 100755 --- a/services/src/main/java/org/keycloak/services/models/ResourceModel.java +++ b/services/src/main/java/org/keycloak/services/models/ResourceModel.java @@ -1,8 +1,9 @@ package org.keycloak.services.models; +import org.keycloak.services.models.relationships.ResourceRelationship; import org.keycloak.services.models.relationships.ScopeRelationship; +import org.picketlink.idm.IdentitySession; import org.picketlink.idm.IdentityManager; -import org.picketlink.idm.internal.IdentityManagerFactory; import org.picketlink.idm.model.Agent; import org.picketlink.idm.model.Attribute; import org.picketlink.idm.model.Grant; @@ -20,112 +21,94 @@ import java.util.Set; * @author Bill Burke * @version $Revision: 1 $ */ -public class ResourceModel -{ - public static final String RESOURCE_AGENT_ID = "_resource_"; - public static final String RESOURCE_NAME = "name"; - public static final String RESOURCE_SURROGATE_AUTH = "surrogate_auth"; +public class ResourceModel { + public static final String RESOURCE_AGENT_ID = "_resource_"; + public static final String RESOURCE_NAME = "name"; + public static final String RESOURCE_SURROGATE_AUTH = "surrogate_auth"; - protected Tier tier; - protected Agent agent; - protected RealmModel realm; - protected IdentityManagerFactory factory; + protected Tier tier; + protected ResourceRelationship agent; + protected RealmModel realm; + protected IdentitySession IdentitySession; - public ResourceModel(Tier tier, Agent agent, RealmModel realm, IdentityManagerFactory factory) - { - this.tier = tier; - this.agent = agent; - this.realm = realm; - this.factory = factory; - } + public ResourceModel(Tier tier, ResourceRelationship agent, RealmModel realm, IdentitySession factory) { + this.tier = tier; + this.agent = agent; + this.realm = realm; + this.IdentitySession = factory; + } - public IdentityManager getIdm() - { - return factory.createIdentityManager(tier); - } + public IdentityManager getIdm() { + return IdentitySession.createIdentityManager(tier); + } - public void updateResource() - { - getIdm().update(agent); - } + public void updateResource() { + getIdm().update(agent); + } - public String getId() - { - return tier.getId(); - } + public String getId() { + return tier.getId(); + } - public String getName() - { - return (String)agent.getAttribute(RESOURCE_NAME).getValue(); - } + public String getName() { + return agent.getResourceName(); + } - public void setName(String name) - { - agent.setAttribute(new Attribute(RESOURCE_NAME, name)); - getIdm().update(agent); - } + public void setName(String name) { + agent.setResourceName(name); + } - public boolean isEnabled() - { - return agent.isEnabled(); - } + public boolean isEnabled() { + return agent.getEnabled(); + } - public void setEnabled(boolean enabled) - { - agent.setEnabled(enabled); - } + public void setEnabled(boolean enabled) { + agent.setEnabled(enabled); + } - public boolean isSurrogateAuthRequired() - { - return (Boolean)agent.getAttribute(RESOURCE_SURROGATE_AUTH).getValue(); - } + public boolean isSurrogateAuthRequired() { + return agent.getSurrogateAuthRequired(); + } - public void setSurrogateAuthRequired(boolean surrogateAuthRequired) - { - agent.setAttribute(new Attribute(RESOURCE_SURROGATE_AUTH, surrogateAuthRequired)); - } + public void setSurrogateAuthRequired(boolean surrogateAuthRequired) { + agent.setSurrogateAuthRequired(surrogateAuthRequired); + } - public List getRoles() - { - IdentityQuery query = getIdm().createIdentityQuery(Role.class); - query.setParameter(Role.PARTITION, tier); - return query.getResultList(); - } + public List getRoles() { + IdentityQuery query = getIdm().createIdentityQuery(Role.class); + query.setParameter(Role.PARTITION, tier); + return query.getResultList(); + } - public Set getRoleMappings(User user) - { - RelationshipQuery query = getIdm().createRelationshipQuery(Grant.class); - query.setParameter(Grant.ASSIGNEE, user); - List grants = query.getResultList(); - HashSet set = new HashSet(); - for (Grant grant : grants) - { - if (grant.getRole().getPartition().getId().equals(tier.getId()))set.add(grant.getRole().getName()); - } - return set; - } + public Set getRoleMappings(User user) { + RelationshipQuery query = getIdm().createRelationshipQuery(Grant.class); + query.setParameter(Grant.ASSIGNEE, user); + List grants = query.getResultList(); + HashSet set = new HashSet(); + for (Grant grant : grants) { + if (grant.getRole().getPartition().getId().equals(tier.getId())) set.add(grant.getRole().getName()); + } + return set; + } - public void addScope(Agent agent, String roleName) - { - IdentityManager idm = getIdm(); - Role role = idm.getRole(roleName); - if (role == null) throw new RuntimeException("role not found"); - ScopeRelationship scope = new ScopeRelationship(); - scope.setClient(agent); - scope.setScope(role); + public void addScope(Agent agent, String roleName) { + IdentityManager idm = getIdm(); + Role role = idm.getRole(roleName); + if (role == null) throw new RuntimeException("role not found"); + ScopeRelationship scope = new ScopeRelationship(); + scope.setClient(agent); + scope.setScope(role); - } + } - public Set getScope(Agent agent) - { - RelationshipQuery query = getIdm().createRelationshipQuery(ScopeRelationship.class); - query.setParameter(ScopeRelationship.CLIENT, agent); - List scope = query.getResultList(); - HashSet set = new HashSet(); - for (ScopeRelationship rel : scope) - { - if (rel.getScope().getPartition().getId().equals(tier.getId())) set.add(rel.getScope().getName()); - } - return set; - } + public Set getScope(Agent agent) { + RelationshipQuery query = getIdm().createRelationshipQuery(ScopeRelationship.class); + query.setParameter(ScopeRelationship.CLIENT, agent); + List scope = query.getResultList(); + HashSet set = new HashSet(); + for (ScopeRelationship rel : scope) { + if (rel.getScope().getPartition().getId().equals(tier.getId())) set.add(rel.getScope().getName()); + } + return set; + } } diff --git a/services/src/main/java/org/keycloak/services/models/UserCredentialModel.java b/services/src/main/java/org/keycloak/services/models/UserCredentialModel.java index e2a3603615..5807319db5 100755 --- a/services/src/main/java/org/keycloak/services/models/UserCredentialModel.java +++ b/services/src/main/java/org/keycloak/services/models/UserCredentialModel.java @@ -4,29 +4,24 @@ package org.keycloak.services.models; * @author Bill Burke * @version $Revision: 1 $ */ -public class UserCredentialModel -{ +public class UserCredentialModel { - protected String type; - protected String value; + protected String type; + protected String value; - public String getType() - { - return type; - } + public String getType() { + return type; + } - public void setType(String type) - { - this.type = type; - } + public void setType(String type) { + this.type = type; + } - public String getValue() - { - return value; - } + public String getValue() { + return value; + } - public void setValue(String value) - { - this.value = value; - } + public void setValue(String value) { + this.value = value; + } } diff --git a/services/src/main/java/org/keycloak/services/models/relationships/RealmAdminRelationship.java b/services/src/main/java/org/keycloak/services/models/relationships/RealmAdminRelationship.java index cb24a98cae..cf68d46829 100755 --- a/services/src/main/java/org/keycloak/services/models/relationships/RealmAdminRelationship.java +++ b/services/src/main/java/org/keycloak/services/models/relationships/RealmAdminRelationship.java @@ -4,6 +4,7 @@ import org.picketlink.idm.model.AbstractAttributedType; import org.picketlink.idm.model.Agent; import org.picketlink.idm.model.Realm; import org.picketlink.idm.model.Relationship; +import org.picketlink.idm.model.annotation.AttributeProperty; import org.picketlink.idm.model.annotation.IdentityProperty; import org.picketlink.idm.query.RelationshipQueryParameter; @@ -11,48 +12,43 @@ import org.picketlink.idm.query.RelationshipQueryParameter; * @author Bill Burke * @version $Revision: 1 $ */ -public class RealmAdminRelationship extends AbstractAttributedType implements Relationship -{ - private static final long serialVersionUID = 1L; +public class RealmAdminRelationship extends AbstractAttributedType implements Relationship { + private static final long serialVersionUID = 1L; - public static final RelationshipQueryParameter REALM = new RelationshipQueryParameter() { + public static final RelationshipQueryParameter REALM = new RelationshipQueryParameter() { - @Override - public String getName() { - return "realm"; - } - }; + @Override + public String getName() { + return "realm"; + } + }; - public static final RelationshipQueryParameter ADMIN = new RelationshipQueryParameter() { + public static final RelationshipQueryParameter ADMIN = new RelationshipQueryParameter() { - @Override - public String getName() { - return "admin"; - } - }; + @Override + public String getName() { + return "admin"; + } + }; - protected Realm realm; - protected Agent admin; + protected String realm; + protected Agent admin; - @IdentityProperty - public Realm getRealm() - { - return realm; - } + @AttributeProperty + public String getRealm() { + return realm; + } - public void setRealm(Realm realm) - { - this.realm = realm; - } + public void setRealm(String realm) { + this.realm = realm; + } - @IdentityProperty - public Agent getAdmin() - { - return admin; - } + @IdentityProperty + public Agent getAdmin() { + return admin; + } - public void setAdmin(Agent admin) - { - this.admin = admin; - } + public void setAdmin(Agent admin) { + this.admin = admin; + } } diff --git a/services/src/main/java/org/keycloak/services/models/relationships/RealmResourceRelationship.java b/services/src/main/java/org/keycloak/services/models/relationships/RealmResourceRelationship.java deleted file mode 100755 index ed5a8f28b3..0000000000 --- a/services/src/main/java/org/keycloak/services/models/relationships/RealmResourceRelationship.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.keycloak.services.models.relationships; - -import org.picketlink.idm.model.AbstractAttributedType; -import org.picketlink.idm.model.Agent; -import org.picketlink.idm.model.Relationship; -import org.picketlink.idm.model.annotation.IdentityProperty; -import org.picketlink.idm.query.RelationshipQueryParameter; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class RealmResourceRelationship extends AbstractAttributedType implements Relationship -{ - private static final long serialVersionUID = 1L; - - public static final RelationshipQueryParameter REALM_AGENT = new RelationshipQueryParameter() { - - @Override - public String getName() { - return "realmAgent"; - } - }; - - public static final RelationshipQueryParameter RESOURCE_AGENT = new RelationshipQueryParameter() { - - @Override - public String getName() { - return "resourceAgent"; - } - }; - - protected Agent realmAgent; - protected Agent resourceAgent; - - @IdentityProperty - public Agent getRealmAgent() - { - return realmAgent; - } - - public void setRealmAgent(Agent realmAgent) - { - this.realmAgent = realmAgent; - } - - @IdentityProperty - public Agent getResourceAgent() - { - return resourceAgent; - } - - public void setResourceAgent(Agent resourceAgent) - { - this.resourceAgent = resourceAgent; - } -} diff --git a/services/src/main/java/org/keycloak/services/models/relationships/RequiredCredentialRelationship.java b/services/src/main/java/org/keycloak/services/models/relationships/RequiredCredentialRelationship.java index 48a1c907ca..59ce02d802 100755 --- a/services/src/main/java/org/keycloak/services/models/relationships/RequiredCredentialRelationship.java +++ b/services/src/main/java/org/keycloak/services/models/relationships/RequiredCredentialRelationship.java @@ -11,69 +11,59 @@ import org.picketlink.idm.query.RelationshipQueryParameter; * @author Bill Burke * @version $Revision: 1 $ */ -public class RequiredCredentialRelationship extends AbstractAttributedType implements Relationship -{ - private static final long serialVersionUID = 1L; +public class RequiredCredentialRelationship extends AbstractAttributedType implements Relationship { + private static final long serialVersionUID = 1L; - public static final RelationshipQueryParameter REALM_AGENT = new RelationshipQueryParameter() { + public static final RelationshipQueryParameter REALM_AGENT = new RelationshipQueryParameter() { - @Override - public String getName() { - return "realmAgent"; - } - }; + @Override + public String getName() { + return "realmAgent"; + } + }; - protected Agent realmAgent; - protected String credentialType; - protected boolean input; - protected boolean secret; + protected Agent realmAgent; + protected String credentialType; + protected boolean input; + protected boolean secret; - public RequiredCredentialRelationship() - { - } + public RequiredCredentialRelationship() { + } - @IdentityProperty - public Agent getRealmAgent() - { - return realmAgent; - } + @IdentityProperty + public Agent getRealmAgent() { + return realmAgent; + } - public void setRealmAgent(Agent realmAgent) - { - this.realmAgent = realmAgent; - } + public void setRealmAgent(Agent realmAgent) { + this.realmAgent = realmAgent; + } - @AttributeProperty - public String getCredentialType() - { - return credentialType; - } + @AttributeProperty + public String getCredentialType() { + return credentialType; + } - public void setCredentialType(String credentialType) - { - this.credentialType = credentialType; - } + public void setCredentialType(String credentialType) { + this.credentialType = credentialType; + } - @AttributeProperty - public boolean isInput() - { - return input; - } + @AttributeProperty + public boolean isInput() { + return input; + } - public void setInput(boolean input) - { - this.input = input; - } + public void setInput(boolean input) { + this.input = input; + } - @AttributeProperty - public boolean isSecret() - { - return secret; - } + @AttributeProperty + public boolean isSecret() { + return secret; + } - public void setSecret(boolean secret) - { - this.secret = secret; - } + public void setSecret(boolean secret) { + this.secret = secret; + } } diff --git a/services/src/main/java/org/keycloak/services/models/relationships/ResourceRelationship.java b/services/src/main/java/org/keycloak/services/models/relationships/ResourceRelationship.java new file mode 100755 index 0000000000..10d10305f0 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/models/relationships/ResourceRelationship.java @@ -0,0 +1,75 @@ +package org.keycloak.services.models.relationships; + +import org.picketlink.idm.model.AbstractAttributedType; +import org.picketlink.idm.model.Agent; +import org.picketlink.idm.model.Relationship; +import org.picketlink.idm.model.annotation.AttributeProperty; +import org.picketlink.idm.model.annotation.IdentityProperty; +import org.picketlink.idm.query.RelationshipQueryParameter; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ResourceRelationship extends AbstractAttributedType implements Relationship { + private static final long serialVersionUID = 1L; + + public static final RelationshipQueryParameter REALM_AGENT = new RelationshipQueryParameter() { + + @Override + public String getName() { + return "realmAgent"; + } + }; + + protected Agent realmAgent; + protected String resourceId; + protected String resourceName; + protected boolean surrogateAuthRequired; + protected boolean enabled; + + @IdentityProperty + public Agent getRealmAgent() { + return realmAgent; + } + + public void setRealmAgent(Agent realmAgent) { + this.realmAgent = realmAgent; + } + + @AttributeProperty + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + @AttributeProperty + public String getResourceName() { + return resourceName; + } + + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + + @AttributeProperty + public boolean getSurrogateAuthRequired() { + return surrogateAuthRequired; + } + + public void setSurrogateAuthRequired(boolean surrogateAuthRequired) { + this.surrogateAuthRequired = surrogateAuthRequired; + } + + @AttributeProperty + public boolean getEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } +} diff --git a/services/src/main/java/org/keycloak/services/models/relationships/ScopeRelationship.java b/services/src/main/java/org/keycloak/services/models/relationships/ScopeRelationship.java index b8bea48d75..7221c72622 100755 --- a/services/src/main/java/org/keycloak/services/models/relationships/ScopeRelationship.java +++ b/services/src/main/java/org/keycloak/services/models/relationships/ScopeRelationship.java @@ -11,40 +11,35 @@ import org.picketlink.idm.query.RelationshipQueryParameter; * @author Bill Burke * @version $Revision: 1 $ */ -public class ScopeRelationship extends AbstractAttributedType implements Relationship -{ - private static final long serialVersionUID = 1L; +public class ScopeRelationship extends AbstractAttributedType implements Relationship { + private static final long serialVersionUID = 1L; - public static final RelationshipQueryParameter CLIENT = new RelationshipQueryParameter() { + public static final RelationshipQueryParameter CLIENT = new RelationshipQueryParameter() { - @Override - public String getName() { - return "client"; - } - }; + @Override + public String getName() { + return "client"; + } + }; - protected Agent client; - protected Role scope; + protected Agent client; + protected Role scope; - @IdentityProperty - public Agent getClient() - { - return client; - } + @IdentityProperty + public Agent getClient() { + return client; + } - public void setClient(Agent client) - { - this.client = client; - } + public void setClient(Agent client) { + this.client = client; + } - @IdentityProperty - public Role getScope() - { - return scope; - } + @IdentityProperty + public Role getScope() { + return scope; + } - public void setScope(Role scope) - { - this.scope = scope; - } + public void setScope(Role scope) { + this.scope = scope; + } } diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java new file mode 100755 index 0000000000..3af3e9c1f8 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -0,0 +1,90 @@ +package org.keycloak.services.resources; + +import org.keycloak.SkeletonKeyContextResolver; +import org.keycloak.services.filters.IdentitySessionFilter; +import org.keycloak.services.models.relationships.RealmAdminRelationship; +import org.keycloak.services.models.relationships.ResourceRelationship; +import org.keycloak.services.models.relationships.RequiredCredentialRelationship; +import org.keycloak.services.models.relationships.ScopeRelationship; +import org.picketlink.idm.IdentitySessionFactory; +import org.picketlink.idm.config.IdentityConfiguration; +import org.picketlink.idm.config.IdentityConfigurationBuilder; +import org.picketlink.idm.internal.DefaultIdentitySessionFactory; +import org.picketlink.idm.jpa.internal.ResourceLocalJpaIdentitySessionHandler; +import org.picketlink.idm.jpa.schema.CredentialObject; +import org.picketlink.idm.jpa.schema.CredentialObjectAttribute; +import org.picketlink.idm.jpa.schema.IdentityObject; +import org.picketlink.idm.jpa.schema.IdentityObjectAttribute; +import org.picketlink.idm.jpa.schema.PartitionObject; +import org.picketlink.idm.jpa.schema.RelationshipIdentityObject; +import org.picketlink.idm.jpa.schema.RelationshipObject; +import org.picketlink.idm.jpa.schema.RelationshipObjectAttribute; + +import javax.annotation.PreDestroy; +import javax.ws.rs.core.Application; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class KeycloakApplication extends Application { + protected Set singletons = new HashSet(); + protected Set> classes = new HashSet>(); + + protected IdentitySessionFactory factory; + + public KeycloakApplication() { + this.factory = createFactory(); + IdentitySessionFilter filter = new IdentitySessionFilter(factory); + singletons.add(new RealmsResource()); + singletons.add(filter); + classes.add(SkeletonKeyContextResolver.class); + classes.add(RegistrationService.class); + } + + public IdentitySessionFactory getFactory() { + return factory; + } + + @PreDestroy + public void destroy() { + factory.close(); + } + + public static IdentitySessionFactory createFactory() { + ResourceLocalJpaIdentitySessionHandler handler = new ResourceLocalJpaIdentitySessionHandler("keycloak-identity-store"); + IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder(); + + builder + .stores() + .jpa() + .identityClass(IdentityObject.class) + .attributeClass(IdentityObjectAttribute.class) + .relationshipClass(RelationshipObject.class) + .relationshipIdentityClass(RelationshipIdentityObject.class) + .relationshipAttributeClass(RelationshipObjectAttribute.class) + .credentialClass(CredentialObject.class) + .credentialAttributeClass(CredentialObjectAttribute.class) + .partitionClass(PartitionObject.class) + .supportAllFeatures() + .supportRelationshipType(RealmAdminRelationship.class, ResourceRelationship.class, RequiredCredentialRelationship.class, ScopeRelationship.class) + .setIdentitySessionHandler(handler); + + IdentityConfiguration build = builder.build(); + return new DefaultIdentitySessionFactory(build); + } + + + @Override + public Set> getClasses() { + return classes; + } + + @Override + public Set getSingletons() { + return singletons; + } + +} diff --git a/services/src/main/java/org/keycloak/services/resources/RealmFactory.java b/services/src/main/java/org/keycloak/services/resources/RealmFactory.java deleted file mode 100755 index 0ea5dd97dd..0000000000 --- a/services/src/main/java/org/keycloak/services/resources/RealmFactory.java +++ /dev/null @@ -1,343 +0,0 @@ -package org.keycloak.services.resources; - -import org.jboss.resteasy.logging.Logger; -import org.keycloak.representations.idm.RequiredCredentialRepresentation; -import org.keycloak.representations.idm.ResourceRepresentation; -import org.keycloak.representations.idm.RoleMappingRepresentation; -import org.keycloak.representations.idm.ScopeMappingRepresentation; -import org.keycloak.representations.idm.UserRepresentation; -import org.keycloak.services.managers.AuthenticationManager; -import org.keycloak.services.models.RealmManager; -import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.services.models.RealmModel; -import org.keycloak.services.models.RequiredCredentialModel; -import org.keycloak.services.models.ResourceModel; -import org.keycloak.services.models.UserCredentialModel; -import org.picketlink.idm.model.Attribute; -import org.picketlink.idm.model.Realm; -import org.picketlink.idm.model.Role; -import org.picketlink.idm.model.SimpleRole; -import org.picketlink.idm.model.SimpleUser; -import org.picketlink.idm.model.User; - -import javax.ws.rs.Consumes; -import javax.ws.rs.NotAuthorizedException; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.util.HashMap; -import java.util.Map; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -@Path("/realmfactory") -public class RealmFactory -{ - protected Logger logger = Logger.getLogger(RealmFactory.class); - - protected RealmManager adapter; - protected AuthenticationManager authenticationManager; - protected RealmModel defaultRealm; - - - @Context - protected UriInfo uriInfo; - - @Context - protected HttpHeaders headers; - - - public RealmFactory(RealmManager adapter) - { - this.adapter = adapter; - defaultRealm = adapter.getRealm(Realm.DEFAULT_REALM); - } - - @POST - @Consumes("application/json") - public Response importDomain(RealmRepresentation rep) - { - RealmModel realm = createRealm(rep); - UriBuilder builder = uriInfo.getRequestUriBuilder().path(realm.getId()); - return Response.created(builder.build()) - .entity(RealmResource.realmRep(realm, uriInfo)) - .type(MediaType.APPLICATION_JSON_TYPE).build(); - } - - protected RealmModel createRealm(RealmRepresentation rep) - { - User realmCreator = authenticationManager.authenticateToken(defaultRealm, headers); - Role creatorRole = defaultRealm.getIdm().getRole(RegistrationService.REALM_CREATOR_ROLE); - if (!defaultRealm.getIdm().hasRole(realmCreator, creatorRole)) - { - logger.warn("not a realm creator"); - throw new NotAuthorizedException("Bearer"); - } - verifyRealmRepresentation(rep); - - RealmModel realm = adapter.create(rep.getRealm()); - realm.addRealmAdmin(realmCreator); - KeyPair keyPair = null; - try - { - keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); - } - catch (NoSuchAlgorithmException e) - { - throw new RuntimeException(e); - } - realm.setPrivateKey(keyPair.getPrivate()); - realm.setPublicKey(keyPair.getPublic()); - realm.setName(rep.getRealm()); - realm.setEnabled(rep.isEnabled()); - realm.setTokenLifespan(rep.getTokenLifespan()); - realm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); - realm.setSslNotRequired(rep.isSslNotRequired()); - realm.setCookieLoginAllowed(rep.isCookieLoginAllowed()); - realm.updateRealm(); - - - Map userMap = new HashMap(); - - for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials()) - { - RequiredCredentialModel credential = new RequiredCredentialModel(); - credential.setType(requiredCred.getType()); - credential.setInput(requiredCred.isInput()); - credential.setSecret(requiredCred.isSecret()); - realm.addRequiredCredential(credential); - } - - for (UserRepresentation userRep : rep.getUsers()) - { - User user = new SimpleUser(userRep.getUsername()); - user.setEnabled(userRep.isEnabled()); - if (userRep.getAttributes() != null) - { - for (Map.Entry entry : userRep.getAttributes().entrySet()) - { - user.setAttribute(new Attribute(entry.getKey(), entry.getValue())); - } - } - realm.getIdm().add(user); - if (userRep.getCredentials() != null) - { - for (UserRepresentation.Credential cred : userRep.getCredentials()) - { - UserCredentialModel credential = new UserCredentialModel(); - credential.setType(cred.getType()); - credential.setValue(cred.getValue()); - realm.updateCredential(user, credential); - } - } - userMap.put(user.getLoginName(), user); - } - - Map roles = new HashMap(); - - if (rep.getRoles() != null) - { - for (String roleString : rep.getRoles()) - { - SimpleRole role = new SimpleRole(roleString.trim()); - realm.getIdm().add(role); - roles.put(role.getName(), role); - } - } - - if (rep.getRoleMappings() != null) - { - for (RoleMappingRepresentation mapping : rep.getRoleMappings()) - { - User user = userMap.get(mapping.getUsername()); - for (String roleString : mapping.getRoles()) - { - Role role = roles.get(roleString.trim()); - if (role == null) - { - role = new SimpleRole(roleString.trim()); - realm.getIdm().add(role); - roles.put(role.getName(), role); - } - realm.getIdm().grantRole(user, role); - } - } - } - - if (rep.getScopeMappings() != null) - { - for (ScopeMappingRepresentation scope : rep.getScopeMappings()) - { - for (String roleString : scope.getRoles()) - { - Role role = roles.get(roleString.trim()); - if (role == null) - { - role = new SimpleRole(roleString.trim()); - realm.getIdm().add(role); - roles.put(role.getName(), role); - } - User user = userMap.get(scope.getUsername()); - realm.addScope(user, role.getName()); - } - - } - } - - if (!roles.containsKey("*")) - { - SimpleRole wildcard = new SimpleRole("*"); - realm.getIdm().add(wildcard); - roles.put("*", wildcard); - } - - if (rep.getResources() != null) - { - createResources(rep, realm, userMap); - } - return realm; - } - - protected void createResources(RealmRepresentation rep, RealmModel realm, Map userMap) - { - for (ResourceRepresentation resourceRep : rep.getResources()) - { - ResourceModel resource = realm.addResource(resourceRep.getName()); - resource.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired()); - resource.updateResource(); - Map roles = new HashMap(); - if (resourceRep.getRoles() != null) - { - for (String roleString : rep.getRoles()) - { - SimpleRole role = new SimpleRole(roleString.trim()); - resource.getIdm().add(role); - roles.put(role.getName(), role); - } - } - if (resourceRep.getRoleMappings() != null) - { - for (RoleMappingRepresentation mapping : resourceRep.getRoleMappings()) - { - User user = userMap.get(mapping.getUsername()); - for (String roleString : mapping.getRoles()) - { - Role role = roles.get(roleString.trim()); - if (role == null) - { - role = new SimpleRole(roleString.trim()); - resource.getIdm().add(role); - roles.put(role.getName(), role); - } - resource.getIdm().grantRole(user, role); - } - } - } - if (resourceRep.getScopeMappings() != null) - { - for (ScopeMappingRepresentation mapping : resourceRep.getScopeMappings()) - { - User user = userMap.get(mapping.getUsername()); - for (String roleString : mapping.getRoles()) - { - Role role = roles.get(roleString.trim()); - if (role == null) - { - role = new SimpleRole(roleString.trim()); - resource.getIdm().add(role); - roles.put(role.getName(), role); - } - resource.addScope(user, role.getName()); - } - } - } - if (!roles.containsKey("*")) - { - SimpleRole wildcard = new SimpleRole("*"); - resource.getIdm().add(wildcard); - roles.put("*", wildcard); - } - - } - } - - protected void verifyRealmRepresentation(RealmRepresentation rep) - { - if (rep.getRequiredCredentials() == null) - { - throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST) - .entity("Realm credential requirements not defined").type("text/plain").build()); - - } - - HashMap userReps = new HashMap(); - for (UserRepresentation userRep : rep.getUsers()) userReps.put(userRep.getUsername(), userRep); - - // override enabled to false if user does not have at least all of browser or client credentials - for (UserRepresentation userRep : rep.getUsers()) - { - if (userRep.getCredentials() == null) - { - userRep.setEnabled(false); - } - else - { - boolean hasBrowserCredentials = true; - for (RequiredCredentialRepresentation credential : rep.getRequiredCredentials()) - { - boolean hasCredential = false; - for (UserRepresentation.Credential cred : userRep.getCredentials()) - { - if (cred.getType().equals(credential.getType())) - { - hasCredential = true; - break; - } - } - if (!hasCredential) - { - hasBrowserCredentials = false; - break; - } - } - if (!hasBrowserCredentials) - { - userRep.setEnabled(false); - } - - } - } - - if (rep.getResources() != null) - { - // check mappings - for (ResourceRepresentation resourceRep : rep.getResources()) - { - if (resourceRep.getRoleMappings() != null) - { - for (RoleMappingRepresentation mapping : resourceRep.getRoleMappings()) - { - if (!userReps.containsKey(mapping.getUsername())) - { - throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST) - .entity("No users declared for role mapping").type("text/plain").build()); - - } - } - } - } - } - } - -} diff --git a/services/src/main/java/org/keycloak/services/resources/RealmResource.java b/services/src/main/java/org/keycloak/services/resources/RealmResource.java deleted file mode 100755 index a2ec89a3ca..0000000000 --- a/services/src/main/java/org/keycloak/services/resources/RealmResource.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.keycloak.services.resources; - -import org.keycloak.services.models.RealmManager; -import org.jboss.resteasy.logging.Logger; -import org.keycloak.representations.idm.PublishedRealmRepresentation; -import org.keycloak.services.models.RealmModel; - -import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -@Path("/realms") -public class RealmResource -{ - protected Logger logger = Logger.getLogger(RealmResource.class); - protected RealmManager adapter; - @Context - protected UriInfo uriInfo; - - public RealmResource(RealmManager adapter) - { - this.adapter = adapter; - } - - @GET - @Path("{realm}") - @Produces("application/json") - public PublishedRealmRepresentation getRealm(@PathParam("realm") String id) - { - RealmModel realm = adapter.getRealm(id); - if (realm == null) - { - logger.debug("realm not found"); - throw new NotFoundException(); - } - return realmRep(realm, uriInfo); - } - - @GET - @Path("{realm}.html") - @Produces("text/html") - public String getRealmHtml(@PathParam("realm") String id) - { - RealmModel realm = adapter.getRealm(id); - if (realm == null) - { - logger.debug("realm not found"); - throw new NotFoundException(); - } - return realmHtml(realm); - } - - private String realmHtml(RealmModel realm) - { - StringBuffer html = new StringBuffer(); - - UriBuilder auth = uriInfo.getBaseUriBuilder(); - auth.path(TokenService.class) - .path(TokenService.class, "requestAccessCode"); - String authUri = auth.build(realm.getId()).toString(); - - UriBuilder code = uriInfo.getBaseUriBuilder(); - code.path(TokenService.class).path(TokenService.class, "accessRequest"); - String codeUri = code.build(realm.getId()).toString(); - - UriBuilder grant = uriInfo.getBaseUriBuilder(); - grant.path(TokenService.class).path(TokenService.class, "accessTokenGrant"); - String grantUrl = grant.build(realm.getId()).toString(); - - UriBuilder idGrant = uriInfo.getBaseUriBuilder(); - grant.path(TokenService.class).path(TokenService.class, "identityTokenGrant"); - String idGrantUrl = idGrant.build(realm.getId()).toString(); - - html.append("

Realm: ").append(realm.getName()).append("

"); - html.append("

auth: ").append(authUri).append("

"); - html.append("

code: ").append(codeUri).append("

"); - html.append("

grant: ").append(grantUrl).append("

"); - html.append("

identity grant: ").append(idGrantUrl).append("

"); - html.append("

public key: ").append(realm.getPublicKeyPem()).append("

"); - html.append(""); - - return html.toString(); - } - - - public static PublishedRealmRepresentation realmRep(RealmModel realm, UriInfo uriInfo) - { - PublishedRealmRepresentation rep = new PublishedRealmRepresentation(); - rep.setRealm(realm.getName()); - rep.setSelf(uriInfo.getRequestUri().toString()); - rep.setPublicKeyPem(realm.getPublicKeyPem()); - - UriBuilder auth = uriInfo.getBaseUriBuilder(); - auth.path(TokenService.class) - .path(TokenService.class, "requestAccessCode"); - rep.setAuthorizationUrl(auth.build(realm.getId()).toString()); - - UriBuilder code = uriInfo.getBaseUriBuilder(); - code.path(TokenService.class).path(TokenService.class, "accessRequest"); - rep.setCodeUrl(code.build(realm.getId()).toString()); - - UriBuilder grant = uriInfo.getBaseUriBuilder(); - grant.path(TokenService.class).path(TokenService.class, "accessTokenGrant"); - String grantUrl = grant.build(realm.getId()).toString(); - rep.setGrantUrl(grantUrl); - - UriBuilder idGrant = uriInfo.getBaseUriBuilder(); - grant.path(TokenService.class).path(TokenService.class, "identityTokenGrant"); - String idGrantUrl = idGrant.build(realm.getId()).toString(); - rep.setIdentityGrantUrl(idGrantUrl); - return rep; - } -} diff --git a/services/src/main/java/org/keycloak/services/resources/RealmSubResource.java b/services/src/main/java/org/keycloak/services/resources/RealmSubResource.java new file mode 100755 index 0000000000..9d76a21a60 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/RealmSubResource.java @@ -0,0 +1,105 @@ +package org.keycloak.services.resources; + +import org.jboss.resteasy.logging.Logger; +import org.keycloak.representations.idm.PublishedRealmRepresentation; +import org.keycloak.services.models.RealmModel; +import org.picketlink.idm.IdentitySession; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class RealmSubResource { + protected static final Logger logger = Logger.getLogger(RealmSubResource.class); + + @Context + protected UriInfo uriInfo; + + @Context + protected IdentitySession IdentitySession; + + protected RealmModel realm; + + public RealmSubResource(RealmModel realm) { + this.realm = realm; + } + + @GET + @Path("json") + @Produces("application/json") + public PublishedRealmRepresentation getRealm(@PathParam("realm") String id) { + return realmRep(realm, uriInfo); + } + + @GET + @Path("html") + @Produces("text/html") + public String getRealmHtml(@PathParam("realm") String id) { + StringBuffer html = new StringBuffer(); + + UriBuilder auth = uriInfo.getBaseUriBuilder(); + auth.path(TokenService.class) + .path(TokenService.class, "requestAccessCode"); + String authUri = auth.build(realm.getId()).toString(); + + UriBuilder code = uriInfo.getBaseUriBuilder(); + code.path(TokenService.class).path(TokenService.class, "accessRequest"); + String codeUri = code.build(realm.getId()).toString(); + + UriBuilder grant = uriInfo.getBaseUriBuilder(); + grant.path(TokenService.class).path(TokenService.class, "accessTokenGrant"); + String grantUrl = grant.build(realm.getId()).toString(); + + UriBuilder idGrant = uriInfo.getBaseUriBuilder(); + grant.path(TokenService.class).path(TokenService.class, "identityTokenGrant"); + String idGrantUrl = idGrant.build(realm.getId()).toString(); + + html.append("

Realm: ").append(realm.getName()).append("

"); + html.append("

auth: ").append(authUri).append("

"); + html.append("

code: ").append(codeUri).append("

"); + html.append("

grant: ").append(grantUrl).append("

"); + html.append("

identity grant: ").append(idGrantUrl).append("

"); + html.append("

public key: ").append(realm.getPublicKeyPem()).append("

"); + html.append(""); + + return html.toString(); + } + + + public static PublishedRealmRepresentation realmRep(RealmModel realm, UriInfo uriInfo) { + PublishedRealmRepresentation rep = new PublishedRealmRepresentation(); + rep.setRealm(realm.getName()); + rep.setSelf(uriInfo.getRequestUri().toString()); + rep.setPublicKeyPem(realm.getPublicKeyPem()); + + UriBuilder auth = uriInfo.getBaseUriBuilder(); + auth.path(RealmsResource.class).path(RealmsResource.class, "getTokenService") + .path(TokenService.class, "requestAccessCode"); + rep.setAuthorizationUrl(auth.build(realm.getId()).toString()); + + UriBuilder code = uriInfo.getBaseUriBuilder(); + code.path(RealmsResource.class).path(RealmsResource.class, "getTokenService").path(TokenService.class, "accessRequest"); + rep.setCodeUrl(code.build(realm.getId()).toString()); + + UriBuilder grant = uriInfo.getBaseUriBuilder(); + grant.path(RealmsResource.class).path(RealmsResource.class, "getTokenService").path(TokenService.class, "accessTokenGrant"); + String grantUrl = grant.build(realm.getId()).toString(); + rep.setGrantUrl(grantUrl); + + UriBuilder idGrant = uriInfo.getBaseUriBuilder(); + grant.path(RealmsResource.class).path(RealmsResource.class, "getTokenService").path(TokenService.class, "identityTokenGrant"); + String idGrantUrl = idGrant.build(realm.getId()).toString(); + rep.setIdentityGrantUrl(idGrantUrl); + return rep; + } + + +} diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java new file mode 100755 index 0000000000..b749569102 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java @@ -0,0 +1,323 @@ +package org.keycloak.services.resources; + +import org.jboss.resteasy.logging.Logger; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.RequiredCredentialRepresentation; +import org.keycloak.representations.idm.ResourceRepresentation; +import org.keycloak.representations.idm.RoleMappingRepresentation; +import org.keycloak.representations.idm.ScopeMappingRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.services.managers.AccessCodeEntry; +import org.keycloak.services.managers.AuthenticationManager; +import org.keycloak.services.models.RealmManager; +import org.keycloak.services.models.RealmModel; +import org.keycloak.services.models.RequiredCredentialModel; +import org.keycloak.services.models.ResourceModel; +import org.keycloak.services.models.UserCredentialModel; +import org.picketlink.idm.IdentitySession; +import org.picketlink.idm.model.Attribute; +import org.picketlink.idm.model.Realm; +import org.picketlink.idm.model.Role; +import org.picketlink.idm.model.SimpleRole; +import org.picketlink.idm.model.SimpleUser; +import org.picketlink.idm.model.User; + +import javax.ws.rs.Consumes; +import javax.ws.rs.NotAuthorizedException; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.container.ResourceContext; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@Path("/realms") +public class RealmsResource { + protected static Logger logger = Logger.getLogger(RealmsResource.class); + + @Context + protected UriInfo uriInfo; + + @Context + protected HttpHeaders headers; + + @Context + protected + IdentitySession IdentitySession; + + @Context + ResourceContext resourceContext; + + protected Map accessCodes = new ConcurrentHashMap(); + + @Path("{realm}/tokens") + public TokenService getTokenService(@PathParam("realm") String id) { + RealmManager realmManager = new RealmManager(IdentitySession); + RealmModel realm = realmManager.getRealm(id); + if (realm == null) { + logger.debug("realm not found"); + throw new NotFoundException(); + } + TokenService tokenService = new TokenService(realm, accessCodes); + resourceContext.initResource(tokenService); + return tokenService; + + } + + + @Path("{realm}") + public RealmSubResource getRealmResource(@PathParam("realm") String id) { + RealmManager realmManager = new RealmManager(IdentitySession); + RealmModel realm = realmManager.getRealm(id); + if (realm == null) { + logger.debug("realm not found"); + throw new NotFoundException(); + } + RealmSubResource realmResource = new RealmSubResource(realm); + resourceContext.initResource(realmResource); + return realmResource; + + } + + + @POST + @Consumes("application/json") + public Response importRealm(RealmRepresentation rep) { + IdentitySession.getTransaction().begin(); + RealmModel realm; + try { + realm = createRealm(rep); + IdentitySession.getTransaction().commit(); + } catch (RuntimeException re) { + IdentitySession.getTransaction().rollback(); + throw re; + } + UriBuilder builder = uriInfo.getRequestUriBuilder().path(realm.getId()); + return Response.created(builder.build()) + .entity(RealmSubResource.realmRep(realm, uriInfo)) + .type(MediaType.APPLICATION_JSON_TYPE).build(); + } + + protected RealmModel createRealm(RealmRepresentation rep) { + RealmManager realmManager = new RealmManager(IdentitySession); + RealmModel defaultRealm = realmManager.getRealm(Realm.DEFAULT_REALM); + User realmCreator = new AuthenticationManager().authenticateToken(defaultRealm, headers); + Role creatorRole = defaultRealm.getIdm().getRole(RegistrationService.REALM_CREATOR_ROLE); + if (!defaultRealm.getIdm().hasRole(realmCreator, creatorRole)) { + logger.warn("not a realm creator"); + throw new NotAuthorizedException("Bearer"); + } + verifyRealmRepresentation(rep); + + RealmModel realm = realmManager.createRealm(rep.getRealm()); + realmManager.generateRealmKeys(realm); + realm.addRealmAdmin(realmCreator); + realm.setName(rep.getRealm()); + realm.setEnabled(rep.isEnabled()); + realm.setTokenLifespan(rep.getTokenLifespan()); + realm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); + realm.setSslNotRequired(rep.isSslNotRequired()); + realm.setCookieLoginAllowed(rep.isCookieLoginAllowed()); + realm.updateRealm(); + + + Map userMap = new HashMap(); + + for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials()) { + RequiredCredentialModel credential = new RequiredCredentialModel(); + credential.setType(requiredCred.getType()); + credential.setInput(requiredCred.isInput()); + credential.setSecret(requiredCred.isSecret()); + realm.addRequiredCredential(credential); + } + + for (UserRepresentation userRep : rep.getUsers()) { + User user = new SimpleUser(userRep.getUsername()); + user.setEnabled(userRep.isEnabled()); + if (userRep.getAttributes() != null) { + for (Map.Entry entry : userRep.getAttributes().entrySet()) { + user.setAttribute(new Attribute(entry.getKey(), entry.getValue())); + } + } + realm.getIdm().add(user); + if (userRep.getCredentials() != null) { + for (UserRepresentation.Credential cred : userRep.getCredentials()) { + UserCredentialModel credential = new UserCredentialModel(); + credential.setType(cred.getType()); + credential.setValue(cred.getValue()); + realm.updateCredential(user, credential); + } + } + userMap.put(user.getLoginName(), user); + } + + Map roles = new HashMap(); + + if (rep.getRoles() != null) { + for (String roleString : rep.getRoles()) { + SimpleRole role = new SimpleRole(roleString.trim()); + realm.getIdm().add(role); + roles.put(role.getName(), role); + } + } + + if (rep.getRoleMappings() != null) { + for (RoleMappingRepresentation mapping : rep.getRoleMappings()) { + User user = userMap.get(mapping.getUsername()); + for (String roleString : mapping.getRoles()) { + Role role = roles.get(roleString.trim()); + if (role == null) { + role = new SimpleRole(roleString.trim()); + realm.getIdm().add(role); + roles.put(role.getName(), role); + } + realm.getIdm().grantRole(user, role); + } + } + } + + if (rep.getScopeMappings() != null) { + for (ScopeMappingRepresentation scope : rep.getScopeMappings()) { + for (String roleString : scope.getRoles()) { + Role role = roles.get(roleString.trim()); + if (role == null) { + role = new SimpleRole(roleString.trim()); + realm.getIdm().add(role); + roles.put(role.getName(), role); + } + User user = userMap.get(scope.getUsername()); + realm.addScope(user, role.getName()); + } + + } + } + + if (!roles.containsKey("*")) { + SimpleRole wildcard = new SimpleRole("*"); + realm.getIdm().add(wildcard); + roles.put("*", wildcard); + } + + if (rep.getResources() != null) { + createResources(rep, realm, userMap); + } + return realm; + } + + protected void createResources(RealmRepresentation rep, RealmModel realm, Map userMap) { + for (ResourceRepresentation resourceRep : rep.getResources()) { + ResourceModel resource = realm.addResource(resourceRep.getName()); + resource.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired()); + resource.updateResource(); + Map roles = new HashMap(); + if (resourceRep.getRoles() != null) { + for (String roleString : resourceRep.getRoles()) { + SimpleRole role = new SimpleRole(roleString.trim()); + resource.getIdm().add(role); + roles.put(role.getName(), role); + } + } + if (resourceRep.getRoleMappings() != null) { + for (RoleMappingRepresentation mapping : resourceRep.getRoleMappings()) { + User user = userMap.get(mapping.getUsername()); + for (String roleString : mapping.getRoles()) { + Role role = roles.get(roleString.trim()); + if (role == null) { + role = new SimpleRole(roleString.trim()); + resource.getIdm().add(role); + roles.put(role.getName(), role); + } + Role role1 = resource.getIdm().getRole(role.getName()); + realm.getIdm().grantRole(user, role1); + } + } + } + if (resourceRep.getScopeMappings() != null) { + for (ScopeMappingRepresentation mapping : resourceRep.getScopeMappings()) { + User user = userMap.get(mapping.getUsername()); + for (String roleString : mapping.getRoles()) { + Role role = roles.get(roleString.trim()); + if (role == null) { + role = new SimpleRole(roleString.trim()); + resource.getIdm().add(role); + roles.put(role.getName(), role); + } + resource.addScope(user, role.getName()); + } + } + } + if (!roles.containsKey("*")) { + SimpleRole wildcard = new SimpleRole("*"); + resource.getIdm().add(wildcard); + roles.put("*", wildcard); + } + + } + } + + protected void verifyRealmRepresentation(RealmRepresentation rep) { + if (rep.getRequiredCredentials() == null) { + throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST) + .entity("Realm credential requirements not defined").type("text/plain").build()); + + } + + HashMap userReps = new HashMap(); + for (UserRepresentation userRep : rep.getUsers()) userReps.put(userRep.getUsername(), userRep); + + // override enabled to false if user does not have at least all of browser or client credentials + for (UserRepresentation userRep : rep.getUsers()) { + if (userRep.getCredentials() == null) { + userRep.setEnabled(false); + } else { + boolean hasBrowserCredentials = true; + for (RequiredCredentialRepresentation credential : rep.getRequiredCredentials()) { + boolean hasCredential = false; + for (UserRepresentation.Credential cred : userRep.getCredentials()) { + if (cred.getType().equals(credential.getType())) { + hasCredential = true; + break; + } + } + if (!hasCredential) { + hasBrowserCredentials = false; + break; + } + } + if (!hasBrowserCredentials) { + userRep.setEnabled(false); + } + + } + } + + if (rep.getResources() != null) { + // check mappings + for (ResourceRepresentation resourceRep : rep.getResources()) { + if (resourceRep.getRoleMappings() != null) { + for (RoleMappingRepresentation mapping : resourceRep.getRoleMappings()) { + if (!userReps.containsKey(mapping.getUsername())) { + throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST) + .entity("No users declared for role mapping").type("text/plain").build()); + + } + } + } + } + } + } + +} 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 c4c9b05015..55c576c3f8 100755 --- a/services/src/main/java/org/keycloak/services/resources/RegistrationService.java +++ b/services/src/main/java/org/keycloak/services/resources/RegistrationService.java @@ -1,15 +1,17 @@ package org.keycloak.services.resources; +import org.jboss.resteasy.logging.Logger; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.models.RealmManager; import org.keycloak.services.models.RealmModel; import org.keycloak.services.models.UserCredentialModel; -import org.picketlink.idm.model.Realm; +import org.picketlink.idm.IdentitySession; import org.picketlink.idm.model.Role; 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; @@ -23,47 +25,53 @@ import java.net.URI; * @version $Revision: 1 $ */ @Path("/registrations") -public class RegistrationService -{ - public static final String REALM_CREATOR_ROLE = "realm-creator"; - protected RealmManager adapter; - protected RealmModel defaultRealm; +public class RegistrationService { + protected static final Logger logger = Logger.getLogger(RegistrationService.class); + public static final String REALM_CREATOR_ROLE = "realm-creator"; - @Context - protected UriInfo uriInfo; + @Context + protected UriInfo uriInfo; - public RegistrationService(RealmManager adapter) - { - this.adapter = adapter; - defaultRealm = adapter.getRealm(Realm.DEFAULT_REALM); - } + @Context + protected IdentitySession identitySession; + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Response register(UserRepresentation newUser) { + identitySession.getTransaction().begin(); + try { + 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(); + } - - @POST - @Consumes(MediaType.APPLICATION_JSON) - public Response register(UserRepresentation newUser) - { - User user = defaultRealm.getIdm().getUser(newUser.getUsername()); - if (user != null) - { - return Response.status(400).type("text/plain").entity("user exists").build(); - } - - user = new SimpleUser(newUser.getUsername()); - defaultRealm.getIdm().add(user); - for (UserRepresentation.Credential cred : newUser.getCredentials()) - { - UserCredentialModel credModel = new UserCredentialModel(); - credModel.setType(cred.getType()); - credModel.setValue(cred.getValue()); - defaultRealm.updateCredential(user, credModel); - } - Role realmCreator = defaultRealm.getIdm().getRole(REALM_CREATOR_ROLE); - defaultRealm.getIdm().grantRole(user, realmCreator); - URI uri = uriInfo.getBaseUriBuilder().path(RealmFactory.class).path(user.getLoginName()).build(); - return Response.created(uri).build(); - } + user = new SimpleUser(newUser.getUsername()); + defaultRealm.getIdm().add(user); + for (UserRepresentation.Credential cred : newUser.getCredentials()) { + UserCredentialModel credModel = new UserCredentialModel(); + credModel.setType(cred.getType()); + credModel.setValue(cred.getValue()); + defaultRealm.updateCredential(user, credModel); + } + Role realmCreator = defaultRealm.getIdm().getRole(REALM_CREATOR_ROLE); + defaultRealm.getIdm().grantRole(user, realmCreator); + 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(); + throw e; + } + } } diff --git a/services/src/main/java/org/keycloak/services/resources/SkeletonKeyApplication.java b/services/src/main/java/org/keycloak/services/resources/SkeletonKeyApplication.java deleted file mode 100755 index 6517a870d8..0000000000 --- a/services/src/main/java/org/keycloak/services/resources/SkeletonKeyApplication.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.keycloak.services.resources; - -import org.infinispan.Cache; -import org.infinispan.manager.DefaultCacheManager; -import org.keycloak.SkeletonKeyContextResolver; - -import javax.ws.rs.core.Application; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashSet; -import java.util.Set; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class SkeletonKeyApplication extends Application -{ - protected Set singletons = new HashSet(); - protected Set> classes = new HashSet>(); - - public SkeletonKeyApplication() - { - Cache cache = getCache(); - singletons.add(new TokenService(null)); - singletons.add(new RealmFactory(null)); - singletons.add(new RealmResource(null)); - classes.add(SkeletonKeyContextResolver.class); - } - - @Override - public Set> getClasses() - { - return classes; - } - - @Override - public Set getSingletons() - { - return singletons; - } - - protected Cache getCache() - { - try - { - InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("skeleton-key.xml"); - return new DefaultCacheManager(is).getCache("skeleton-key"); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - } -} diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java index ebdf2a9520..d77339daef 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -9,21 +9,21 @@ import org.jboss.resteasy.logging.Logger; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.SkeletonKeyScope; import org.keycloak.representations.SkeletonKeyToken; +import org.keycloak.services.managers.AccessCodeEntry; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.TokenManager; import org.keycloak.services.models.RealmManager; import org.keycloak.services.models.RealmModel; import org.keycloak.services.models.RequiredCredentialModel; import org.keycloak.services.models.ResourceModel; +import org.picketlink.idm.IdentitySession; import org.picketlink.idm.model.User; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.NotAuthorizedException; -import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.Path; -import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; @@ -41,555 +41,400 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicLong; /** * @author Bill Burke * @version $Revision: 1 $ */ -@Path("/realms") -public class TokenService -{ - public static class AccessCode - { - protected String id = UUID.randomUUID().toString() + System.currentTimeMillis(); - protected long expiration; - protected SkeletonKeyToken token; - protected User client; - - 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 User getClient() - { - return client; - } - - public void setClient(User client) - { - this.client = client; - } - } - - protected RealmManager adapter; - protected TokenManager tokenManager; - protected AuthenticationManager authManager; - protected Logger logger = Logger.getLogger(TokenService.class); - protected Map accessCodeMap = new HashMap(); - @Context - protected UriInfo uriInfo; - @Context - protected Providers providers; - @Context - protected SecurityContext securityContext; - @Context - protected HttpHeaders headers; - - private static AtomicLong counter = new AtomicLong(1); - private static String generateId() - { - return counter.getAndIncrement() + "." + UUID.randomUUID().toString(); - } - - public TokenService(RealmManager adapter) - { - this.adapter = adapter; - this.tokenManager = new TokenManager(adapter); - this.authManager = new AuthenticationManager(adapter); - } - - @Path("{realm}/grants/identity-token") - @POST - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces(MediaType.APPLICATION_JSON) - public Response identityTokenGrant(@PathParam("realm") String realmId, MultivaluedMap form) - { - String username = form.getFirst(AuthenticationManager.FORM_USERNAME); - if (username == null) - { - throw new NotAuthorizedException("No user"); - } - RealmModel realm = adapter.getRealm(realmId); - if (realm == null) - { - throw new NotFoundException("Realm not found"); - } - if (!realm.isEnabled()) - { - throw new NotAuthorizedException("Disabled realm"); - } - User user = realm.getIdm().getUser(username); - if (user == null) - { - throw new NotAuthorizedException("No user"); - } - if (!user.isEnabled()) - { - throw new NotAuthorizedException("Disabled user."); - } - SkeletonKeyToken token = tokenManager.createIdentityToken(realm, username); - String encoded = tokenManager.encodeToken(realm, token); - AccessTokenResponse res = accessTokenResponse(token, encoded); - return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build(); - } - - @Path("{realm}/grants/access") - @POST - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - @Produces(MediaType.APPLICATION_JSON) - public Response accessTokenGrant(@PathParam("realm") String realmId, MultivaluedMap form) - { - String username = form.getFirst(AuthenticationManager.FORM_USERNAME); - if (username == null) - { - throw new NotAuthorizedException("No user"); - } - RealmModel realm = adapter.getRealm(realmId); - if (realm == null) - { - throw new NotFoundException("Realm not found"); - } - if (!realm.isEnabled()) - { - throw new NotAuthorizedException("Disabled realm"); - } - User user = realm.getIdm().getUser(username); - if (user == null) - { - throw new NotAuthorizedException("No user"); - } - if (!user.isEnabled()) - { - throw new NotAuthorizedException("Disabled user."); - } - if (authManager.authenticateForm(realm, user, form)) - { - throw new NotAuthorizedException("Auth failed"); - } - SkeletonKeyToken token = tokenManager.createAccessToken(realm, user); - String encoded = tokenManager.encodeToken(realm, token); - AccessTokenResponse res = accessTokenResponse(token, encoded); - return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build(); - } - - @Path("{realm}/auth/request/login") - @POST - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - public Response login(@PathParam("realm") String realmId, - MultivaluedMap formData) - { - String clientId = formData.getFirst("client_id"); - String scopeParam = formData.getFirst("scope"); - String state = formData.getFirst("state"); - String redirect = formData.getFirst("redirect_uri"); - - RealmModel realm = adapter.getRealm(realmId); - if (realm == null) - { - throw new NotFoundException("Realm not found"); - } - if (!realm.isEnabled()) - { - return Response.ok("Realm not enabled").type("text/html").build(); - } - User client = realm.getIdm().getUser(clientId); - if (client == null) - { - throw new NotAuthorizedException("No client"); - } - if (!client.isEnabled()) - { - return Response.ok("Requester not enabled").type("text/html").build(); - } - String username = formData.getFirst("username"); - User user = realm.getIdm().getUser(username); - if (user == null) - { - logger.debug("user not found"); - return loginForm("Not valid user", redirect, clientId, scopeParam, state, realm, client); - } - if (!user.isEnabled()) - { - return Response.ok("Your account is not enabled").type("text/html").build(); - - } - boolean authenticated = authManager.authenticateForm(realm, user, formData); - if (!authenticated) return loginForm("Unable to authenticate, try again", redirect, clientId, scopeParam, state, realm, client); - - SkeletonKeyToken token = null; - if (scopeParam != null) token = tokenManager.createScopedToken(scopeParam, realm, client, user); - else token = tokenManager.createLoginToken(realm, client, user); - - AccessCode code = new AccessCode(); - code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan()); - code.setToken(token); - code.setClient(client); - synchronized (accessCodeMap) - { - accessCodeMap.put(code.getId(), code); - } - String accessCode = null; - try - { - accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realm.getPrivateKey()); - } - catch (UnsupportedEncodingException e) - { - throw new RuntimeException(e); - } - UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", accessCode); - if (state != null) redirectUri.queryParam("state", state); - return Response.status(302).location(redirectUri.build()).build(); - } - - @Path("{realm}/access/codes") - @POST - @Produces("application/json") - public Response accessRequest(@PathParam("realm") String realmId, - MultivaluedMap formData) - { - RealmModel realm = adapter.getRealm(realmId); - if (realm == null) - { - throw new NotFoundException("Realm not found"); - } - if (!realm.isEnabled()) - { - throw new NotAuthorizedException("Realm not enabled"); - } - - String code = formData.getFirst("code"); - if (code == null) - { - logger.debug("code not specified"); - Map error = new HashMap(); - error.put("error", "invalid_request"); - error.put("error_description", "code not specified"); - return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); - - } - String client_id = formData.getFirst("client_id"); - if (client_id == null) - { - logger.debug("client_id not specified"); - Map error = new HashMap(); - error.put("error", "invalid_request"); - error.put("error_description", "client_id not specified"); - return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); - } - User client = realm.getIdm().getUser(client_id); - if (client == null) - { - logger.debug("Could not find user"); - Map error = new HashMap(); - error.put("error", "invalid_client"); - error.put("error_description", "Could not find user"); - return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); - } - - if (!client.isEnabled()) - { - logger.debug("user is not enabled"); - Map error = new HashMap(); - error.put("error", "invalid_client"); - error.put("error_description", "User is not enabled"); - return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); - } - - boolean authenticated = authManager.authenticateForm(realm, client, formData); - if (!authenticated) - { - Map error = new HashMap(); - error.put("error", "unauthorized_client"); - return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); - } +public class TokenService { + protected static final Logger logger = Logger.getLogger(TokenService.class); + protected Map accessCodeMap; - JWSInput input = new JWSInput(code, providers); - boolean verifiedCode = false; - try - { - verifiedCode = RSAProvider.verify(input, realm.getPublicKey()); - } - catch (Exception ignored) - { - logger.debug("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"); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); - } - String key = input.readContent(String.class); - AccessCode accessCode = null; - synchronized (accessCodeMap) - { - accessCode = accessCodeMap.remove(key); - } - if (accessCode == null) - { - Map res = new HashMap(); - res.put("error", "invalid_grant"); - res.put("error_description", "Code not found"); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); - } - if (accessCode.isExpired()) - { - Map res = new HashMap(); - res.put("error", "invalid_grant"); - res.put("error_description", "Code is expired"); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); - } - if (!accessCode.getToken().isActive()) - { - Map res = new HashMap(); - res.put("error", "invalid_grant"); - res.put("error_description", "Token expired"); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); - } - if (!client.getId().equals(accessCode.getClient().getId())) - { - Map res = new HashMap(); - res.put("error", "invalid_grant"); - res.put("error_description", "Auth error"); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); - } - AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken()); - return Response.ok(res).build(); + @Context + protected UriInfo uriInfo; + @Context + protected Providers providers; + @Context + protected SecurityContext securityContext; + @Context + protected HttpHeaders headers; + @Context + protected + IdentitySession IdentitySession; - } + protected RealmModel realm; + protected TokenManager tokenManager = new TokenManager(); + protected AuthenticationManager authManager = new AuthenticationManager(); - protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) - { - byte[] tokenBytes = null; - try - { - tokenBytes = JsonSerialization.toByteArray(token, false); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - String encodedToken = new JWSBuilder() - .content(tokenBytes) - .rsa256(privateKey); + public TokenService(RealmModel realm, Map accessCodeMap) { + this.realm = realm; + this.accessCodeMap = accessCodeMap; + } - return accessTokenResponse(token, encodedToken); - } + @Path("grants/identity-token") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_JSON) + public Response identityTokenGrant(MultivaluedMap form) { + String username = form.getFirst(AuthenticationManager.FORM_USERNAME); + if (username == null) { + throw new NotAuthorizedException("No user"); + } + if (!realm.isEnabled()) { + throw new NotAuthorizedException("Disabled realm"); + } + User user = realm.getIdm().getUser(username); + if (user == null) { + throw new NotAuthorizedException("No user"); + } + if (!user.isEnabled()) { + throw new NotAuthorizedException("Disabled user."); + } + if (!authManager.authenticateForm(realm, user, form)) { + throw new NotAuthorizedException("FORM"); + } + tokenManager = new TokenManager(); + SkeletonKeyToken token = tokenManager.createIdentityToken(realm, username); + String encoded = tokenManager.encodeToken(realm, token); + AccessTokenResponse res = accessTokenResponse(token, encoded); + return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build(); + } - protected AccessTokenResponse accessTokenResponse(SkeletonKeyToken token, String encodedToken) - { - 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; - } + @Path("grants/access") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_JSON) + public Response accessTokenGrant(MultivaluedMap form) { + String username = form.getFirst(AuthenticationManager.FORM_USERNAME); + if (username == null) { + throw new NotAuthorizedException("No user"); + } + if (!realm.isEnabled()) { + throw new NotAuthorizedException("Disabled realm"); + } + User user = realm.getIdm().getUser(username); + if (user == null) { + throw new NotAuthorizedException("No user"); + } + if (!user.isEnabled()) { + throw new NotAuthorizedException("Disabled user."); + } + if (authManager.authenticateForm(realm, user, form)) { + throw new NotAuthorizedException("Auth failed"); + } + SkeletonKeyToken token = tokenManager.createAccessToken(realm, user); + String encoded = tokenManager.encodeToken(realm, token); + AccessTokenResponse res = accessTokenResponse(token, encoded); + return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build(); + } - @Path("{realm}/auth/request") - @GET - public Response requestAccessCode(@PathParam("realm") String realmId, - @QueryParam("response_type") String responseType, - @QueryParam("redirect_uri") String redirect, - @QueryParam("client_id") String clientId, - @QueryParam("scope") String scopeParam, - @QueryParam("state") String state) - { - RealmModel realm = adapter.getRealm(realmId); - if (realm == null) - { - throw new NotFoundException("Realm not found"); - } - if (!realm.isEnabled()) - { - throw new NotAuthorizedException("Realm not enabled"); - } - User client = realm.getIdm().getUser(clientId); - if (client == null) - return Response.ok("

Security Alert

Unknown client trying to get access to your account.

").type("text/html").build(); + @Path("auth/request/login") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public Response login(MultivaluedMap formData) { + String clientId = formData.getFirst("client_id"); + String scopeParam = formData.getFirst("scope"); + String state = formData.getFirst("state"); + String redirect = formData.getFirst("redirect_uri"); - return loginForm(null, redirect, clientId, scopeParam, state, realm, client); - } + if (!realm.isEnabled()) { + return Response.ok("Realm not enabled").type("text/html").build(); + } + User client = realm.getIdm().getUser(clientId); + if (client == null) { + throw new NotAuthorizedException("No client"); + } + if (!client.isEnabled()) { + return Response.ok("Requester not enabled").type("text/html").build(); + } + String username = formData.getFirst("username"); + User user = realm.getIdm().getUser(username); + if (user == null) { + logger.debug("user not found"); + return loginForm("Not valid user", redirect, clientId, scopeParam, state, realm, client); + } + if (!user.isEnabled()) { + return Response.ok("Your account is not enabled").type("text/html").build(); - private Response loginForm(String validationError, String redirect, String clientId, String scopeParam, String state, RealmModel realm, User client) - { - StringBuffer html = new StringBuffer(); - if (scopeParam != null) - { - html.append("

Grant Request For ").append(realm.getName()).append(" Realm

"); - if (validationError != null) - { - try - { - Thread.sleep(1000); // put in a delay - } - catch (InterruptedException e) - { - throw new RuntimeException(e); - } - html.append("

").append(validationError).append("

"); - } - html.append("

A Third Party is requesting access to the following resources

"); - html.append(""); - SkeletonKeyScope scope = tokenManager.decodeScope(scopeParam); - Map resourceMap = realm.getResourceMap(); + } + boolean authenticated = authManager.authenticateForm(realm, user, formData); + if (!authenticated) + return loginForm("Unable to authenticate, try again", redirect, clientId, scopeParam, state, realm, client); - for (String res : scope.keySet()) - { - ResourceModel resource = resourceMap.get(res); - html.append(""); - } - html.append("
Resource: ").append(resource.getName()).append("Roles:"); - Set scopeMapping = resource.getScope(client); - for (String role : scope.get(res)) - { - html.append(" ").append(role); - if (!scopeMapping.contains("*") && !scopeMapping.contains(role)) - { - return Response.ok("

Security Alert

Known client not authorized for the requested scope.

").type("text/html").build(); - } - } - html.append("

To Authorize, please login below

"); - } - else - { - Set scopeMapping = realm.getScope(client); - if (scopeMapping.contains("*")) - { - html.append("

Login For ").append(realm.getName()).append(" Realm

"); - if (validationError != null) - { - try - { - Thread.sleep(1000); // put in a delay - } - catch (InterruptedException e) - { - throw new RuntimeException(e); - } - html.append("

").append(validationError).append("

"); - } - } - else - { + SkeletonKeyToken token = null; + if (scopeParam != null) token = tokenManager.createScopedToken(scopeParam, realm, client, user); + else token = tokenManager.createLoginToken(realm, client, user); + + AccessCodeEntry code = new AccessCodeEntry(); + code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan()); + code.setToken(token); + code.setClient(client); + synchronized (accessCodeMap) { + accessCodeMap.put(code.getId(), code); + } + String accessCode = null; + try { + accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realm.getPrivateKey()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", accessCode); + if (state != null) redirectUri.queryParam("state", state); + return Response.status(302).location(redirectUri.build()).build(); + } + + @Path("access/codes") + @POST + @Produces("application/json") + public Response accessRequest(MultivaluedMap formData) { + if (!realm.isEnabled()) { + throw new NotAuthorizedException("Realm not enabled"); + } + + String code = formData.getFirst("code"); + if (code == null) { + logger.debug("code not specified"); + Map error = new HashMap(); + error.put("error", "invalid_request"); + error.put("error_description", "code not specified"); + return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); + + } + String client_id = formData.getFirst("client_id"); + if (client_id == null) { + logger.debug("client_id not specified"); + Map error = new HashMap(); + error.put("error", "invalid_request"); + error.put("error_description", "client_id not specified"); + return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); + } + User client = realm.getIdm().getUser(client_id); + if (client == null) { + logger.debug("Could not find user"); + Map error = new HashMap(); + error.put("error", "invalid_client"); + error.put("error_description", "Could not find user"); + return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); + } + + if (!client.isEnabled()) { + logger.debug("user is not enabled"); + Map error = new HashMap(); + error.put("error", "invalid_client"); + error.put("error_description", "User is not enabled"); + return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); + } + + boolean authenticated = authManager.authenticateForm(realm, client, formData); + if (!authenticated) { + Map error = new HashMap(); + error.put("error", "unauthorized_client"); + return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); + } + + + JWSInput input = new JWSInput(code, providers); + boolean verifiedCode = false; + try { + verifiedCode = RSAProvider.verify(input, realm.getPublicKey()); + } catch (Exception ignored) { + logger.debug("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"); + return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); + } + String key = input.readContent(String.class); + AccessCodeEntry accessCode = null; + synchronized (accessCodeMap) { + accessCode = accessCodeMap.remove(key); + } + if (accessCode == null) { + Map res = new HashMap(); + res.put("error", "invalid_grant"); + res.put("error_description", "Code not found"); + return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); + } + if (accessCode.isExpired()) { + Map res = new HashMap(); + res.put("error", "invalid_grant"); + res.put("error_description", "Code is expired"); + return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); + } + if (!accessCode.getToken().isActive()) { + Map res = new HashMap(); + res.put("error", "invalid_grant"); + res.put("error_description", "Token expired"); + return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); + } + if (!client.getId().equals(accessCode.getClient().getId())) { + Map res = new HashMap(); + res.put("error", "invalid_grant"); + res.put("error_description", "Auth error"); + return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); + } + AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken()); + return Response.ok(res).build(); + + } + + protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) { + byte[] tokenBytes = null; + try { + tokenBytes = JsonSerialization.toByteArray(token, false); + } catch (Exception e) { + throw new RuntimeException(e); + } + String encodedToken = new JWSBuilder() + .content(tokenBytes) + .rsa256(privateKey); + + return accessTokenResponse(token, encodedToken); + } + + protected AccessTokenResponse accessTokenResponse(SkeletonKeyToken token, String encodedToken) { + 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; + } + + @Path("auth/request") + @GET + public Response requestAccessCode(@QueryParam("response_type") String responseType, + @QueryParam("redirect_uri") String redirect, + @QueryParam("client_id") String clientId, + @QueryParam("scope") String scopeParam, + @QueryParam("state") String state) { + if (!realm.isEnabled()) { + throw new NotAuthorizedException("Realm not enabled"); + } + User client = realm.getIdm().getUser(clientId); + if (client == null) + return Response.ok("

Security Alert

Unknown client trying to get access to your account.

").type("text/html").build(); + + return loginForm(null, redirect, clientId, scopeParam, state, realm, client); + } + + private Response loginForm(String validationError, String redirect, String clientId, String scopeParam, String state, RealmModel realm, User client) { + StringBuffer html = new StringBuffer(); + if (scopeParam != null) { html.append("

Grant Request For ").append(realm.getName()).append(" Realm

"); - if (validationError != null) - { - try - { - Thread.sleep(1000); // put in a delay - } - catch (InterruptedException e) - { - throw new RuntimeException(e); - } - html.append("

").append(validationError).append("

"); - } - SkeletonKeyScope scope = new SkeletonKeyScope(); - List resources = realm.getResources(); - boolean found = false; - for (ResourceModel resource : resources) - { - Set resourceScope = resource.getScope(client); - if (resourceScope == null) continue; - if (resourceScope.size() == 0) continue; - if (!found) - { - found = true; - html.append("

A Third Party is requesting access to the following resources

"); - html.append(""); - } - html.append("
Resource: ").append(resource.getName()).append("Roles:"); - // todo add description of role - for (String role : resourceScope) - { - html.append(" ").append(role); - scope.add(resource.getName(), role); - } - } - if (!found) - { - return Response.ok("

Security Alert

Known client not authorized to access this realm.

").type("text/html").build(); - } - html.append("
"); - try - { - String json = JsonSerialization.toString(scope, false); - scopeParam = Base64Url.encode(json.getBytes("UTF-8")); - } - catch (Exception e) - { - throw new RuntimeException(e); + if (validationError != null) { + try { + Thread.sleep(1000); // put in a delay + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + html.append("

").append(validationError).append("

"); } + html.append("

A Third Party is requesting access to the following resources

"); + html.append(""); + SkeletonKeyScope scope = tokenManager.decodeScope(scopeParam); + Map resourceMap = realm.getResourceMap(); - } - } + for (String res : scope.keySet()) { + ResourceModel resource = resourceMap.get(res); + html.append(""); + } + html.append("
Resource: ").append(resource.getName()).append("Roles:"); + Set scopeMapping = resource.getScope(client); + for (String role : scope.get(res)) { + html.append(" ").append(role); + if (!scopeMapping.contains("*") && !scopeMapping.contains(role)) { + return Response.ok("

Security Alert

Known client not authorized for the requested scope.

").type("text/html").build(); + } + } + html.append("

To Authorize, please login below

"); + } else { + Set scopeMapping = realm.getScope(client); + if (scopeMapping.contains("*")) { + html.append("

Login For ").append(realm.getName()).append(" Realm

"); + if (validationError != null) { + try { + Thread.sleep(1000); // put in a delay + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + html.append("

").append(validationError).append("

"); + } + } else { + html.append("

Grant Request For ").append(realm.getName()).append(" Realm

"); + if (validationError != null) { + try { + Thread.sleep(1000); // put in a delay + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + html.append("

").append(validationError).append("

"); + } + SkeletonKeyScope scope = new SkeletonKeyScope(); + List resources = realm.getResources(); + boolean found = false; + for (ResourceModel resource : resources) { + Set resourceScope = resource.getScope(client); + if (resourceScope == null) continue; + if (resourceScope.size() == 0) continue; + if (!found) { + found = true; + html.append("

A Third Party is requesting access to the following resources

"); + html.append(""); + } + html.append("
Resource: ").append(resource.getName()).append("Roles:"); + // todo add description of role + for (String role : resourceScope) { + html.append(" ").append(role); + scope.add(resource.getName(), role); + } + } + if (!found) { + return Response.ok("

Security Alert

Known client not authorized to access this realm.

").type("text/html").build(); + } + html.append("
"); + try { + String json = JsonSerialization.toString(scope, false); + scopeParam = Base64Url.encode(json.getBytes("UTF-8")); + } catch (Exception e) { + throw new RuntimeException(e); + } - UriBuilder formActionUri = uriInfo.getBaseUriBuilder().path(TokenService.class).path(TokenService.class, "login"); - String action = formActionUri.build(realm.getId()).toString(); - html.append("
"); - html.append("Username:
"); + } + } - for (RequiredCredentialModel credential : realm.getRequiredCredentials()) - { - if (!credential.isInput()) continue; - html.append(credential.getType()).append(": "); - if (credential.isSecret()) - { - html.append("
"); + UriBuilder formActionUri = uriInfo.getBaseUriBuilder().path(TokenService.class).path(TokenService.class, "login"); + String action = formActionUri.build(realm.getId()).toString(); + html.append(""); + html.append("Username:
"); - } else - { - html.append("
"); - } - } - html.append(""); - if (scopeParam != null) - { - html.append(""); - } - if (state != null) html.append(""); - html.append(""); - html.append(""); - html.append("
"); - return Response.ok(html.toString()).type("text/html").build(); - } + for (RequiredCredentialModel credential : realm.getRequiredCredentials()) { + if (!credential.isInput()) continue; + html.append(credential.getType()).append(": "); + if (credential.isSecret()) { + html.append("
"); + + } else { + html.append("
"); + } + } + html.append(""); + if (scopeParam != null) { + html.append(""); + } + if (state != null) html.append(""); + html.append(""); + html.append(""); + html.append(""); + return Response.ok(html.toString()).type("text/html").build(); + } } diff --git a/services/src/test/java/org/keycloak/test/AdapterTest.java b/services/src/test/java/org/keycloak/test/AdapterTest.java index 6605780e5d..98fa18b055 100755 --- a/services/src/test/java/org/keycloak/test/AdapterTest.java +++ b/services/src/test/java/org/keycloak/test/AdapterTest.java @@ -7,27 +7,23 @@ import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; import org.keycloak.representations.idm.RequiredCredentialRepresentation; +import org.keycloak.services.managers.InstallationManager; import org.keycloak.services.models.RealmManager; import org.keycloak.services.models.RealmModel; -import org.keycloak.services.models.relationships.RealmResourceRelationship; import org.keycloak.services.models.RequiredCredentialModel; -import org.keycloak.services.models.relationships.RequiredCredentialRelationship; -import org.keycloak.services.models.relationships.ScopeRelationship; import org.keycloak.services.models.UserCredentialModel; +import org.keycloak.services.resources.KeycloakApplication; +import org.picketlink.idm.IdentitySession; +import org.picketlink.idm.IdentitySessionFactory; import org.picketlink.idm.IdentityManager; -import org.picketlink.idm.config.IdentityConfigurationBuilder; import org.picketlink.idm.credential.Credentials; import org.picketlink.idm.credential.Password; import org.picketlink.idm.credential.UsernamePasswordCredentials; -import org.picketlink.idm.file.internal.FileUtils; -import org.picketlink.idm.internal.IdentityManagerFactory; -import org.picketlink.idm.model.Realm; import org.picketlink.idm.model.Role; import org.picketlink.idm.model.SimpleRole; import org.picketlink.idm.model.SimpleUser; import org.picketlink.idm.model.User; -import java.io.File; import java.util.List; /** @@ -35,125 +31,108 @@ import java.util.List; * @version $Revision: 1 $ */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class AdapterTest -{ - private static IdentityManagerFactory factory; - public static final String WORKING_DIRECTORY = "/tmp/keycloak"; - public RealmManager adapter; - public RealmModel realmModel; - @Before - public void before() throws Exception - { - after(); - factory = createFactory(); - adapter = new RealmManager(factory); - } +public class AdapterTest { + private IdentitySessionFactory factory; + private IdentitySession IdentitySession; + private RealmManager adapter; + private RealmModel realmModel; - private static IdentityManagerFactory createFactory() { - IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder(); + @Before + public void before() throws Exception { + factory = KeycloakApplication.createFactory(); + IdentitySession = factory.createIdentitySession(); + adapter = new RealmManager(IdentitySession); + } - builder - .stores() - .file() - .addRealm(Realm.DEFAULT_REALM) - .workingDirectory(WORKING_DIRECTORY) - .preserveState(true) - .supportAllFeatures() - .supportRelationshipType(RealmResourceRelationship.class, RequiredCredentialRelationship.class, ScopeRelationship.class); + @After + public void after() throws Exception { + IdentitySession.close(); + factory.close(); + } - return new IdentityManagerFactory(builder.build()); - } + @Test + public void installTest() throws Exception { + new InstallationManager().install(adapter); - @After - public void after() throws Exception - { - File file = new File(WORKING_DIRECTORY); - FileUtils.delete(file); - Thread.sleep(10); // my windows machine seems to have delays on deleting files sometimes - } + } - @Test - public void test1CreateRealm() throws Exception - { - realmModel = adapter.create("JUGGLER"); - realmModel.setAccessCodeLifespan(100); - realmModel.setCookieLoginAllowed(true); - realmModel.setEnabled(true); - realmModel.setName("JUGGLER"); - realmModel.setPrivateKeyPem("0234234"); - realmModel.setPublicKeyPem("0234234"); - realmModel.setTokenLifespan(1000); - realmModel.updateRealm(); + @Test + public void test1CreateRealm() throws Exception { + realmModel = adapter.createRealm("JUGGLER"); + realmModel.setAccessCodeLifespan(100); + realmModel.setCookieLoginAllowed(true); + realmModel.setEnabled(true); + realmModel.setName("JUGGLER"); + realmModel.setPrivateKeyPem("0234234"); + realmModel.setPublicKeyPem("0234234"); + realmModel.setTokenLifespan(1000); + realmModel.updateRealm(); - System.out.println(realmModel.getId()); - realmModel = adapter.getRealm(realmModel.getId()); - Assert.assertNotNull(realmModel); - Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100); - Assert.assertEquals(realmModel.getTokenLifespan(), 1000); - Assert.assertEquals(realmModel.isEnabled(), true); - Assert.assertEquals(realmModel.getName(), "JUGGLER"); - Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234"); - Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234"); - } + System.out.println(realmModel.getId()); + realmModel = adapter.getRealm(realmModel.getId()); + Assert.assertNotNull(realmModel); + Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100); + Assert.assertEquals(realmModel.getTokenLifespan(), 1000); + Assert.assertEquals(realmModel.isEnabled(), true); + Assert.assertEquals(realmModel.getName(), "JUGGLER"); + Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234"); + Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234"); + } - @Test - public void test2RequiredCredential() throws Exception - { - test1CreateRealm(); - RequiredCredentialModel creds = new RequiredCredentialModel(); - creds.setSecret(true); - creds.setType(RequiredCredentialRepresentation.PASSWORD); - creds.setInput(true); - realmModel.addRequiredCredential(creds); - creds = new RequiredCredentialModel(); - creds.setSecret(true); - creds.setType(RequiredCredentialRepresentation.TOTP); - creds.setInput(true); - realmModel.addRequiredCredential(creds); - List storedCreds = realmModel.getRequiredCredentials(); - Assert.assertEquals(2, storedCreds.size()); - boolean totp = false; - boolean password = false; - for (RequiredCredentialModel cred : storedCreds) - { - if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) password = true; - else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) totp = true; - } - Assert.assertTrue(totp); - Assert.assertTrue(password); - } + @Test + public void test2RequiredCredential() throws Exception { + test1CreateRealm(); + RequiredCredentialModel creds = new RequiredCredentialModel(); + creds.setSecret(true); + creds.setType(RequiredCredentialRepresentation.PASSWORD); + creds.setInput(true); + realmModel.addRequiredCredential(creds); + creds = new RequiredCredentialModel(); + creds.setSecret(true); + creds.setType(RequiredCredentialRepresentation.TOTP); + creds.setInput(true); + realmModel.addRequiredCredential(creds); + List storedCreds = realmModel.getRequiredCredentials(); + Assert.assertEquals(2, storedCreds.size()); + boolean totp = false; + boolean password = false; + for (RequiredCredentialModel cred : storedCreds) { + if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) password = true; + else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) totp = true; + } + Assert.assertTrue(totp); + Assert.assertTrue(password); + } - @Test - public void testCredentialValidation() throws Exception - { - test1CreateRealm(); - User user = new SimpleUser("bburke"); - realmModel.getIdm().add(user); - UserCredentialModel cred = new UserCredentialModel(); - cred.setType(RequiredCredentialRepresentation.PASSWORD); - cred.setValue("geheim"); - realmModel.updateCredential(user, cred); - IdentityManager idm = realmModel.getIdm(); - UsernamePasswordCredentials creds = new UsernamePasswordCredentials(user.getLoginName(), new Password("geheim")); - idm.validateCredentials(creds); - Assert.assertEquals(creds.getStatus(), Credentials.Status.VALID); - } + @Test + public void testCredentialValidation() throws Exception { + test1CreateRealm(); + User user = new SimpleUser("bburke"); + realmModel.getIdm().add(user); + UserCredentialModel cred = new UserCredentialModel(); + cred.setType(RequiredCredentialRepresentation.PASSWORD); + cred.setValue("geheim"); + realmModel.updateCredential(user, cred); + IdentityManager idm = realmModel.getIdm(); + UsernamePasswordCredentials creds = new UsernamePasswordCredentials(user.getLoginName(), new Password("geheim")); + idm.validateCredentials(creds); + Assert.assertEquals(creds.getStatus(), Credentials.Status.VALID); + } - @Test - public void testRoles() throws Exception - { - test1CreateRealm(); - IdentityManager idm = realmModel.getIdm(); - idm.add(new SimpleRole("admin")); - idm.add(new SimpleRole("user")); - List roles = realmModel.getRoles(); - Assert.assertEquals(2, roles.size()); - SimpleUser user = new SimpleUser("bburke"); - idm.add(user); - Role role = idm.getRole("user"); - idm.grantRole(user, role); - Assert.assertTrue(idm.hasRole(user, role)); - } + @Test + public void testRoles() throws Exception { + test1CreateRealm(); + IdentityManager idm = realmModel.getIdm(); + idm.add(new SimpleRole("admin")); + idm.add(new SimpleRole("user")); + List roles = realmModel.getRoles(); + Assert.assertEquals(2, roles.size()); + SimpleUser user = new SimpleUser("bburke"); + idm.add(user); + Role role = idm.getRole("user"); + idm.grantRole(user, role); + Assert.assertTrue(idm.hasRole(user, role)); + } } diff --git a/services/src/test/java/org/keycloak/test/KeycloakTestBase.java b/services/src/test/java/org/keycloak/test/KeycloakTestBase.java new file mode 100755 index 0000000000..5bd0b6da7a --- /dev/null +++ b/services/src/test/java/org/keycloak/test/KeycloakTestBase.java @@ -0,0 +1,31 @@ +package org.keycloak.test; + +import org.jboss.resteasy.jwt.JsonSerialization; +import org.jboss.resteasy.test.BaseResourceTest; +import org.keycloak.representations.idm.RealmRepresentation; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class KeycloakTestBase extends BaseResourceTest +{ + public static RealmRepresentation loadJson(String path) throws IOException + { + InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + int c; + while ( (c = is.read()) != -1) + { + os.write(c); + } + byte[] bytes = os.toByteArray(); + System.out.println(new String(bytes)); + + return JsonSerialization.fromBytes(RealmRepresentation.class, bytes); + } +} diff --git a/services/src/test/java/org/keycloak/test/RealmCreationTest.java b/services/src/test/java/org/keycloak/test/RealmCreationTest.java new file mode 100755 index 0000000000..57d378f3f7 --- /dev/null +++ b/services/src/test/java/org/keycloak/test/RealmCreationTest.java @@ -0,0 +1,94 @@ +package org.keycloak.test; + +import org.jboss.resteasy.client.jaxrs.ResteasyClient; +import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; +import org.jboss.resteasy.spi.ResteasyDeployment; +import org.jboss.resteasy.test.EmbeddedContainer; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.keycloak.SkeletonKeyContextResolver; +import org.keycloak.representations.AccessTokenResponse; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.RequiredCredentialRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.services.managers.AuthenticationManager; +import org.keycloak.services.managers.InstallationManager; +import org.keycloak.services.models.RealmManager; +import org.keycloak.services.resources.KeycloakApplication; +import org.picketlink.idm.IdentitySession; +import org.picketlink.idm.model.Realm; + +import javax.ws.rs.NotAuthorizedException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Form; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; + +import static org.jboss.resteasy.test.TestPortProvider.generateURL; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class RealmCreationTest { + + private static ResteasyDeployment deployment; + private static Client client; + + @BeforeClass + public static void before() throws Exception { + deployment = new ResteasyDeployment(); + deployment.setApplicationClass(KeycloakApplication.class.getName()); + EmbeddedContainer.start(deployment); + KeycloakApplication application = (KeycloakApplication) deployment.getApplication(); + IdentitySession IdentitySession = application.getFactory().createIdentitySession(); + RealmManager manager = new RealmManager(IdentitySession); + new InstallationManager().install(manager); + client = new ResteasyClientBuilder().build(); + client.register(SkeletonKeyContextResolver.class); + } + + public static void after() throws Exception { + client.close(); + EmbeddedContainer.stop(); + } + + @Test + public void testRegisterLoginAndCreate() throws Exception { + UserRepresentation user = new UserRepresentation(); + user.setUsername("bburke"); + user.credential(RequiredCredentialRepresentation.PASSWORD, "geheim", false); + + WebTarget target = client.target(generateURL("/")); + Response response = target.path("registrations").request().post(Entity.json(user)); + Assert.assertEquals(201, response.getStatus()); + response.close(); + + + AccessTokenResponse tokenResponse = null; + try { + Form form = new Form(); + form.param(AuthenticationManager.FORM_USERNAME, "bburke"); + form.param(RequiredCredentialRepresentation.PASSWORD, "badpassword"); + tokenResponse = target.path("realms").path(Realm.DEFAULT_REALM).path("tokens/grants/identity-token").request().post(Entity.form(form), AccessTokenResponse.class); + Assert.fail(); + } catch (NotAuthorizedException e) { + } + Form form = new Form(); + form.param(AuthenticationManager.FORM_USERNAME, "bburke"); + form.param(RequiredCredentialRepresentation.PASSWORD, "geheim"); + tokenResponse = target.path("realms").path(Realm.DEFAULT_REALM).path("tokens/grants/identity-token").request().post(Entity.form(form), AccessTokenResponse.class); + Assert.assertNotNull(tokenResponse); + System.out.println(tokenResponse.getToken()); + // + + RealmRepresentation realm = KeycloakTestBase.loadJson("testrealm.json"); + response = target.path("realms").request().header(HttpHeaders.AUTHORIZATION, "Bearer " + tokenResponse.getToken()).post(Entity.json(realm)); + Assert.assertEquals(201, response.getStatus()); + response.close(); + } +} diff --git a/services/src/test/resources/META-INF/persistence.xml b/services/src/test/resources/META-INF/persistence.xml index 1e5524fe05..7b7e664090 100755 --- a/services/src/test/resources/META-INF/persistence.xml +++ b/services/src/test/resources/META-INF/persistence.xml @@ -2,7 +2,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> - + org.hibernate.ejb.HibernatePersistence org.picketlink.idm.jpa.schema.IdentityObject