diff --git a/pom.xml b/pom.xml index bcb889c1f5..6ba8fd23c4 100755 --- a/pom.xml +++ b/pom.xml @@ -2,14 +2,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 - Identity Guardener + Keycloak org.keycloak keycloak-parent 1.0-alpha-1 pom - 3.0.1.Final + 3.0.2.Final http://keycloak.org 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 7b725da747..e77e1c66a4 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -21,6 +21,8 @@ import java.util.HashSet; import java.util.Set; /** + * Stateless object that manages authentication + * * @author Bill Burke * @version $Revision: 1 $ */ 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 6c015048f5..dc54604c59 100755 --- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java +++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java @@ -13,17 +13,51 @@ import org.picketlink.idm.model.User; import javax.ws.rs.ForbiddenException; import javax.ws.rs.core.Response; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** + * Stateful object that creates tokens and manages oauth access codes + * * @author Bill Burke * @version $Revision: 1 $ */ public class TokenManager { - public SkeletonKeyToken createScopedToken(SkeletonKeyScope scope, RealmModel realm, User client, User user) { + protected Map accessCodeMap = new ConcurrentHashMap(); + + public void clearAccessCodes() { + accessCodeMap.clear(); + } + + public AccessCodeEntry pullAccessCode(String key) { + return accessCodeMap.remove(key); + } + + public String createAccessCode(String scopeParam, RealmModel realm, User client, User user) + { + SkeletonKeyToken token = null; + if (scopeParam != null) token = createScopedToken(scopeParam, realm, client, user); + else token = createLoginToken(realm, client, user); + + AccessCodeEntry code = new AccessCodeEntry(); + code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan()); + code.setToken(token); + code.setClient(client); + 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); + } + return accessCode; + } + + public SkeletonKeyToken createScopedToken(SkeletonKeyScope scope, RealmModel realm, User client, User user) { SkeletonKeyToken token = new SkeletonKeyToken(); token.id(RealmManager.generateId()); token.principal(user.getLoginName()); 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 6ee87cea25..77a75b3cc3 100755 --- a/services/src/main/java/org/keycloak/services/models/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/models/RealmManager.java @@ -1,15 +1,10 @@ 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.IdentitySession; 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; @@ -26,10 +21,10 @@ public class RealmManager { return counter.getAndIncrement() + "-" + System.currentTimeMillis(); } - protected IdentitySession IdentitySession; + protected IdentitySession identitySession; public RealmManager(IdentitySession IdentitySession) { - this.IdentitySession = IdentitySession; + this.identitySession = IdentitySession; } public RealmModel defaultRealm() { @@ -37,11 +32,11 @@ public class RealmManager { } public RealmModel getRealm(String id) { - Realm existing = IdentitySession.findRealm(id); + Realm existing = identitySession.findRealm(id); if (existing == null) { return null; } - return new RealmModel(existing, IdentitySession); + return new RealmModel(existing, identitySession); } public RealmModel createRealm(String name) { @@ -49,11 +44,11 @@ public class RealmManager { } public RealmModel createRealm(String id, String name) { - Realm newRealm = IdentitySession.createRealm(id); - IdentityManager idm = IdentitySession.createIdentityManager(newRealm); + 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); + RealmModel realm = new RealmModel(newRealm, identitySession); return realm; } @@ -68,4 +63,4 @@ public class RealmManager { realm.setPublicKey(keyPair.getPublic()); realm.updateRealm(); } - } +} 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 cb343b0989..bad532c657 100755 --- a/services/src/main/java/org/keycloak/services/models/ResourceModel.java +++ b/services/src/main/java/org/keycloak/services/models/ResourceModel.java @@ -5,7 +5,6 @@ import org.keycloak.services.models.relationships.ScopeRelationship; import org.picketlink.idm.IdentitySession; import org.picketlink.idm.IdentityManager; import org.picketlink.idm.model.Agent; -import org.picketlink.idm.model.Attribute; import org.picketlink.idm.model.Grant; import org.picketlink.idm.model.Role; import org.picketlink.idm.model.Tier; @@ -22,24 +21,20 @@ import java.util.Set; * @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"; - protected Tier tier; protected ResourceRelationship agent; protected RealmModel realm; - protected IdentitySession IdentitySession; + protected IdentitySession identitySession; - public ResourceModel(Tier tier, ResourceRelationship agent, RealmModel realm, IdentitySession factory) { + public ResourceModel(Tier tier, ResourceRelationship agent, RealmModel realm, IdentitySession session) { this.tier = tier; this.agent = agent; this.realm = realm; - this.IdentitySession = factory; + this.identitySession = session; } public IdentityManager getIdm() { - return IdentitySession.createIdentityManager(tier); + return identitySession.createIdentityManager(tier); } public void updateResource() { diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index 3af3e9c1f8..79b9f47470 100755 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -2,6 +2,7 @@ package org.keycloak.services.resources; import org.keycloak.SkeletonKeyContextResolver; import org.keycloak.services.filters.IdentitySessionFilter; +import org.keycloak.services.managers.TokenManager; import org.keycloak.services.models.relationships.RealmAdminRelationship; import org.keycloak.services.models.relationships.ResourceRelationship; import org.keycloak.services.models.relationships.RequiredCredentialRelationship; @@ -38,7 +39,7 @@ public class KeycloakApplication extends Application { public KeycloakApplication() { this.factory = createFactory(); IdentitySessionFilter filter = new IdentitySessionFilter(factory); - singletons.add(new RealmsResource()); + singletons.add(new RealmsResource(new TokenManager())); singletons.add(filter); classes.add(SkeletonKeyContextResolver.class); classes.add(RegistrationService.class); diff --git a/services/src/main/java/org/keycloak/services/resources/RealmSubResource.java b/services/src/main/java/org/keycloak/services/resources/RealmSubResource.java index 9d76a21a60..8e33a5ad13 100755 --- a/services/src/main/java/org/keycloak/services/resources/RealmSubResource.java +++ b/services/src/main/java/org/keycloak/services/resources/RealmSubResource.java @@ -24,7 +24,7 @@ public class RealmSubResource { protected UriInfo uriInfo; @Context - protected IdentitySession IdentitySession; + protected IdentitySession identitySession; protected RealmModel realm; diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java index b749569102..cf7576d431 100755 --- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java @@ -7,8 +7,8 @@ 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.managers.TokenManager; import org.keycloak.services.models.RealmManager; import org.keycloak.services.models.RealmModel; import org.keycloak.services.models.RequiredCredentialModel; @@ -38,7 +38,6 @@ 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 @@ -55,23 +54,26 @@ public class RealmsResource { protected HttpHeaders headers; @Context - protected - IdentitySession IdentitySession; + protected IdentitySession identitySession; @Context ResourceContext resourceContext; - protected Map accessCodes = new ConcurrentHashMap(); + protected TokenManager tokenManager; + + public RealmsResource(TokenManager tokenManager) { + this.tokenManager = tokenManager; + } @Path("{realm}/tokens") public TokenService getTokenService(@PathParam("realm") String id) { - RealmManager realmManager = new RealmManager(IdentitySession); + 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); + TokenService tokenService = new TokenService(realm, tokenManager); resourceContext.initResource(tokenService); return tokenService; @@ -80,7 +82,7 @@ public class RealmsResource { @Path("{realm}") public RealmSubResource getRealmResource(@PathParam("realm") String id) { - RealmManager realmManager = new RealmManager(IdentitySession); + RealmManager realmManager = new RealmManager(identitySession); RealmModel realm = realmManager.getRealm(id); if (realm == null) { logger.debug("realm not found"); @@ -96,13 +98,13 @@ public class RealmsResource { @POST @Consumes("application/json") public Response importRealm(RealmRepresentation rep) { - IdentitySession.getTransaction().begin(); + identitySession.getTransaction().begin(); RealmModel realm; try { realm = createRealm(rep); - IdentitySession.getTransaction().commit(); + identitySession.getTransaction().commit(); } catch (RuntimeException re) { - IdentitySession.getTransaction().rollback(); + identitySession.getTransaction().rollback(); throw re; } UriBuilder builder = uriInfo.getRequestUriBuilder().path(realm.getId()); @@ -112,7 +114,7 @@ public class RealmsResource { } protected RealmModel createRealm(RealmRepresentation rep) { - RealmManager realmManager = new RealmManager(IdentitySession); + 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); 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 d77339daef..b9831ce500 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -12,7 +12,6 @@ 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; @@ -35,7 +34,6 @@ import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.Providers; -import java.io.UnsupportedEncodingException; import java.security.PrivateKey; import java.util.HashMap; import java.util.List; @@ -50,7 +48,7 @@ public class TokenService { protected static final Logger logger = Logger.getLogger(TokenService.class); - protected Map accessCodeMap; + //protected Map accessCodeMap; @Context protected UriInfo uriInfo; @@ -65,12 +63,12 @@ public class TokenService { IdentitySession IdentitySession; protected RealmModel realm; - protected TokenManager tokenManager = new TokenManager(); + protected TokenManager tokenManager; protected AuthenticationManager authManager = new AuthenticationManager(); - public TokenService(RealmModel realm, Map accessCodeMap) { + public TokenService(RealmModel realm, TokenManager tokenManager) { this.realm = realm; - this.accessCodeMap = accessCodeMap; + this.tokenManager = tokenManager; } @Path("grants/identity-token") @@ -163,23 +161,7 @@ public class TokenService { 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); - - 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); - } + String accessCode = tokenManager.createAccessCode(scopeParam, realm, client, user); UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", accessCode); if (state != null) redirectUri.queryParam("state", state); return Response.status(302).location(redirectUri.build()).build(); @@ -249,10 +231,7 @@ public class TokenService { 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); - } + AccessCodeEntry accessCode = tokenManager.pullAccessCode(key); if (accessCode == null) { Map res = new HashMap(); res.put("error", "invalid_grant");