Details
diff --git a/core/src/main/java/org/keycloak/representations/AccessCode.java b/core/src/main/java/org/keycloak/representations/AccessCode.java
index 1ecebb2..9fe860e 100755
--- a/core/src/main/java/org/keycloak/representations/AccessCode.java
+++ b/core/src/main/java/org/keycloak/representations/AccessCode.java
@@ -1,6 +1,5 @@
package org.keycloak.representations;
-import java.util.HashSet;
import java.util.Set;
/**
@@ -10,15 +9,18 @@ import java.util.Set;
*/
public class AccessCode {
protected String id;
+ protected String clientId;
+ protected String userId;
protected String usernameUsed;
protected String state;
+ protected String sessionState;
protected String redirectUri;
protected boolean rememberMe;
protected String authMethod;
protected int timestamp;
protected int expiration;
- protected AccessToken accessToken;
protected Set<String> requiredActions;
+ protected Set<String> requestedRoles;
public String getId() {
return id;
@@ -28,6 +30,22 @@ public class AccessCode {
this.id = id;
}
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
public String getState() {
return state;
}
@@ -36,6 +54,14 @@ public class AccessCode {
this.state = state;
}
+ public String getSessionState() {
+ return sessionState;
+ }
+
+ public void setSessionState(String sessionState) {
+ this.sessionState = sessionState;
+ }
+
public String getRedirectUri() {
return redirectUri;
}
@@ -68,14 +94,6 @@ public class AccessCode {
this.expiration = expiration;
}
- public AccessToken getAccessToken() {
- return accessToken;
- }
-
- public void setAccessToken(AccessToken accessToken) {
- this.accessToken = accessToken;
- }
-
public int getTimestamp() {
return timestamp;
}
@@ -99,4 +117,12 @@ public class AccessCode {
public void setUsernameUsed(String usernameUsed) {
this.usernameUsed = usernameUsed;
}
+
+ public Set<String> getRequestedRoles() {
+ return requestedRoles;
+ }
+
+ public void setRequestedRoles(Set<String> requestedRoles) {
+ this.requestedRoles = requestedRoles;
+ }
}
diff --git a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
index 179e730..f5afe34 100755
--- a/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
+++ b/services/src/main/java/org/keycloak/services/managers/AccessCodeEntry.java
@@ -1,9 +1,11 @@
package org.keycloak.services.managers;
+import org.keycloak.OAuthErrorException;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.representations.AccessCode;
@@ -33,29 +35,45 @@ public class AccessCodeEntry {
}
public UserModel getUser() {
- return keycloakSession.users().getUserById(accessCode.getAccessToken().getSubject(), realm);
+ return keycloakSession.users().getUserById(accessCode.getUserId(), realm);
}
public String getSessionState() {
- return accessCode.getAccessToken().getSessionState();
+ return accessCode.getSessionState();
+ }
+
+ public void setSessionState(String state) {
+ accessCode.setSessionState(state);
}
public boolean isExpired() {
return accessCode.getExpiration() != 0 && Time.currentTime() > accessCode.getExpiration();
}
- public AccessToken getToken() {
- return accessCode.getAccessToken();
+ public Set<RoleModel> getRequestedRoles() {
+ Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
+ for (String roleId : accessCode.getRequestedRoles()) {
+ RoleModel role = realm.getRoleById(roleId);
+ if (role == null) {
+ new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid role " + roleId);
+ }
+ requestedRoles.add(realm.getRoleById(roleId));
+ }
+ return requestedRoles;
}
public ClientModel getClient() {
- return realm.findClient(accessCode.getAccessToken().getIssuedFor());
+ return realm.findClient(accessCode.getClientId());
}
public String getState() {
return accessCode.getState();
}
+ public void setState(String state) {
+ accessCode.setState(state);
+ }
+
public String getRedirectUri() {
return accessCode.getRedirectUri();
}
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 e8e1161..e5ba119 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -1,7 +1,6 @@
package org.keycloak.services.managers;
import org.jboss.logging.Logger;
-import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.keycloak.OAuthErrorException;
import org.keycloak.audit.Audit;
import org.keycloak.audit.Details;
@@ -24,12 +23,9 @@ import org.keycloak.representations.IDToken;
import org.keycloak.representations.RefreshToken;
import org.keycloak.util.Time;
-import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -80,21 +76,24 @@ public class TokenManager {
}
private AccessCodeEntry createAccessCodeEntry(String scopeParam, String state, String redirect, KeycloakSession keycloakSession, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
- List<RoleModel> realmRolesRequested = new LinkedList<RoleModel>();
- MultivaluedMap<String, RoleModel> resourceRolesRequested = new MultivaluedMapImpl<String, RoleModel>();
-
- AccessToken token = createClientAccessToken(scopeParam, realm, client, user, session, realmRolesRequested, resourceRolesRequested);
- if (session != null) token.setSessionState(session.getId());
AccessCode code = new AccessCode();
code.setId(UUID.randomUUID().toString() + System.currentTimeMillis());
- code.setAccessToken(token);
+ code.setClientId(client.getClientId());
+ code.setUserId(user.getId());
code.setTimestamp(Time.currentTime());
code.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
- code.setState(state);
+ code.setSessionState(session != null ? session.getId() : null);
code.setRedirectUri(redirect);
+ code.setState(state);
+
+ Set<String> requestedRoles = new HashSet<String>();
+ for (RoleModel r : getAccess(scopeParam, client, user)) {
+ requestedRoles.add(r.getId());
+ }
+ code.setRequestedRoles(requestedRoles);
+
AccessCodeEntry entry = new AccessCodeEntry(keycloakSession, realm, code);
return entry;
-
}
public AccessToken refreshAccessToken(KeycloakSession session, UriInfo uriInfo, RealmModel realm, ClientModel client, String encodedRefreshToken, Audit audit) throws OAuthErrorException {
@@ -142,44 +141,7 @@ public class TokenManager {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale refresh token");
}
- ApplicationModel clientApp = (client instanceof ApplicationModel) ? (ApplicationModel)client : null;
-
-
- if (refreshToken.getRealmAccess() != null) {
- for (String roleName : refreshToken.getRealmAccess().getRoles()) {
- RoleModel role = realm.getRole(roleName);
- if (role == null) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid realm role " + roleName);
- }
- if (!user.hasRole(role)) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "User no long has permission for realm role: " + roleName);
- }
- if (!client.hasScope(role)) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Client no longer has realm scope: " + roleName);
- }
- }
- }
- if (refreshToken.getResourceAccess() != null) {
- for (Map.Entry<String, AccessToken.Access> entry : refreshToken.getResourceAccess().entrySet()) {
- ApplicationModel app = realm.getApplicationByName(entry.getKey());
- if (app == null) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Application no longer exists", "Application no longer exists: " + app.getName());
- }
- for (String roleName : entry.getValue().getRoles()) {
- RoleModel role = app.getRole(roleName);
- if (role == null) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown application role: " + roleName);
- }
- if (!user.hasRole(role)) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "User no long has permission for application role " + roleName);
- }
- if (clientApp != null && !clientApp.equals(app) && !client.hasScope(role)) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Client no longer has application scope" + roleName);
- }
- }
-
- }
- }
+ verifyAccess(refreshToken, realm, client, user);
AccessToken accessToken = initToken(realm, client, user, userSession);
accessToken.setRealmAccess(refreshToken.getRealmAccess());
@@ -193,54 +155,73 @@ public class TokenManager {
return accessToken;
}
- public AccessToken createClientAccessToken(String scopeParam, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
- return createClientAccessToken(scopeParam, realm, client, user, session, new LinkedList<RoleModel>(), new MultivaluedMapImpl<String, RoleModel>());
+ public AccessToken createClientAccessToken(Set<RoleModel> requestedRoles, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
+ AccessToken token = initToken(realm, client, user, session);
+ for (RoleModel role : requestedRoles) {
+ addComposites(token, role);
+ }
+ return token;
}
- public AccessToken createClientAccessToken(String scopeParam, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested) {
+ public Set<RoleModel> getAccess(String scopeParam, ClientModel client, UserModel user) {
// todo scopeParam is ignored until we figure out a scheme that fits with openid connect
+ Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
Set<RoleModel> roleMappings = user.getRoleMappings();
Set<RoleModel> scopeMappings = client.getScopeMappings();
- ApplicationModel clientApp = (client instanceof ApplicationModel) ? (ApplicationModel)client : null;
- Set<RoleModel> clientAppRoles = clientApp == null ? null : clientApp.getRoles();
- if (clientAppRoles != null) scopeMappings.addAll(clientAppRoles);
-
- Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
+ if (client instanceof ApplicationModel) {
+ scopeMappings.addAll(((ApplicationModel) client).getRoles());
+ }
for (RoleModel role : roleMappings) {
- if (clientApp != null && role.getContainer().equals(clientApp)) requestedRoles.add(role);
for (RoleModel desiredRole : scopeMappings) {
Set<RoleModel> visited = new HashSet<RoleModel>();
applyScope(role, desiredRole, visited, requestedRoles);
}
}
- for (RoleModel role : requestedRoles) {
- if (role.getContainer() instanceof RealmModel) {
- realmRolesRequested.add(role);
- } else if (role.getContainer() instanceof ApplicationModel) {
- ApplicationModel app = (ApplicationModel)role.getContainer();
- resourceRolesRequested.add(app.getName(), role);
- }
- }
+ return requestedRoles;
+ }
+
+ public void verifyAccess(AccessToken token, RealmModel realm, ClientModel client, UserModel user) throws OAuthErrorException {
+ ApplicationModel clientApp = (client instanceof ApplicationModel) ? (ApplicationModel)client : null;
- AccessToken token = initToken(realm, client, user, session);
- if (realmRolesRequested.size() > 0) {
- for (RoleModel role : realmRolesRequested) {
- addComposites(token, role);
+ if (token.getRealmAccess() != null) {
+ for (String roleName : token.getRealmAccess().getRoles()) {
+ RoleModel role = realm.getRole(roleName);
+ if (role == null) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid realm role " + roleName);
+ }
+ if (!user.hasRole(role)) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "User no long has permission for realm role: " + roleName);
+ }
+ if (!client.hasScope(role)) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Client no longer has realm scope: " + roleName);
+ }
}
}
-
- if (resourceRolesRequested.size() > 0) {
- for (List<RoleModel> roles : resourceRolesRequested.values()) {
- for (RoleModel role : roles) {
- addComposites(token, role);
+ if (token.getResourceAccess() != null) {
+ for (Map.Entry<String, AccessToken.Access> entry : token.getResourceAccess().entrySet()) {
+ ApplicationModel app = realm.getApplicationByName(entry.getKey());
+ if (app == null) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Application no longer exists", "Application no longer exists: " + app.getName());
}
+ for (String roleName : entry.getValue().getRoles()) {
+ RoleModel role = app.getRole(roleName);
+ if (role == null) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown application role: " + roleName);
+ }
+ if (!user.hasRole(role)) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "User no long has permission for application role " + roleName);
+ }
+ if (clientApp != null && !clientApp.equals(app) && !client.hasScope(role)) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Client no longer has application scope" + roleName);
+ }
+ }
+
}
}
- return token;
}
public void initClaims(IDToken token, ClientModel model, UserModel user) {
@@ -363,7 +344,8 @@ public class TokenManager {
}
public AccessTokenResponseBuilder generateAccessToken(String scopeParam, ClientModel client, UserModel user, UserSessionModel session) {
- accessToken = createClientAccessToken(scopeParam, realm, client, user, session);
+ Set<RoleModel> requestedRoles = getAccess(scopeParam, client, user);
+ accessToken = createClientAccessToken(requestedRoles, realm, client, user, session);
return this;
}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
index c843fbe..06a0e1a 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
@@ -157,32 +157,22 @@ public class OAuthFlows {
if (!isResource) {
accessCode.resetExpiration();
- List<RoleModel> realmRolesRequested = new LinkedList<RoleModel>();
- MultivaluedMap<String, RoleModel> appRolesRequested = new MultivaluedMapImpl<String, RoleModel>();
- if (accessCode.getToken().getRealmAccess() != null) {
- if (accessCode.getToken().getRealmAccess().getRoles() != null) {
- for (String role : accessCode.getToken().getRealmAccess().getRoles()) {
- RoleModel roleModel = realm.getRole(role);
- if (roleModel != null) realmRolesRequested.add(roleModel);
- }
- }
- }
- if (accessCode.getToken().getResourceAccess().size() > 0) {
- for (Map.Entry<String, AccessToken.Access> entry : accessCode.getToken().getResourceAccess().entrySet()) {
- ApplicationModel app = realm.getApplicationByName(entry.getKey());
- if (app == null) continue;
- if (entry.getValue().getRoles() != null) {
- for (String role : entry.getValue().getRoles()) {
- RoleModel roleModel = app.getRole(role);
- if (roleModel != null) appRolesRequested.add(entry.getKey(), roleModel);
- }
-
- }
+
+ List<RoleModel> realmRoles = new LinkedList<RoleModel>();
+ MultivaluedMap<String, RoleModel> resourceRoles = new MultivaluedMapImpl<String, RoleModel>();
+ for (RoleModel r : accessCode.getRequestedRoles()) {
+ if (r.getContainer() instanceof RealmModel) {
+ realmRoles.add(r);
+ } else {
+ resourceRoles.add(((ApplicationModel) r.getContainer()).getName(), r);
}
}
- return Flows.forms(this.session, realm, uriInfo).setAccessCode(accessCode.getCode()).
- setAccessRequest(realmRolesRequested, appRolesRequested).
- setClient(client).createOAuthGrant();
+
+ return Flows.forms(this.session, realm, uriInfo)
+ .setAccessCode(accessCode.getCode())
+ .setAccessRequest(realmRoles, resourceRoles)
+ .setClient(client)
+ .createOAuthGrant();
}
if (redirect != null) {
diff --git a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
index 85eabf5..1a03dc5 100755
--- a/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/RequiredActionsService.java
@@ -227,7 +227,7 @@ public class RequiredActionsService {
// Password reset through email won't have an associated session
if (accessCode.getSessionState() == null) {
UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserById(accessCode.getUser().getId(), realm), clientConnection.getRemoteAddr());
- accessCode.getToken().setSessionState(userSession.getId());
+ accessCode.setSessionState(userSession.getId());
audit.session(userSession);
}
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 dc7c937..ef32859 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -23,6 +23,7 @@ import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
+import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
@@ -641,14 +642,6 @@ public class TokenService {
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
.build();
}
- if (!accessCode.getToken().isActive()) {
- Map<String, String> res = new HashMap<String, String>();
- res.put(OAuth2Constants.ERROR, "invalid_grant");
- res.put(OAuth2Constants.ERROR_DESCRIPTION, "Token expired");
- audit.error(Errors.INVALID_CODE);
- return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
- .build();
- }
audit.user(accessCode.getUser());
audit.session(accessCode.getSessionState());
@@ -698,8 +691,20 @@ public class TokenService {
userSession.associateClient(client);
+ AccessToken token = tokenManager.createClientAccessToken(accessCode.getRequestedRoles(), realm, client, user, userSession);
+
+ try {
+ tokenManager.verifyAccess(token, realm, client, user);
+ } catch (OAuthErrorException e) {
+ Map<String, String> error = new HashMap<String, String>();
+ error.put(OAuth2Constants.ERROR, e.getError());
+ if (e.getDescription() != null) error.put(OAuth2Constants.ERROR_DESCRIPTION, e.getDescription());
+ audit.error(Errors.INVALID_CODE);
+ return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
+ }
+
AccessTokenResponse res = tokenManager.responseBuilder(realm, client, audit)
- .accessToken(accessCode.getToken())
+ .accessToken(token)
.generateIDToken()
.generateRefreshToken().build();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
index f43d735..b40c37f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
@@ -101,7 +101,7 @@ public class AdapterTest {
TokenManager tm = new TokenManager();
UserModel admin = session.users().getUserByUsername("admin", adminRealm);
UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, null);
- AccessToken token = tm.createClientAccessToken(null, adminRealm, adminConsole, admin, userSession);
+ AccessToken token = tm.createClientAccessToken(tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession);
return tm.encodeToken(adminRealm, token);
} finally {
keycloakRule.stopSession(session, true);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
index f3da0af..7c155fb 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
@@ -88,7 +88,7 @@ public class RelativeUriAdapterTest {
TokenManager tm = new TokenManager();
UserModel admin = session.users().getUserByUsername("admin", adminRealm);
UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, null);
- AccessToken token = tm.createClientAccessToken(null, adminRealm, adminConsole, admin, userSession);
+ AccessToken token = tm.createClientAccessToken(tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession);
adminToken = tm.encodeToken(adminRealm, token);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
index 416cc3d..54f85e2 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
@@ -80,7 +80,7 @@ public class AdminAPITest {
TokenManager tm = new TokenManager();
UserModel admin = session.users().getUserByUsername("admin", adminRealm);
UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, null);
- AccessToken token = tm.createClientAccessToken(null, adminRealm, adminConsole, admin, userSession);
+ AccessToken token = tm.createClientAccessToken(tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession);
return tm.encodeToken(adminRealm, token);
} finally {
keycloakRule.stopSession(session, true);