[KEYCLOAK-8012] - Fix offline session support in authorization services
This commit is contained in:
parent
6a0a1031a1
commit
47066e1b89
3 changed files with 76 additions and 3 deletions
|
@ -65,6 +65,7 @@ import org.keycloak.models.ClientSessionContext;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
import org.keycloak.models.UserSessionProvider;
|
||||||
import org.keycloak.protocol.oidc.TokenManager;
|
import org.keycloak.protocol.oidc.TokenManager;
|
||||||
import org.keycloak.protocol.oidc.TokenManager.AccessTokenResponseBuilder;
|
import org.keycloak.protocol.oidc.TokenManager.AccessTokenResponseBuilder;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
|
@ -239,7 +240,13 @@ public class AuthorizationTokenService {
|
||||||
KeycloakSession keycloakSession = request.getKeycloakSession();
|
KeycloakSession keycloakSession = request.getKeycloakSession();
|
||||||
AccessToken accessToken = identity.getAccessToken();
|
AccessToken accessToken = identity.getAccessToken();
|
||||||
RealmModel realm = request.getRealm();
|
RealmModel realm = request.getRealm();
|
||||||
UserSessionModel userSessionModel = keycloakSession.sessions().getUserSession(realm, accessToken.getSessionState());
|
UserSessionProvider sessions = keycloakSession.sessions();
|
||||||
|
UserSessionModel userSessionModel = sessions.getUserSession(realm, accessToken.getSessionState());
|
||||||
|
|
||||||
|
if (userSessionModel == null) {
|
||||||
|
userSessionModel = sessions.getOfflineUserSession(realm, accessToken.getSessionState());
|
||||||
|
}
|
||||||
|
|
||||||
ClientModel client = realm.getClientByClientId(accessToken.getIssuedFor());
|
ClientModel client = realm.getClientByClientId(accessToken.getIssuedFor());
|
||||||
AuthenticatedClientSessionModel clientSession = userSessionModel.getAuthenticatedClientSessionByClient(client.getId());
|
AuthenticatedClientSessionModel clientSession = userSessionModel.getAuthenticatedClientSessionByClient(client.getId());
|
||||||
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSession);
|
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSession);
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
import org.keycloak.models.UserSessionProvider;
|
||||||
import org.keycloak.protocol.oidc.TokenManager;
|
import org.keycloak.protocol.oidc.TokenManager;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.IDToken;
|
import org.keycloak.representations.IDToken;
|
||||||
|
@ -116,7 +117,13 @@ public class KeycloakIdentity implements Identity {
|
||||||
if (token instanceof AccessToken) {
|
if (token instanceof AccessToken) {
|
||||||
this.accessToken = AccessToken.class.cast(token);
|
this.accessToken = AccessToken.class.cast(token);
|
||||||
} else {
|
} else {
|
||||||
UserSessionModel userSession = keycloakSession.sessions().getUserSession(realm, token.getSessionState());
|
UserSessionProvider sessions = keycloakSession.sessions();
|
||||||
|
UserSessionModel userSession = sessions.getUserSession(realm, token.getSessionState());
|
||||||
|
|
||||||
|
if (userSession == null) {
|
||||||
|
userSession = sessions.getOfflineUserSession(realm, token.getSessionState());
|
||||||
|
}
|
||||||
|
|
||||||
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
|
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
|
||||||
AuthenticatedClientSessionModel clientSessionModel = userSession.getAuthenticatedClientSessions().get(client.getId());
|
AuthenticatedClientSessionModel clientSessionModel = userSession.getAuthenticatedClientSessions().get(client.getId());
|
||||||
|
|
||||||
|
@ -252,7 +259,13 @@ public class KeycloakIdentity implements Identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserModel getUserFromSessionState() {
|
private UserModel getUserFromSessionState() {
|
||||||
UserSessionModel userSession = keycloakSession.sessions().getUserSession(realm, accessToken.getSessionState());
|
UserSessionProvider sessions = keycloakSession.sessions();
|
||||||
|
UserSessionModel userSession = sessions.getUserSession(realm, accessToken.getSessionState());
|
||||||
|
|
||||||
|
if (userSession == null) {
|
||||||
|
userSession = sessions.getOfflineUserSession(realm, accessToken.getSessionState());
|
||||||
|
}
|
||||||
|
|
||||||
return userSession.getUser();
|
return userSession.getUser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@ import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
|
import org.jboss.arquillian.container.test.api.ContainerController;
|
||||||
|
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -48,11 +50,13 @@ import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.authorization.client.AuthorizationDeniedException;
|
import org.keycloak.authorization.client.AuthorizationDeniedException;
|
||||||
import org.keycloak.authorization.client.AuthzClient;
|
import org.keycloak.authorization.client.AuthzClient;
|
||||||
import org.keycloak.authorization.client.Configuration;
|
import org.keycloak.authorization.client.Configuration;
|
||||||
|
import org.keycloak.authorization.client.representation.TokenIntrospectionResponse;
|
||||||
import org.keycloak.authorization.client.util.HttpResponseException;
|
import org.keycloak.authorization.client.util.HttpResponseException;
|
||||||
import org.keycloak.common.util.Base64Url;
|
import org.keycloak.common.util.Base64Url;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.AccessToken.Authorization;
|
import org.keycloak.representations.AccessToken.Authorization;
|
||||||
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
|
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
|
||||||
|
@ -92,12 +96,16 @@ public class EntitlementAPITest extends AbstractAuthzTest {
|
||||||
|
|
||||||
private AuthzClient authzClient;
|
private AuthzClient authzClient;
|
||||||
|
|
||||||
|
@ArquillianResource
|
||||||
|
protected ContainerController controller;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||||
testRealms.add(RealmBuilder.create().name("authz-test")
|
testRealms.add(RealmBuilder.create().name("authz-test")
|
||||||
.roles(RolesBuilder.create().realmRole(RoleBuilder.create().name("uma_authorization").build()))
|
.roles(RolesBuilder.create().realmRole(RoleBuilder.create().name("uma_authorization").build()))
|
||||||
.user(UserBuilder.create().username("marta").password("password").addRoles("uma_authorization"))
|
.user(UserBuilder.create().username("marta").password("password").addRoles("uma_authorization"))
|
||||||
.user(UserBuilder.create().username("kolo").password("password"))
|
.user(UserBuilder.create().username("kolo").password("password"))
|
||||||
|
.user(UserBuilder.create().username("offlineuser").password("password").addRoles("offline_access"))
|
||||||
.client(ClientBuilder.create().clientId(RESOURCE_SERVER_TEST)
|
.client(ClientBuilder.create().clientId(RESOURCE_SERVER_TEST)
|
||||||
.secret("secret")
|
.secret("secret")
|
||||||
.authorizationServicesEnabled(true)
|
.authorizationServicesEnabled(true)
|
||||||
|
@ -1155,6 +1163,51 @@ public class EntitlementAPITest extends AbstractAuthzTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOfflineRequestingPartyToken() throws Exception {
|
||||||
|
ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST);
|
||||||
|
AuthorizationResource authorization = client.authorization();
|
||||||
|
|
||||||
|
JSPolicyRepresentation policy = new JSPolicyRepresentation();
|
||||||
|
|
||||||
|
policy.setName(KeycloakModelUtils.generateId());
|
||||||
|
policy.setCode("$evaluation.grant();");
|
||||||
|
|
||||||
|
authorization.policies().js().create(policy).close();
|
||||||
|
|
||||||
|
ResourceRepresentation resource = new ResourceRepresentation();
|
||||||
|
|
||||||
|
resource.setName("Sensors");
|
||||||
|
resource.addScope("sensors:view", "sensors:update", "sensors:delete");
|
||||||
|
|
||||||
|
resource = authorization.resources().create(resource).readEntity(ResourceRepresentation.class);
|
||||||
|
|
||||||
|
ScopePermissionRepresentation permission = new ScopePermissionRepresentation();
|
||||||
|
|
||||||
|
permission.setName("View Sensor");
|
||||||
|
permission.addScope("sensors:view");
|
||||||
|
permission.addPolicy(policy.getName());
|
||||||
|
|
||||||
|
authorization.permissions().scope().create(permission);
|
||||||
|
|
||||||
|
String accessToken = new OAuthClient().realm("authz-test").clientId(RESOURCE_SERVER_TEST).scope("offline_access").doGrantAccessTokenRequest("secret", "offlineuser", "password").getAccessToken();
|
||||||
|
AuthzClient authzClient = getAuthzClient(AUTHZ_CLIENT_CONFIG);
|
||||||
|
AccessTokenResponse response = authzClient.authorization(accessToken).authorize();
|
||||||
|
assertNotNull(response.getToken());
|
||||||
|
|
||||||
|
controller.stop(suiteContext.getAuthServerInfo().getQualifier());
|
||||||
|
controller.start(suiteContext.getAuthServerInfo().getQualifier());
|
||||||
|
reconnectAdminClient();
|
||||||
|
|
||||||
|
TokenIntrospectionResponse introspectionResponse = authzClient.protection().introspectRequestingPartyToken(response.getToken());
|
||||||
|
|
||||||
|
assertTrue(introspectionResponse.getActive());
|
||||||
|
assertFalse(introspectionResponse.getPermissions().isEmpty());
|
||||||
|
|
||||||
|
response = authzClient.authorization(accessToken).authorize();
|
||||||
|
assertNotNull(response.getToken());
|
||||||
|
}
|
||||||
|
|
||||||
private void testRptRequestWithResourceName(String configFile) {
|
private void testRptRequestWithResourceName(String configFile) {
|
||||||
Metadata metadata = new Metadata();
|
Metadata metadata = new Metadata();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue