KEYCLOAK-4626 Authentication sessions - SAML, offline tokens, broker logout and other fixes
This commit is contained in:
parent
47aaa5a636
commit
168153c6e7
102 changed files with 2589 additions and 3188 deletions
|
@ -170,8 +170,8 @@ public class HttpClientBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder disableCookieCache() {
|
||||
this.disableCookieCache = true;
|
||||
public HttpClientBuilder disableCookieCache(boolean disable) {
|
||||
this.disableCookieCache = disable;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -334,7 +334,7 @@ public class HttpClientBuilder {
|
|||
}
|
||||
|
||||
public HttpClient build(AdapterHttpClientConfig adapterConfig) {
|
||||
disableCookieCache(); // disable cookie cache as we don't want sticky sessions for load balancing
|
||||
disableCookieCache(true); // disable cookie cache as we don't want sticky sessions for load balancing
|
||||
|
||||
String truststorePath = adapterConfig.getTruststore();
|
||||
if (truststorePath != null) {
|
||||
|
|
|
@ -168,7 +168,7 @@ public class OIDCFilterSessionStore extends FilterSessionStore implements Adapte
|
|||
HttpSession httpSession = request.getSession();
|
||||
httpSession.setAttribute(KeycloakAccount.class.getName(), sAccount);
|
||||
httpSession.setAttribute(KeycloakSecurityContext.class.getName(), sAccount.getKeycloakSecurityContext());
|
||||
if (idMapper != null) idMapper.map(account.getKeycloakSecurityContext().getToken().getClientSession(), account.getPrincipal().getName(), httpSession.getId());
|
||||
if (idMapper != null) idMapper.map(account.getKeycloakSecurityContext().getToken().getSessionState(), account.getPrincipal().getName(), httpSession.getId());
|
||||
//String username = securityContext.getToken().getSubject();
|
||||
//log.fine("userSessionManagement.login: " + username);
|
||||
}
|
||||
|
|
|
@ -97,9 +97,6 @@ public class AccessToken extends IDToken {
|
|||
}
|
||||
}
|
||||
|
||||
@JsonProperty("client_session")
|
||||
protected String clientSession;
|
||||
|
||||
@JsonProperty("trusted-certs")
|
||||
protected Set<String> trustedCertificates;
|
||||
|
||||
|
@ -156,10 +153,6 @@ public class AccessToken extends IDToken {
|
|||
return resourceAccess.get(resource);
|
||||
}
|
||||
|
||||
public String getClientSession() {
|
||||
return clientSession;
|
||||
}
|
||||
|
||||
public Access addAccess(String service) {
|
||||
Access access = resourceAccess.get(service);
|
||||
if (access != null) return access;
|
||||
|
@ -168,11 +161,6 @@ public class AccessToken extends IDToken {
|
|||
return access;
|
||||
}
|
||||
|
||||
public AccessToken clientSession(String session) {
|
||||
this.clientSession = session;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessToken id(String id) {
|
||||
return (AccessToken) super.id(id);
|
||||
|
|
|
@ -40,7 +40,6 @@ public class RefreshToken extends AccessToken {
|
|||
*/
|
||||
public RefreshToken(AccessToken token) {
|
||||
this();
|
||||
this.clientSession = token.getClientSession();
|
||||
this.issuer = token.issuer;
|
||||
this.subject = token.subject;
|
||||
this.issuedFor = token.issuedFor;
|
||||
|
|
|
@ -47,7 +47,7 @@ is in your `/etc/hosts` before other records for the 127.0.0.1 host to avoid iss
|
|||
|
||||
**5)** Configure Kerberos client (On linux it's in file `/etc/krb5.conf` ). You need to configure `KEYCLOAK.ORG` realm for host `localhost` and enable `forwardable` flag, which is needed
|
||||
for credential delegation example, as application needs to forward Kerberos ticket and authenticate with it against LDAP server.
|
||||
See [this file](https://github.com/keycloak/keycloak/blob/master/testsuite/integration/src/test/resources/kerberos/test-krb5.conf) for inspiration.
|
||||
See [this file](https://github.com/keycloak/keycloak/blob/master/testsuite/integration-arquillian/tests/base/src/test/resources/kerberos/test-krb5.conf) for inspiration.
|
||||
On OS X the file to edit (or create) is `/Library/Preferences/edu.mit.Kerberos` with the same syntax as `krb5.conf`.
|
||||
On Windows the file to edit (or create) is `c:\Windows\krb5.ini` with the same syntax as `krb5.conf`.
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.keycloak.models.AuthenticatedClientSessionModel;
|
|||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.sessions.infinispan.entities.ClientLoginSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
|
||||
|
||||
|
@ -36,14 +36,16 @@ import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
|
|||
*/
|
||||
public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSessionModel {
|
||||
|
||||
private final ClientLoginSessionEntity entity;
|
||||
private final AuthenticatedClientSessionEntity entity;
|
||||
private final ClientModel client;
|
||||
private final InfinispanUserSessionProvider provider;
|
||||
private final Cache<String, SessionEntity> cache;
|
||||
private UserSessionAdapter userSession;
|
||||
|
||||
public AuthenticatedClientSessionAdapter(ClientLoginSessionEntity entity, UserSessionAdapter userSession, InfinispanUserSessionProvider provider, Cache<String, SessionEntity> cache) {
|
||||
public AuthenticatedClientSessionAdapter(AuthenticatedClientSessionEntity entity, ClientModel client, UserSessionAdapter userSession, InfinispanUserSessionProvider provider, Cache<String, SessionEntity> cache) {
|
||||
this.provider = provider;
|
||||
this.entity = entity;
|
||||
this.client = client;
|
||||
this.cache = cache;
|
||||
this.userSession = userSession;
|
||||
}
|
||||
|
@ -55,23 +57,23 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
|
|||
|
||||
@Override
|
||||
public void setUserSession(UserSessionModel userSession) {
|
||||
String clientUUID = entity.getClient();
|
||||
String clientUUID = client.getId();
|
||||
UserSessionEntity sessionEntity = this.userSession.getEntity();
|
||||
|
||||
// Dettach userSession
|
||||
if (userSession == null) {
|
||||
if (sessionEntity.getClientLoginSessions() != null) {
|
||||
sessionEntity.getClientLoginSessions().remove(clientUUID);
|
||||
if (sessionEntity.getAuthenticatedClientSessions() != null) {
|
||||
sessionEntity.getAuthenticatedClientSessions().remove(clientUUID);
|
||||
update();
|
||||
this.userSession = null;
|
||||
}
|
||||
} else {
|
||||
this.userSession = (UserSessionAdapter) userSession;
|
||||
|
||||
if (sessionEntity.getClientLoginSessions() == null) {
|
||||
sessionEntity.setClientLoginSessions(new HashMap<>());
|
||||
if (sessionEntity.getAuthenticatedClientSessions() == null) {
|
||||
sessionEntity.setAuthenticatedClientSessions(new HashMap<>());
|
||||
}
|
||||
sessionEntity.getClientLoginSessions().put(clientUUID, entity);
|
||||
sessionEntity.getAuthenticatedClientSessions().put(clientUUID, entity);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
@ -104,8 +106,7 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
|
|||
|
||||
@Override
|
||||
public ClientModel getClient() {
|
||||
String client = entity.getClient();
|
||||
return getRealm().getClientById(client);
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -192,4 +193,5 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
|
|||
copy.putAll(entity.getNotes());
|
||||
return copy;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,292 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.sessions.infinispan;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class ClientSessionAdapter implements ClientSessionModel {
|
||||
|
||||
private KeycloakSession session;
|
||||
private InfinispanUserSessionProvider provider;
|
||||
private Cache<String, SessionEntity> cache;
|
||||
private RealmModel realm;
|
||||
private ClientSessionEntity entity;
|
||||
private boolean offline;
|
||||
|
||||
public ClientSessionAdapter(KeycloakSession session, InfinispanUserSessionProvider provider, Cache<String, SessionEntity> cache, RealmModel realm,
|
||||
ClientSessionEntity entity, boolean offline) {
|
||||
this.session = session;
|
||||
this.provider = provider;
|
||||
this.cache = cache;
|
||||
this.realm = realm;
|
||||
this.entity = entity;
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClient() {
|
||||
return realm.getClientById(entity.getClient());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSessionAdapter getUserSession() {
|
||||
return entity.getUserSession() != null ? provider.getUserSession(realm, entity.getUserSession(), offline) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserSession(UserSessionModel userSession) {
|
||||
if (userSession == null) {
|
||||
if (entity.getUserSession() != null) {
|
||||
provider.dettachSession(getUserSession(), this);
|
||||
}
|
||||
entity.setUserSession(null);
|
||||
} else {
|
||||
UserSessionAdapter userSessionAdapter = (UserSessionAdapter) userSession;
|
||||
if (entity.getUserSession() != null) {
|
||||
if (entity.getUserSession().equals(userSession.getId())) {
|
||||
return;
|
||||
} else {
|
||||
provider.dettachSession(userSessionAdapter, this);
|
||||
}
|
||||
} else {
|
||||
provider.attachSession(userSessionAdapter, this);
|
||||
}
|
||||
|
||||
entity.setUserSession(userSession.getId());
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRedirectUri() {
|
||||
return entity.getRedirectUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRedirectUri(String uri) {
|
||||
entity.setRedirectUri(uri);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimestamp() {
|
||||
return entity.getTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimestamp(int timestamp) {
|
||||
entity.setTimestamp(timestamp);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAction() {
|
||||
return entity.getAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAction(String action) {
|
||||
entity.setAction(action);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRoles() {
|
||||
if (entity.getRoles() == null || entity.getRoles().isEmpty()) return Collections.emptySet();
|
||||
return new HashSet<>(entity.getRoles());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRoles(Set<String> roles) {
|
||||
entity.setRoles(roles);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getProtocolMappers() {
|
||||
if (entity.getProtocolMappers() == null || entity.getProtocolMappers().isEmpty()) return Collections.emptySet();
|
||||
return new HashSet<>(entity.getProtocolMappers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProtocolMappers(Set<String> protocolMappers) {
|
||||
entity.setProtocolMappers(protocolMappers);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
return entity.getAuthMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProtocol(String authMethod) {
|
||||
entity.setAuthMethod(authMethod);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNote(String name) {
|
||||
return entity.getNotes() != null ? entity.getNotes().get(name) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNote(String name, String value) {
|
||||
if (entity.getNotes() == null) {
|
||||
entity.setNotes(new HashMap<String, String>());
|
||||
}
|
||||
entity.getNotes().put(name, value);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNote(String name) {
|
||||
if (entity.getNotes() != null) {
|
||||
entity.getNotes().remove(name);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getNotes() {
|
||||
if (entity.getNotes() == null || entity.getNotes().isEmpty()) return Collections.emptyMap();
|
||||
Map<String, String> copy = new HashMap<>();
|
||||
copy.putAll(entity.getNotes());
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserSessionNote(String name, String value) {
|
||||
if (entity.getUserSessionNotes() == null) {
|
||||
entity.setUserSessionNotes(new HashMap<String, String>());
|
||||
}
|
||||
entity.getUserSessionNotes().put(name, value);
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getUserSessionNotes() {
|
||||
if (entity.getUserSessionNotes() == null) {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
HashMap<String, String> copy = new HashMap<>();
|
||||
copy.putAll(entity.getUserSessionNotes());
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearUserSessionNotes() {
|
||||
entity.setUserSessionNotes(new HashMap<String, String>());
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRequiredActions() {
|
||||
Set<String> copy = new HashSet<>();
|
||||
copy.addAll(entity.getRequiredActions());
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequiredAction(String action) {
|
||||
entity.getRequiredActions().add(action);
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRequiredAction(String action) {
|
||||
entity.getRequiredActions().remove(action);
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRequiredAction(UserModel.RequiredAction action) {
|
||||
addRequiredAction(action.name());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRequiredAction(UserModel.RequiredAction action) {
|
||||
removeRequiredAction(action.name());
|
||||
}
|
||||
|
||||
void update() {
|
||||
provider.getTx().replace(cache, entity.getId(), entity);
|
||||
}
|
||||
@Override
|
||||
public Map<String, ExecutionStatus> getExecutionStatus() {
|
||||
return entity.getAuthenticatorStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExecutionStatus(String authenticator, ExecutionStatus status) {
|
||||
entity.getAuthenticatorStatus().put(authenticator, status);
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearExecutionStatus() {
|
||||
entity.getAuthenticatorStatus().clear();
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getAuthenticatedUser() {
|
||||
return entity.getAuthUserId() == null ? null : session.users().getUserById(entity.getAuthUserId(), realm); }
|
||||
|
||||
@Override
|
||||
public void setAuthenticatedUser(UserModel user) {
|
||||
if (user == null) entity.setAuthUserId(null);
|
||||
else entity.setAuthUserId(user.getId());
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -19,11 +19,9 @@ package org.keycloak.models.sessions.infinispan;
|
|||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -41,21 +39,6 @@ public class Consumers {
|
|||
return new UserSessionModelsConsumer(provider, realm, offline);
|
||||
}
|
||||
|
||||
public static class UserSessionIdAndTimestampConsumer implements Consumer<Map.Entry<String, SessionEntity>> {
|
||||
|
||||
private Map<String, Integer> sessions = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void accept(Map.Entry<String, SessionEntity> entry) {
|
||||
SessionEntity e = entry.getValue();
|
||||
if (e instanceof ClientSessionEntity) {
|
||||
ClientSessionEntity ce = (ClientSessionEntity) e;
|
||||
sessions.put(ce.getUserSession(), ce.getTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class UserSessionModelsConsumer implements Consumer<Map.Entry<String, SessionEntity>> {
|
||||
|
||||
private InfinispanUserSessionProvider provider;
|
||||
|
|
|
@ -120,6 +120,7 @@ public class InfinispanAuthenticationSessionProvider implements AuthenticationSe
|
|||
log.debugf("Removed %d expired user sessions for realm '%s'", counter, realm.getName());
|
||||
}
|
||||
|
||||
// TODO: Should likely listen to "RealmRemovedEvent" received from cluster and clean just local sessions
|
||||
@Override
|
||||
public void onRealmRemoved(RealmModel realm) {
|
||||
Iterator<Map.Entry<String, AuthenticationSessionEntity>> itr = cache.entrySet().stream().filter(AuthenticationSessionPredicate.create(realm.getId())).iterator();
|
||||
|
@ -128,6 +129,7 @@ public class InfinispanAuthenticationSessionProvider implements AuthenticationSe
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Should likely listen to "ClientRemovedEvent" received from cluster and clean just local sessions
|
||||
@Override
|
||||
public void onClientRemoved(RealmModel realm, ClientModel client) {
|
||||
Iterator<Map.Entry<String, AuthenticationSessionEntity>> itr = cache.entrySet().stream().filter(AuthenticationSessionPredicate.create(realm.getId()).client(client.getId())).iterator();
|
||||
|
|
|
@ -25,9 +25,7 @@ import org.keycloak.common.util.Time;
|
|||
import org.keycloak.models.ClientInitialAccessModel;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -35,31 +33,29 @@ import org.keycloak.models.UserSessionModel;
|
|||
import org.keycloak.models.UserSessionProvider;
|
||||
import org.keycloak.models.session.UserSessionPersisterProvider;
|
||||
import org.keycloak.models.sessions.infinispan.entities.ClientInitialAccessEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.ClientLoginSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
|
||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.stream.ClientInitialAccessPredicate;
|
||||
import org.keycloak.models.sessions.infinispan.stream.ClientSessionPredicate;
|
||||
import org.keycloak.models.sessions.infinispan.stream.Comparators;
|
||||
import org.keycloak.models.sessions.infinispan.stream.Mappers;
|
||||
import org.keycloak.models.sessions.infinispan.stream.SessionPredicate;
|
||||
import org.keycloak.models.sessions.infinispan.stream.UserLoginFailurePredicate;
|
||||
import org.keycloak.models.sessions.infinispan.stream.UserSessionPredicate;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RealmInfoUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
|
@ -90,31 +86,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
return offline ? offlineSessionCache : sessionCache;
|
||||
}
|
||||
|
||||
|
||||
// TODO:mposolda remove
|
||||
@Override
|
||||
public ClientSessionModel createClientSession(RealmModel realm, ClientModel client) {
|
||||
String id = KeycloakModelUtils.generateId();
|
||||
|
||||
ClientSessionEntity entity = new ClientSessionEntity();
|
||||
entity.setId(id);
|
||||
entity.setRealm(realm.getId());
|
||||
entity.setTimestamp(Time.currentTime());
|
||||
entity.setClient(client.getId());
|
||||
|
||||
|
||||
tx.put(sessionCache, id, entity);
|
||||
|
||||
ClientSessionAdapter wrap = wrap(realm, entity, false);
|
||||
return wrap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession) {
|
||||
ClientLoginSessionEntity entity = new ClientLoginSessionEntity();
|
||||
entity.setClient(client.getId());
|
||||
AuthenticatedClientSessionEntity entity = new AuthenticatedClientSessionEntity();
|
||||
|
||||
AuthenticatedClientSessionAdapter adapter = new AuthenticatedClientSessionAdapter(entity, (UserSessionAdapter) userSession, this, sessionCache);
|
||||
AuthenticatedClientSessionAdapter adapter = new AuthenticatedClientSessionAdapter(entity, client, (UserSessionAdapter) userSession, this, sessionCache);
|
||||
adapter.setUserSession(userSession);
|
||||
return adapter;
|
||||
}
|
||||
|
@ -123,6 +99,15 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
public UserSessionModel createUserSession(String id, RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId) {
|
||||
UserSessionEntity entity = new UserSessionEntity();
|
||||
entity.setId(id);
|
||||
|
||||
updateSessionEntity(entity, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId);
|
||||
|
||||
tx.putIfAbsent(sessionCache, id, entity);
|
||||
|
||||
return wrap(realm, entity, false);
|
||||
}
|
||||
|
||||
void updateSessionEntity(UserSessionEntity entity, RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId) {
|
||||
entity.setRealm(realm.getId());
|
||||
entity.setUser(user.getId());
|
||||
entity.setLoginUsername(loginUsername);
|
||||
|
@ -137,41 +122,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
entity.setStarted(currentTime);
|
||||
entity.setLastSessionRefresh(currentTime);
|
||||
|
||||
tx.putIfAbsent(sessionCache, id, entity);
|
||||
|
||||
return wrap(realm, entity, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSessionModel getClientSession(RealmModel realm, String id) {
|
||||
return getClientSession(realm, id, false);
|
||||
}
|
||||
|
||||
protected ClientSessionModel getClientSession(RealmModel realm, String id, boolean offline) {
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
ClientSessionEntity entity = (ClientSessionEntity) tx.get(cache, id); // Chance created in this transaction
|
||||
|
||||
if (entity == null) {
|
||||
entity = (ClientSessionEntity) cache.get(id);
|
||||
}
|
||||
|
||||
return wrap(realm, entity, offline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSessionModel getClientSession(String id) {
|
||||
// Chance created in this transaction
|
||||
ClientSessionEntity entity = (ClientSessionEntity) tx.get(sessionCache, id);
|
||||
|
||||
if (entity == null) {
|
||||
entity = (ClientSessionEntity) sessionCache.get(id);
|
||||
}
|
||||
|
||||
if (entity != null) {
|
||||
RealmModel realm = session.realms().getRealm(entity.getRealm());
|
||||
return wrap(realm, entity, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -230,37 +181,50 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
protected List<UserSessionModel> getUserSessions(final RealmModel realm, ClientModel client, int firstResult, int maxResults, final boolean offline) {
|
||||
final Cache<String, SessionEntity> cache = getCache(offline);
|
||||
|
||||
Iterator<UserSessionTimestamp> itr = cache.entrySet().stream()
|
||||
.filter(ClientSessionPredicate.create(realm.getId()).client(client.getId()).requireUserSession())
|
||||
.map(Mappers.clientSessionToUserSessionTimestamp())
|
||||
.iterator();
|
||||
Stream<UserSessionEntity> stream = cache.entrySet().stream()
|
||||
.filter(UserSessionPredicate.create(realm.getId()).client(client.getId()))
|
||||
.map(Mappers.userSessionEntity())
|
||||
.sorted(Comparators.userSessionLastSessionRefresh());
|
||||
|
||||
Map<String, UserSessionTimestamp> m = new HashMap<>();
|
||||
while(itr.hasNext()) {
|
||||
UserSessionTimestamp next = itr.next();
|
||||
if (!m.containsKey(next.getUserSessionId()) || m.get(next.getUserSessionId()).getClientSessionTimestamp() < next.getClientSessionTimestamp()) {
|
||||
m.put(next.getUserSessionId(), next);
|
||||
}
|
||||
// Doesn't work due to ISPN-6575 . TODO Fix once infinispan upgraded to 8.2.2.Final or 9.0
|
||||
// if (firstResult > 0) {
|
||||
// stream = stream.skip(firstResult);
|
||||
// }
|
||||
//
|
||||
// if (maxResults > 0) {
|
||||
// stream = stream.limit(maxResults);
|
||||
// }
|
||||
//
|
||||
// List<UserSessionEntity> entities = stream.collect(Collectors.toList());
|
||||
|
||||
|
||||
// Workaround for ISPN-6575 TODO Fix once infinispan upgraded to 8.2.2.Final or 9.0 and replace with the more effective code above
|
||||
if (firstResult < 0) {
|
||||
firstResult = 0;
|
||||
}
|
||||
if (maxResults < 0) {
|
||||
maxResults = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
Stream<UserSessionTimestamp> stream = new LinkedList<>(m.values()).stream().sorted(Comparators.userSessionTimestamp());
|
||||
int count = firstResult + maxResults;
|
||||
if (count > 0) {
|
||||
stream = stream.limit(count);
|
||||
}
|
||||
List<UserSessionEntity> entities = stream.collect(Collectors.toList());
|
||||
|
||||
if (firstResult > 0) {
|
||||
stream = stream.skip(firstResult);
|
||||
if (firstResult > entities.size()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (maxResults > 0) {
|
||||
stream = stream.limit(maxResults);
|
||||
}
|
||||
maxResults = Math.min(maxResults, entities.size() - firstResult);
|
||||
entities = entities.subList(firstResult, firstResult + maxResults);
|
||||
|
||||
|
||||
final List<UserSessionModel> sessions = new LinkedList<>();
|
||||
stream.forEach(new Consumer<UserSessionTimestamp>() {
|
||||
entities.stream().forEach(new Consumer<UserSessionEntity>() {
|
||||
@Override
|
||||
public void accept(UserSessionTimestamp userSessionTimestamp) {
|
||||
SessionEntity entity = cache.get(userSessionTimestamp.getUserSessionId());
|
||||
if (entity != null) {
|
||||
sessions.add(wrap(realm, (UserSessionEntity) entity, offline));
|
||||
}
|
||||
public void accept(UserSessionEntity userSessionEntity) {
|
||||
sessions.add(wrap(realm, userSessionEntity, offline));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -273,7 +237,9 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
protected long getUserSessionsCount(RealmModel realm, ClientModel client, boolean offline) {
|
||||
return getCache(offline).entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).client(client.getId()).requireUserSession()).map(Mappers.clientSessionToUserSessionId()).distinct().count();
|
||||
return getCache(offline).entrySet().stream()
|
||||
.filter(UserSessionPredicate.create(realm.getId()).client(client.getId()))
|
||||
.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -303,9 +269,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
public void removeExpired(RealmModel realm) {
|
||||
log.debugf("Removing expired sessions");
|
||||
removeExpiredUserSessions(realm);
|
||||
removeExpiredClientSessions(realm);
|
||||
removeExpiredOfflineUserSessions(realm);
|
||||
removeExpiredOfflineClientSessions(realm);
|
||||
removeExpiredClientInitialAccess(realm);
|
||||
}
|
||||
|
||||
|
@ -322,33 +286,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
counter++;
|
||||
UserSessionEntity entity = (UserSessionEntity) itr.next().getValue();
|
||||
tx.remove(sessionCache, entity.getId());
|
||||
|
||||
if (entity.getClientSessions() != null) {
|
||||
for (String clientSessionId : entity.getClientSessions()) {
|
||||
tx.remove(sessionCache, clientSessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debugf("Removed %d expired user sessions for realm '%s'", counter, realm.getName());
|
||||
}
|
||||
|
||||
private void removeExpiredClientSessions(RealmModel realm) {
|
||||
int expiredDettachedClientSession = Time.currentTime() - RealmInfoUtil.getDettachedClientSessionLifespan(realm);
|
||||
|
||||
// Each cluster node cleanups just local sessions, which are those owned by himself (+ few more taking l1 cache into account)
|
||||
Iterator<Map.Entry<String, SessionEntity>> itr = sessionCache.getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL)
|
||||
.entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).expiredRefresh(expiredDettachedClientSession).requireNullUserSession()).iterator();
|
||||
|
||||
int counter = 0;
|
||||
while (itr.hasNext()) {
|
||||
counter++;
|
||||
tx.remove(sessionCache, itr.next().getKey());
|
||||
}
|
||||
|
||||
log.debugf("Removed %d expired client sessions for realm '%s'", counter, realm.getName());
|
||||
}
|
||||
|
||||
private void removeExpiredOfflineUserSessions(RealmModel realm) {
|
||||
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
int expiredOffline = Time.currentTime() - realm.getOfflineSessionIdleTimeout();
|
||||
|
@ -366,33 +308,14 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
persister.removeUserSession(entity.getId(), true);
|
||||
|
||||
for (String clientSessionId : entity.getClientSessions()) {
|
||||
tx.remove(offlineSessionCache, clientSessionId);
|
||||
for (String clientUUID : entity.getAuthenticatedClientSessions().keySet()) {
|
||||
persister.removeClientSession(entity.getId(), clientUUID, true);
|
||||
}
|
||||
}
|
||||
|
||||
log.debugf("Removed %d expired offline user sessions for realm '%s'", counter, realm.getName());
|
||||
}
|
||||
|
||||
private void removeExpiredOfflineClientSessions(RealmModel realm) {
|
||||
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
int expiredOffline = Time.currentTime() - realm.getOfflineSessionIdleTimeout();
|
||||
|
||||
// Each cluster node cleanups just local sessions, which are those owned by himself (+ few more taking l1 cache into account)
|
||||
Iterator<String> itr = offlineSessionCache.getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL)
|
||||
.entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).expiredRefresh(expiredOffline)).map(Mappers.sessionId()).iterator();
|
||||
|
||||
int counter = 0;
|
||||
while (itr.hasNext()) {
|
||||
counter++;
|
||||
String sessionId = itr.next();
|
||||
tx.remove(offlineSessionCache, sessionId);
|
||||
persister.removeClientSession(sessionId, true);
|
||||
}
|
||||
|
||||
log.debugf("Removed %d expired offline client sessions for realm '%s'", counter, realm.getName());
|
||||
}
|
||||
|
||||
private void removeExpiredClientInitialAccess(RealmModel realm) {
|
||||
Iterator<String> itr = sessionCache.getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL)
|
||||
.entrySet().stream().filter(ClientInitialAccessPredicate.create(realm.getId()).expired(Time.currentTime())).map(Mappers.sessionId()).iterator();
|
||||
|
@ -454,21 +377,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
@Override
|
||||
public void onClientRemoved(RealmModel realm, ClientModel client) {
|
||||
onClientRemoved(realm, client, true);
|
||||
onClientRemoved(realm, client, false);
|
||||
}
|
||||
|
||||
private void onClientRemoved(RealmModel realm, ClientModel client, boolean offline) {
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
|
||||
Iterator<Map.Entry<String, SessionEntity>> itr = cache.entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).client(client.getId())).iterator();
|
||||
while (itr.hasNext()) {
|
||||
ClientSessionEntity entity = (ClientSessionEntity) itr.next().getValue();
|
||||
ClientSessionAdapter adapter = wrap(realm, entity, offline);
|
||||
adapter.setUserSession(null);
|
||||
|
||||
tx.remove(cache, entity.getId());
|
||||
}
|
||||
// Nothing for now. userSession.getAuthenticatedClientSessions() will check lazily if particular client exists and update userSession on-the-fly.
|
||||
}
|
||||
|
||||
|
||||
|
@ -484,55 +393,10 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
public void close() {
|
||||
}
|
||||
|
||||
void attachSession(UserSessionAdapter userSession, ClientSessionModel clientSession) {
|
||||
UserSessionEntity entity = userSession.getEntity();
|
||||
String clientSessionId = clientSession.getId();
|
||||
if (!entity.getClientSessions().contains(clientSessionId)) {
|
||||
entity.getClientSessions().add(clientSessionId);
|
||||
userSession.update();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeClientSession(RealmModel realm, ClientSessionModel clientSession) {
|
||||
removeClientSession(realm, clientSession, false);
|
||||
}
|
||||
|
||||
protected void removeClientSession(RealmModel realm, ClientSessionModel clientSession, boolean offline) {
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
|
||||
UserSessionModel userSession = clientSession.getUserSession();
|
||||
if (userSession != null) {
|
||||
UserSessionEntity entity = ((UserSessionAdapter) userSession).getEntity();
|
||||
if (entity.getClientSessions() != null) {
|
||||
entity.getClientSessions().remove(clientSession.getId());
|
||||
|
||||
}
|
||||
tx.replace(cache, entity.getId(), entity);
|
||||
}
|
||||
tx.remove(cache, clientSession.getId());
|
||||
}
|
||||
|
||||
|
||||
void dettachSession(UserSessionAdapter userSession, ClientSessionModel clientSession) {
|
||||
UserSessionEntity entity = userSession.getEntity();
|
||||
String clientSessionId = clientSession.getId();
|
||||
if (entity.getClientSessions() != null && entity.getClientSessions().contains(clientSessionId)) {
|
||||
entity.getClientSessions().remove(clientSessionId);
|
||||
userSession.update();
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeUserSession(RealmModel realm, UserSessionEntity sessionEntity, boolean offline) {
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
|
||||
tx.remove(cache, sessionEntity.getId());
|
||||
|
||||
if (sessionEntity.getClientSessions() != null) {
|
||||
for (String clientSessionId : sessionEntity.getClientSessions()) {
|
||||
tx.remove(cache, clientSessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InfinispanKeycloakTransaction getTx() {
|
||||
|
@ -560,11 +424,6 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
return models;
|
||||
}
|
||||
|
||||
ClientSessionAdapter wrap(RealmModel realm, ClientSessionEntity entity, boolean offline) {
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
return entity != null ? new ClientSessionAdapter(session, this, cache, realm, entity, offline) : null;
|
||||
}
|
||||
|
||||
ClientInitialAccessAdapter wrap(RealmModel realm, ClientInitialAccessEntity entity) {
|
||||
Cache<String, SessionEntity> cache = getCache(false);
|
||||
return entity != null ? new ClientInitialAccessAdapter(session, this, cache, realm, entity) : null;
|
||||
|
@ -574,14 +433,6 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
return entity != null ? new UserLoginFailureAdapter(this, loginFailureCache, key, entity) : null;
|
||||
}
|
||||
|
||||
List<ClientSessionModel> wrapClientSessions(RealmModel realm, Collection<ClientSessionEntity> entities, boolean offline) {
|
||||
List<ClientSessionModel> models = new LinkedList<>();
|
||||
for (ClientSessionEntity e : entities) {
|
||||
models.add(wrap(realm, e, offline));
|
||||
}
|
||||
return models;
|
||||
}
|
||||
|
||||
UserSessionEntity getUserSessionEntity(UserSessionModel userSession, boolean offline) {
|
||||
if (userSession instanceof UserSessionAdapter) {
|
||||
return ((UserSessionAdapter) userSession).getEntity();
|
||||
|
@ -594,7 +445,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
@Override
|
||||
public UserSessionModel createOfflineUserSession(UserSessionModel userSession) {
|
||||
UserSessionAdapter offlineUserSession = importUserSession(userSession, true);
|
||||
UserSessionAdapter offlineUserSession = importUserSession(userSession, true, false);
|
||||
|
||||
// started and lastSessionRefresh set to current time
|
||||
int currentTime = Time.currentTime();
|
||||
|
@ -605,7 +456,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public UserSessionModel getOfflineUserSession(RealmModel realm, String userSessionId) {
|
||||
public UserSessionAdapter getOfflineUserSession(RealmModel realm, String userSessionId) {
|
||||
return getUserSession(realm, userSessionId, true);
|
||||
}
|
||||
|
||||
|
@ -617,26 +468,19 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO:mposolda
|
||||
/*
|
||||
|
||||
|
||||
@Override
|
||||
public ClientSessionModel createOfflineClientSession(ClientSessionModel clientSession) {
|
||||
ClientSessionAdapter offlineClientSession = importClientSession(clientSession, true);
|
||||
public AuthenticatedClientSessionModel createOfflineClientSession(AuthenticatedClientSessionModel clientSession, UserSessionModel offlineUserSession) {
|
||||
UserSessionAdapter userSessionAdapter = (offlineUserSession instanceof UserSessionAdapter) ? (UserSessionAdapter) offlineUserSession :
|
||||
getOfflineUserSession(offlineUserSession.getRealm(), offlineUserSession.getId());
|
||||
|
||||
AuthenticatedClientSessionAdapter offlineClientSession = importClientSession(userSessionAdapter, clientSession);
|
||||
|
||||
// update timestamp to current time
|
||||
offlineClientSession.setTimestamp(Time.currentTime());
|
||||
|
||||
return offlineClientSession;
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public AuthenticatedClientSessionModel createOfflineClientSession(AuthenticatedClientSessionModel clientSession) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSessionModel getOfflineClientSession(RealmModel realm, String clientSessionId) {
|
||||
return getClientSession(realm, clientSessionId, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -653,12 +497,6 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
return userSessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOfflineClientSession(RealmModel realm, String clientSessionId) {
|
||||
ClientSessionModel clientSession = getOfflineClientSession(realm, clientSessionId);
|
||||
removeClientSession(realm, clientSession, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getOfflineSessionsCount(RealmModel realm, ClientModel client) {
|
||||
return getUserSessionsCount(realm, client, true);
|
||||
|
@ -670,7 +508,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public UserSessionAdapter importUserSession(UserSessionModel userSession, boolean offline) {
|
||||
public UserSessionAdapter importUserSession(UserSessionModel userSession, boolean offline, boolean importAuthenticatedClientSessions) {
|
||||
UserSessionEntity entity = new UserSessionEntity();
|
||||
entity.setId(userSession.getId());
|
||||
entity.setRealm(userSession.getRealm().getId());
|
||||
|
@ -688,34 +526,45 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
entity.setStarted(userSession.getStarted());
|
||||
entity.setLastSessionRefresh(userSession.getLastSessionRefresh());
|
||||
|
||||
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
tx.put(cache, userSession.getId(), entity);
|
||||
return wrap(userSession.getRealm(), entity, offline);
|
||||
UserSessionAdapter importedSession = wrap(userSession.getRealm(), entity, offline);
|
||||
|
||||
// Handle client sessions
|
||||
if (importAuthenticatedClientSessions) {
|
||||
for (AuthenticatedClientSessionModel clientSession : userSession.getAuthenticatedClientSessions().values()) {
|
||||
importClientSession(importedSession, clientSession);
|
||||
}
|
||||
}
|
||||
|
||||
return importedSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientSessionAdapter importClientSession(ClientSessionModel clientSession, boolean offline) {
|
||||
ClientSessionEntity entity = new ClientSessionEntity();
|
||||
entity.setId(clientSession.getId());
|
||||
entity.setRealm(clientSession.getRealm().getId());
|
||||
|
||||
private AuthenticatedClientSessionAdapter importClientSession(UserSessionAdapter importedUserSession, AuthenticatedClientSessionModel clientSession) {
|
||||
AuthenticatedClientSessionEntity entity = new AuthenticatedClientSessionEntity();
|
||||
|
||||
entity.setAction(clientSession.getAction());
|
||||
entity.setAuthenticatorStatus(clientSession.getExecutionStatus());
|
||||
entity.setAuthMethod(clientSession.getProtocol());
|
||||
if (clientSession.getAuthenticatedUser() != null) {
|
||||
entity.setAuthUserId(clientSession.getAuthenticatedUser().getId());
|
||||
}
|
||||
entity.setClient(clientSession.getClient().getId());
|
||||
|
||||
entity.setNotes(clientSession.getNotes());
|
||||
entity.setProtocolMappers(clientSession.getProtocolMappers());
|
||||
entity.setRedirectUri(clientSession.getRedirectUri());
|
||||
entity.setRoles(clientSession.getRoles());
|
||||
entity.setTimestamp(clientSession.getTimestamp());
|
||||
entity.setUserSessionNotes(clientSession.getUserSessionNotes());
|
||||
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
tx.put(cache, clientSession.getId(), entity);
|
||||
return wrap(clientSession.getRealm(), entity, offline);
|
||||
Map<String, AuthenticatedClientSessionEntity> clientSessions = importedUserSession.getEntity().getAuthenticatedClientSessions();
|
||||
if (clientSessions == null) {
|
||||
clientSessions = new HashMap<>();
|
||||
importedUserSession.getEntity().setAuthenticatedClientSessions(clientSessions);
|
||||
}
|
||||
|
||||
clientSessions.put(clientSession.getClient().getId(), entity);
|
||||
|
||||
importedUserSession.update();
|
||||
|
||||
return new AuthenticatedClientSessionAdapter(entity, clientSession.getClient(), importedUserSession, this, importedUserSession.getCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,12 +19,12 @@ package org.keycloak.models.sessions.infinispan;
|
|||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.sessions.infinispan.entities.ClientLoginSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
|
||||
|
||||
|
@ -64,15 +64,31 @@ public class UserSessionAdapter implements UserSessionModel {
|
|||
|
||||
@Override
|
||||
public Map<String, AuthenticatedClientSessionModel> getAuthenticatedClientSessions() {
|
||||
Map<String, ClientLoginSessionEntity> clientSessionEntities = entity.getClientLoginSessions();
|
||||
Map<String, AuthenticatedClientSessionEntity> clientSessionEntities = entity.getAuthenticatedClientSessions();
|
||||
Map<String, AuthenticatedClientSessionModel> result = new HashMap<>();
|
||||
|
||||
List<String> removedClientUUIDS = new LinkedList<>();
|
||||
|
||||
if (clientSessionEntities != null) {
|
||||
clientSessionEntities.forEach((String key, ClientLoginSessionEntity value) -> {
|
||||
result.put(key, new AuthenticatedClientSessionAdapter(value, this, provider, cache));
|
||||
clientSessionEntities.forEach((String key, AuthenticatedClientSessionEntity value) -> {
|
||||
// Check if client still exists
|
||||
ClientModel client = realm.getClientById(key);
|
||||
if (client != null) {
|
||||
result.put(key, new AuthenticatedClientSessionAdapter(value, client, this, provider, cache));
|
||||
} else {
|
||||
removedClientUUIDS.add(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update user session
|
||||
if (!removedClientUUIDS.isEmpty()) {
|
||||
for (String clientUUID : removedClientUUIDS) {
|
||||
entity.getAuthenticatedClientSessions().remove(clientUUID);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
|
||||
|
@ -101,6 +117,7 @@ public class UserSessionAdapter implements UserSessionModel {
|
|||
@Override
|
||||
public void setUser(UserModel user) {
|
||||
entity.setUser(user.getId());
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -180,19 +197,14 @@ public class UserSessionAdapter implements UserSessionModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<ClientSessionModel> getClientSessions() {
|
||||
if (entity.getClientSessions() != null) {
|
||||
List<ClientSessionModel> clientSessions = new LinkedList<>();
|
||||
for (String c : entity.getClientSessions()) {
|
||||
ClientSessionModel clientSession = provider.getClientSession(realm, c, offline);
|
||||
if (clientSession != null) {
|
||||
clientSessions.add(clientSession);
|
||||
}
|
||||
}
|
||||
return clientSessions;
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
public void restartSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId) {
|
||||
provider.updateSessionEntity(entity, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId);
|
||||
|
||||
entity.setState(null);
|
||||
entity.setNotes(null);
|
||||
entity.setAuthenticatedClientSessions(null);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -217,4 +229,7 @@ public class UserSessionAdapter implements UserSessionModel {
|
|||
provider.getTx().replace(cache, entity.getId(), entity);
|
||||
}
|
||||
|
||||
Cache<String, SessionEntity> getCache() {
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,9 @@ import java.util.Set;
|
|||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ClientLoginSessionEntity {
|
||||
public class AuthenticatedClientSessionEntity {
|
||||
|
||||
private String id;
|
||||
private String client;
|
||||
private String authMethod;
|
||||
private String redirectUri;
|
||||
private int timestamp;
|
||||
|
@ -44,14 +43,6 @@ public class ClientLoginSessionEntity {
|
|||
this.id = id;
|
||||
}
|
||||
|
||||
public String getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setClient(String client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public String getAuthMethod() {
|
||||
return authMethod;
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.sessions.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class ClientSessionEntity extends SessionEntity {
|
||||
|
||||
private String client;
|
||||
|
||||
private String userSession;
|
||||
|
||||
private String authMethod;
|
||||
|
||||
private String redirectUri;
|
||||
|
||||
private int timestamp;
|
||||
|
||||
private String action;
|
||||
|
||||
private Set<String> roles;
|
||||
private Set<String> protocolMappers;
|
||||
private Map<String, String> notes;
|
||||
private Map<String, String> userSessionNotes;
|
||||
private Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus = new HashMap<>();
|
||||
private String authUserId;
|
||||
private Set<String> requiredActions = new HashSet<>();
|
||||
|
||||
|
||||
public String getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setClient(String client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public String getUserSession() {
|
||||
return userSession;
|
||||
}
|
||||
|
||||
public void setUserSession(String userSession) {
|
||||
this.userSession = userSession;
|
||||
}
|
||||
|
||||
public String getAuthMethod() {
|
||||
return authMethod;
|
||||
}
|
||||
|
||||
public void setAuthMethod(String authMethod) {
|
||||
this.authMethod = authMethod;
|
||||
}
|
||||
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
public void setRedirectUri(String redirectUri) {
|
||||
this.redirectUri = redirectUri;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public Set<String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Set<String> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public Set<String> getProtocolMappers() {
|
||||
return protocolMappers;
|
||||
}
|
||||
|
||||
public void setProtocolMappers(Set<String> protocolMappers) {
|
||||
this.protocolMappers = protocolMappers;
|
||||
}
|
||||
|
||||
public Map<String, String> getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public void setNotes(Map<String, String> notes) {
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
public Map<String, ClientSessionModel.ExecutionStatus> getAuthenticatorStatus() {
|
||||
return authenticatorStatus;
|
||||
}
|
||||
|
||||
public void setAuthenticatorStatus(Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus) {
|
||||
this.authenticatorStatus = authenticatorStatus;
|
||||
}
|
||||
|
||||
public String getAuthUserId() {
|
||||
return authUserId;
|
||||
}
|
||||
|
||||
public void setAuthUserId(String authUserId) {
|
||||
this.authUserId = authUserId;
|
||||
}
|
||||
|
||||
public Map<String, String> getUserSessionNotes() {
|
||||
return userSessionNotes;
|
||||
}
|
||||
|
||||
public void setUserSessionNotes(Map<String, String> userSessionNotes) {
|
||||
this.userSessionNotes = userSessionNotes;
|
||||
}
|
||||
|
||||
public Set<String> getRequiredActions() {
|
||||
return requiredActions;
|
||||
}
|
||||
}
|
|
@ -46,13 +46,11 @@ public class UserSessionEntity extends SessionEntity {
|
|||
|
||||
private int lastSessionRefresh;
|
||||
|
||||
private Set<String> clientSessions = new CopyOnWriteArraySet<>();
|
||||
|
||||
private UserSessionModel.State state;
|
||||
|
||||
private Map<String, String> notes = new ConcurrentHashMap<>();
|
||||
|
||||
private Map<String, ClientLoginSessionEntity> clientLoginSessions;
|
||||
private Map<String, AuthenticatedClientSessionEntity> authenticatedClientSessions;
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
|
@ -110,10 +108,6 @@ public class UserSessionEntity extends SessionEntity {
|
|||
this.lastSessionRefresh = lastSessionRefresh;
|
||||
}
|
||||
|
||||
public Set<String> getClientSessions() {
|
||||
return clientSessions;
|
||||
}
|
||||
|
||||
public Map<String, String> getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
@ -122,12 +116,12 @@ public class UserSessionEntity extends SessionEntity {
|
|||
this.notes = notes;
|
||||
}
|
||||
|
||||
public Map<String, ClientLoginSessionEntity> getClientLoginSessions() {
|
||||
return clientLoginSessions;
|
||||
public Map<String, AuthenticatedClientSessionEntity> getAuthenticatedClientSessions() {
|
||||
return authenticatedClientSessions;
|
||||
}
|
||||
|
||||
public void setClientLoginSessions(Map<String, ClientLoginSessionEntity> clientLoginSessions) {
|
||||
this.clientLoginSessions = clientLoginSessions;
|
||||
public void setAuthenticatedClientSessions(Map<String, AuthenticatedClientSessionEntity> authenticatedClientSessions) {
|
||||
this.authenticatedClientSessions = authenticatedClientSessions;
|
||||
}
|
||||
|
||||
public UserSessionModel.State getState() {
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.models.sessions.infinispan.initializer;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.session.UserSessionPersisterProvider;
|
||||
|
@ -64,12 +63,7 @@ public class OfflineUserSessionLoader implements SessionLoader {
|
|||
for (UserSessionModel persistentSession : sessions) {
|
||||
|
||||
// Save to memory/infinispan
|
||||
UserSessionModel offlineUserSession = session.sessions().importUserSession(persistentSession, true);
|
||||
|
||||
for (ClientSessionModel persistentClientSession : persistentSession.getClientSessions()) {
|
||||
ClientSessionModel offlineClientSession = session.sessions().importClientSession(persistentClientSession, true);
|
||||
offlineClientSession.setUserSession(offlineUserSession);
|
||||
}
|
||||
UserSessionModel offlineUserSession = session.sessions().importUserSession(persistentSession, true, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.sessions.infinispan.mapreduce;
|
||||
|
||||
import org.infinispan.distexec.mapreduce.Collector;
|
||||
import org.infinispan.distexec.mapreduce.Mapper;
|
||||
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class ClientSessionMapper implements Mapper<String, SessionEntity, String, Object>, Serializable {
|
||||
|
||||
public ClientSessionMapper(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
private enum EmitValue {
|
||||
KEY, ENTITY, USER_SESSION_AND_TIMESTAMP
|
||||
}
|
||||
|
||||
private String realm;
|
||||
|
||||
private EmitValue emit = EmitValue.ENTITY;
|
||||
|
||||
private String client;
|
||||
|
||||
private String userSession;
|
||||
|
||||
private Long expiredRefresh;
|
||||
|
||||
private Boolean requireNullUserSession = false;
|
||||
|
||||
public static ClientSessionMapper create(String realm) {
|
||||
return new ClientSessionMapper(realm);
|
||||
}
|
||||
|
||||
public ClientSessionMapper emitKey() {
|
||||
emit = EmitValue.KEY;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientSessionMapper emitUserSessionAndTimestamp() {
|
||||
emit = EmitValue.USER_SESSION_AND_TIMESTAMP;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientSessionMapper client(String client) {
|
||||
this.client = client;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientSessionMapper userSession(String userSession) {
|
||||
this.userSession = userSession;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientSessionMapper expiredRefresh(long expiredRefresh) {
|
||||
this.expiredRefresh = expiredRefresh;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientSessionMapper requireNullUserSession(boolean requireNullUserSession) {
|
||||
this.requireNullUserSession = requireNullUserSession;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void map(String key, SessionEntity e, Collector collector) {
|
||||
if (!realm.equals(e.getRealm())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(e instanceof ClientSessionEntity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClientSessionEntity entity = (ClientSessionEntity) e;
|
||||
|
||||
if (client != null && !entity.getClient().equals(client)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (userSession != null && !userSession.equals(entity.getUserSession())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (requireNullUserSession && entity.getUserSession() != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (expiredRefresh != null && entity.getTimestamp() > expiredRefresh) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (emit) {
|
||||
case KEY:
|
||||
collector.emit(key, key);
|
||||
break;
|
||||
case ENTITY:
|
||||
collector.emit(key, entity);
|
||||
break;
|
||||
case USER_SESSION_AND_TIMESTAMP:
|
||||
if (entity.getUserSession() != null) {
|
||||
collector.emit(entity.getUserSession(), entity.getTimestamp());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.sessions.infinispan.mapreduce;
|
||||
|
||||
import org.infinispan.distexec.mapreduce.Collector;
|
||||
import org.infinispan.distexec.mapreduce.Mapper;
|
||||
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Return all clientSessions attached to any from input list of userSessions
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ClientSessionsOfUserSessionMapper implements Mapper<String, SessionEntity, String, Object>, Serializable {
|
||||
|
||||
private String realm;
|
||||
private Collection<String> userSessions;
|
||||
|
||||
private EmitValue emit = EmitValue.ENTITY;
|
||||
|
||||
private enum EmitValue {
|
||||
KEY, ENTITY
|
||||
}
|
||||
|
||||
public ClientSessionsOfUserSessionMapper(String realm, Collection<String> userSessions) {
|
||||
this.realm = realm;
|
||||
this.userSessions = userSessions;
|
||||
}
|
||||
|
||||
public ClientSessionsOfUserSessionMapper emitKey() {
|
||||
emit = EmitValue.KEY;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void map(String key, SessionEntity e, Collector<String, Object> collector) {
|
||||
if (!realm.equals(e.getRealm())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(e instanceof ClientSessionEntity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClientSessionEntity entity = (ClientSessionEntity) e;
|
||||
|
||||
if (userSessions.contains(entity.getUserSession())) {
|
||||
switch (emit) {
|
||||
case KEY:
|
||||
collector.emit(entity.getId(), entity.getId());
|
||||
break;
|
||||
case ENTITY:
|
||||
collector.emit(entity.getId(), entity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.sessions.infinispan.stream;
|
||||
|
||||
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class ClientSessionPredicate implements Predicate<Map.Entry<String, SessionEntity>>, Serializable {
|
||||
|
||||
private String realm;
|
||||
|
||||
private String client;
|
||||
|
||||
private String userSession;
|
||||
|
||||
private Long expiredRefresh;
|
||||
|
||||
private Boolean requireUserSession = false;
|
||||
|
||||
private Boolean requireNullUserSession = false;
|
||||
|
||||
private ClientSessionPredicate(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public static ClientSessionPredicate create(String realm) {
|
||||
return new ClientSessionPredicate(realm);
|
||||
}
|
||||
|
||||
public ClientSessionPredicate client(String client) {
|
||||
this.client = client;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientSessionPredicate userSession(String userSession) {
|
||||
this.userSession = userSession;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientSessionPredicate expiredRefresh(long expiredRefresh) {
|
||||
this.expiredRefresh = expiredRefresh;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientSessionPredicate requireUserSession() {
|
||||
requireUserSession = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientSessionPredicate requireNullUserSession() {
|
||||
requireNullUserSession = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Map.Entry<String, SessionEntity> entry) {
|
||||
SessionEntity e = entry.getValue();
|
||||
|
||||
if (!realm.equals(e.getRealm())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(e instanceof ClientSessionEntity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClientSessionEntity entity = (ClientSessionEntity) e;
|
||||
|
||||
if (client != null && !entity.getClient().equals(client)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (userSession != null && !userSession.equals(entity.getUserSession())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requireUserSession && entity.getUserSession() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requireNullUserSession && entity.getUserSession() != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expiredRefresh != null && entity.getTimestamp() > expiredRefresh) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -18,6 +18,8 @@
|
|||
package org.keycloak.models.sessions.infinispan.stream;
|
||||
|
||||
import org.keycloak.models.sessions.infinispan.UserSessionTimestamp;
|
||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
|
@ -38,4 +40,17 @@ public class Comparators {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static Comparator<UserSessionEntity> userSessionLastSessionRefresh() {
|
||||
return new UserSessionLastSessionRefreshComparator();
|
||||
}
|
||||
|
||||
private static class UserSessionLastSessionRefreshComparator implements Comparator<UserSessionEntity>, Serializable {
|
||||
|
||||
@Override
|
||||
public int compare(UserSessionEntity u1, UserSessionEntity u2) {
|
||||
return u1.getLastSessionRefresh() - u2.getLastSessionRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
package org.keycloak.models.sessions.infinispan.stream;
|
||||
|
||||
import org.keycloak.models.sessions.infinispan.UserSessionTimestamp;
|
||||
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
|
||||
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
|
||||
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
@ -33,10 +33,6 @@ import java.util.function.Function;
|
|||
*/
|
||||
public class Mappers {
|
||||
|
||||
public static Function<Map.Entry<String, SessionEntity>, UserSessionTimestamp> clientSessionToUserSessionTimestamp() {
|
||||
return new ClientSessionToUserSessionTimestampMapper();
|
||||
}
|
||||
|
||||
public static Function<Map.Entry<String, Optional<UserSessionTimestamp>>, UserSessionTimestamp> userSessionTimestamp() {
|
||||
return new UserSessionTimestampMapper();
|
||||
}
|
||||
|
@ -49,23 +45,14 @@ public class Mappers {
|
|||
return new SessionEntityMapper();
|
||||
}
|
||||
|
||||
public static Function<Map.Entry<String, SessionEntity>, UserSessionEntity> userSessionEntity() {
|
||||
return new UserSessionEntityMapper();
|
||||
}
|
||||
|
||||
public static Function<Map.Entry<LoginFailureKey, LoginFailureEntity>, LoginFailureKey> loginFailureId() {
|
||||
return new LoginFailureIdMapper();
|
||||
}
|
||||
|
||||
public static Function<Map.Entry<String, SessionEntity>, String> clientSessionToUserSessionId() {
|
||||
return new ClientSessionToUserSessionIdMapper();
|
||||
}
|
||||
|
||||
private static class ClientSessionToUserSessionTimestampMapper implements Function<Map.Entry<String, SessionEntity>, UserSessionTimestamp>, Serializable {
|
||||
@Override
|
||||
public UserSessionTimestamp apply(Map.Entry<String, SessionEntity> entry) {
|
||||
SessionEntity e = entry.getValue();
|
||||
ClientSessionEntity entity = (ClientSessionEntity) e;
|
||||
return new UserSessionTimestamp(entity.getUserSession(), entity.getTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
private static class UserSessionTimestampMapper implements Function<Map.Entry<String, Optional<org.keycloak.models.sessions.infinispan.UserSessionTimestamp>>, org.keycloak.models.sessions.infinispan.UserSessionTimestamp>, Serializable {
|
||||
@Override
|
||||
public org.keycloak.models.sessions.infinispan.UserSessionTimestamp apply(Map.Entry<String, Optional<org.keycloak.models.sessions.infinispan.UserSessionTimestamp>> e) {
|
||||
|
@ -87,6 +74,13 @@ public class Mappers {
|
|||
}
|
||||
}
|
||||
|
||||
private static class UserSessionEntityMapper implements Function<Map.Entry<String, SessionEntity>, UserSessionEntity>, Serializable {
|
||||
@Override
|
||||
public UserSessionEntity apply(Map.Entry<String, SessionEntity> entry) {
|
||||
return (UserSessionEntity) entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LoginFailureIdMapper implements Function<Map.Entry<LoginFailureKey, LoginFailureEntity>, LoginFailureKey>, Serializable {
|
||||
@Override
|
||||
public LoginFailureKey apply(Map.Entry<LoginFailureKey, LoginFailureEntity> entry) {
|
||||
|
@ -94,12 +88,4 @@ public class Mappers {
|
|||
}
|
||||
}
|
||||
|
||||
private static class ClientSessionToUserSessionIdMapper implements Function<Map.Entry<String, SessionEntity>, String>, Serializable {
|
||||
@Override
|
||||
public String apply(Map.Entry<String, SessionEntity> entry) {
|
||||
SessionEntity e = entry.getValue();
|
||||
ClientSessionEntity entity = (ClientSessionEntity) e;
|
||||
return entity.getUserSession();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ public class UserSessionPredicate implements Predicate<Map.Entry<String, Session
|
|||
|
||||
private String user;
|
||||
|
||||
private String client;
|
||||
|
||||
private Integer expired;
|
||||
|
||||
private Integer expiredRefresh;
|
||||
|
@ -53,6 +55,11 @@ public class UserSessionPredicate implements Predicate<Map.Entry<String, Session
|
|||
return this;
|
||||
}
|
||||
|
||||
public UserSessionPredicate client(String clientUUID) {
|
||||
this.client = clientUUID;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserSessionPredicate expired(Integer expired, Integer expiredRefresh) {
|
||||
this.expired = expired;
|
||||
this.expiredRefresh = expiredRefresh;
|
||||
|
@ -87,6 +94,10 @@ public class UserSessionPredicate implements Predicate<Map.Entry<String, Session
|
|||
return false;
|
||||
}
|
||||
|
||||
if (client != null && (entity.getAuthenticatedClientSessions() == null || !entity.getAuthenticatedClientSessions().containsKey(client))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (brokerSessionId != null && !brokerSessionId.equals(entity.getBrokerSessionId())) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.models.jpa.session;
|
|||
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -35,8 +34,9 @@ import javax.persistence.EntityManager;
|
|||
import javax.persistence.Query;
|
||||
import javax.persistence.TypedQuery;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -69,12 +69,11 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
}
|
||||
|
||||
@Override
|
||||
public void createClientSession(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession, boolean offline) {
|
||||
public void createClientSession(AuthenticatedClientSessionModel clientSession, boolean offline) {
|
||||
PersistentAuthenticatedClientSessionAdapter adapter = new PersistentAuthenticatedClientSessionAdapter(clientSession);
|
||||
PersistentClientSessionModel model = adapter.getUpdatedModel();
|
||||
|
||||
PersistentClientSessionEntity entity = new PersistentClientSessionEntity();
|
||||
entity.setClientSessionId(clientSession.getId());
|
||||
entity.setClientId(clientSession.getClient().getId());
|
||||
entity.setTimestamp(clientSession.getTimestamp());
|
||||
String offlineStr = offlineToString(offline);
|
||||
|
@ -122,9 +121,9 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
}
|
||||
|
||||
@Override
|
||||
public void removeClientSession(String clientSessionId, boolean offline) {
|
||||
public void removeClientSession(String userSessionId, String clientUUID, boolean offline) {
|
||||
String offlineStr = offlineToString(offline);
|
||||
PersistentClientSessionEntity sessionEntity = em.find(PersistentClientSessionEntity.class, new PersistentClientSessionEntity.Key(clientSessionId, offlineStr));
|
||||
PersistentClientSessionEntity sessionEntity = em.find(PersistentClientSessionEntity.class, new PersistentClientSessionEntity.Key(userSessionId, clientUUID, offlineStr));
|
||||
if (sessionEntity != null) {
|
||||
em.remove(sessionEntity);
|
||||
|
||||
|
@ -218,8 +217,6 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
userSessionIds.add(entity.getUserSessionId());
|
||||
}
|
||||
|
||||
// TODO:mposolda
|
||||
/*
|
||||
if (!userSessionIds.isEmpty()) {
|
||||
TypedQuery<PersistentClientSessionEntity> query2 = em.createNamedQuery("findClientSessionsByUserSessions", PersistentClientSessionEntity.class);
|
||||
query2.setParameter("userSessionIds", userSessionIds);
|
||||
|
@ -230,14 +227,14 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
int j = 0;
|
||||
for (UserSessionModel ss : result) {
|
||||
PersistentUserSessionAdapter userSession = (PersistentUserSessionAdapter) ss;
|
||||
List<ClientSessionModel> currentClientSessions = userSession.getClientSessions(); // This is empty now and we want to fill it
|
||||
Map<String, AuthenticatedClientSessionModel> currentClientSessions = userSession.getAuthenticatedClientSessions(); // This is empty now and we want to fill it
|
||||
|
||||
boolean next = true;
|
||||
while (next && j < clientSessions.size()) {
|
||||
PersistentClientSessionEntity clientSession = clientSessions.get(j);
|
||||
if (clientSession.getUserSessionId().equals(userSession.getId())) {
|
||||
PersistentClientSessionAdapter clientSessAdapter = toAdapter(userSession.getRealm(), userSession, clientSession);
|
||||
currentClientSessions.add(clientSessAdapter);
|
||||
PersistentAuthenticatedClientSessionAdapter clientSessAdapter = toAdapter(userSession.getRealm(), userSession, clientSession);
|
||||
currentClientSessions.put(clientSession.getClientId(), clientSessAdapter);
|
||||
j++;
|
||||
} else {
|
||||
next = false;
|
||||
|
@ -245,7 +242,7 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -256,7 +253,7 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
model.setLastSessionRefresh(entity.getLastSessionRefresh());
|
||||
model.setData(entity.getData());
|
||||
|
||||
List<ClientSessionModel> clientSessions = new LinkedList<>();
|
||||
Map<String, AuthenticatedClientSessionModel> clientSessions = new HashMap<>();
|
||||
return new PersistentUserSessionAdapter(model, realm, user, clientSessions);
|
||||
}
|
||||
|
||||
|
@ -264,7 +261,6 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
ClientModel client = realm.getClientById(entity.getClientId());
|
||||
|
||||
PersistentClientSessionModel model = new PersistentClientSessionModel();
|
||||
model.setClientSessionId(entity.getClientSessionId());
|
||||
model.setClientId(entity.getClientId());
|
||||
model.setUserSessionId(userSession.getId());
|
||||
model.setUserId(userSession.getUser().getId());
|
||||
|
|
|
@ -20,7 +20,6 @@ package org.keycloak.models.jpa.session;
|
|||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.session.UserSessionPersisterProvider;
|
||||
import org.keycloak.models.session.UserSessionPersisterProviderFactory;
|
||||
|
||||
|
|
|
@ -45,12 +45,10 @@ import java.io.Serializable;
|
|||
public class PersistentClientSessionEntity {
|
||||
|
||||
@Id
|
||||
@Column(name="CLIENT_SESSION_ID", length = 36)
|
||||
protected String clientSessionId;
|
||||
|
||||
@Column(name = "USER_SESSION_ID", length = 36)
|
||||
protected String userSessionId;
|
||||
|
||||
@Id
|
||||
@Column(name="CLIENT_ID", length = 36)
|
||||
protected String clientId;
|
||||
|
||||
|
@ -64,14 +62,6 @@ public class PersistentClientSessionEntity {
|
|||
@Column(name="DATA")
|
||||
protected String data;
|
||||
|
||||
public String getClientSessionId() {
|
||||
return clientSessionId;
|
||||
}
|
||||
|
||||
public void setClientSessionId(String clientSessionId) {
|
||||
this.clientSessionId = clientSessionId;
|
||||
}
|
||||
|
||||
public String getUserSessionId() {
|
||||
return userSessionId;
|
||||
}
|
||||
|
@ -114,20 +104,27 @@ public class PersistentClientSessionEntity {
|
|||
|
||||
public static class Key implements Serializable {
|
||||
|
||||
protected String clientSessionId;
|
||||
protected String userSessionId;
|
||||
|
||||
protected String clientId;
|
||||
|
||||
protected String offline;
|
||||
|
||||
public Key() {
|
||||
}
|
||||
|
||||
public Key(String clientSessionId, String offline) {
|
||||
this.clientSessionId = clientSessionId;
|
||||
public Key(String userSessionId, String clientId, String offline) {
|
||||
this.userSessionId = userSessionId;
|
||||
this.clientId = clientId;
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
public String getClientSessionId() {
|
||||
return clientSessionId;
|
||||
public String getUserSessionId() {
|
||||
return userSessionId;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public String getOffline() {
|
||||
|
@ -141,7 +138,8 @@ public class PersistentClientSessionEntity {
|
|||
|
||||
Key key = (Key) o;
|
||||
|
||||
if (this.clientSessionId != null ? !this.clientSessionId.equals(key.clientSessionId) : key.clientSessionId != null) return false;
|
||||
if (this.userSessionId != null ? !this.userSessionId.equals(key.userSessionId) : key.userSessionId != null) return false;
|
||||
if (this.clientId != null ? !this.clientId.equals(key.clientId) : key.clientId != null) return false;
|
||||
if (this.offline != null ? !this.offline.equals(key.offline) : key.offline != null) return false;
|
||||
|
||||
return true;
|
||||
|
@ -149,7 +147,8 @@ public class PersistentClientSessionEntity {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.clientSessionId != null ? this.clientSessionId.hashCode() : 0;
|
||||
int result = this.userSessionId != null ? this.userSessionId.hashCode() : 0;
|
||||
result = 37 * result + (this.clientId != null ? this.clientId.hashCode() : 0);
|
||||
result = 31 * result + (this.offline != null ? this.offline.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
|
||||
<changeSet author="mposolda@redhat.com" id="3.2.0">
|
||||
<dropPrimaryKey constraintName="CONSTRAINT_OFFLINE_CL_SES_PK2" tableName="OFFLINE_CLIENT_SESSION" />
|
||||
<dropColumn tableName="OFFLINE_CLIENT_SESSION" columnName="CLIENT_SESSION_ID" />
|
||||
<addPrimaryKey columnNames="USER_SESSION_ID,CLIENT_ID, OFFLINE_FLAG" constraintName="CONSTRAINT_OFFL_CL_SES_PK3" tableName="OFFLINE_CLIENT_SESSION"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
|
@ -47,4 +47,5 @@
|
|||
<include file="META-INF/jpa-changelog-2.5.0.xml"/>
|
||||
<include file="META-INF/jpa-changelog-2.5.1.xml"/>
|
||||
<include file="META-INF/jpa-changelog-3.0.0.xml"/>
|
||||
<include file="META-INF/jpa-changelog-3.2.0.xml"/>
|
||||
</databaseChangeLog>
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
|
|
|
@ -78,8 +78,6 @@ public interface LoginFormsProvider extends Provider {
|
|||
|
||||
public LoginFormsProvider setClientSessionCode(String accessCode);
|
||||
|
||||
public LoginFormsProvider setAuthenticationSession(AuthenticationSessionModel authenticationSession);
|
||||
|
||||
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String,RoleModel> resourceRolesRequested, List<ProtocolMapperModel> protocolMappers);
|
||||
public LoginFormsProvider setAccessRequest(String message);
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ public class DisabledUserSessionPersisterProvider implements UserSessionPersiste
|
|||
}
|
||||
|
||||
@Override
|
||||
public void createClientSession(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession, boolean offline) {
|
||||
public void createClientSession(AuthenticatedClientSessionModel clientSession, boolean offline) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ public class DisabledUserSessionPersisterProvider implements UserSessionPersiste
|
|||
}
|
||||
|
||||
@Override
|
||||
public void removeClientSession(String clientSessionId, boolean offline) {
|
||||
public void removeClientSession(String userSessionId, String clientUUID, boolean offline) {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ package org.keycloak.models.session;
|
|||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
@ -55,10 +54,7 @@ public class PersistentAuthenticatedClientSessionAdapter implements Authenticate
|
|||
|
||||
model = new PersistentClientSessionModel();
|
||||
model.setClientId(clientSession.getClient().getId());
|
||||
model.setClientSessionId(clientSession.getId());
|
||||
if (clientSession.getUserSession() != null) {
|
||||
model.setUserId(clientSession.getUserSession().getUser().getId());
|
||||
}
|
||||
model.setUserId(clientSession.getUserSession().getUser().getId());
|
||||
model.setUserSessionId(clientSession.getUserSession().getId());
|
||||
model.setTimestamp(clientSession.getTimestamp());
|
||||
|
||||
|
@ -101,7 +97,7 @@ public class PersistentAuthenticatedClientSessionAdapter implements Authenticate
|
|||
|
||||
@Override
|
||||
public String getId() {
|
||||
return model.getClientSessionId();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -194,7 +190,7 @@ public class PersistentAuthenticatedClientSessionAdapter implements Authenticate
|
|||
public void setNote(String name, String value) {
|
||||
PersistentClientSessionData entity = getData();
|
||||
if (entity.getNotes() == null) {
|
||||
entity.setNotes(new HashMap<String, String>());
|
||||
entity.setNotes(new HashMap<>());
|
||||
}
|
||||
entity.getNotes().put(name, value);
|
||||
}
|
||||
|
@ -214,13 +210,12 @@ public class PersistentAuthenticatedClientSessionAdapter implements Authenticate
|
|||
return entity.getNotes();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || !(o instanceof ClientSessionModel)) return false;
|
||||
if (o == null || !(o instanceof AuthenticatedClientSessionModel)) return false;
|
||||
|
||||
ClientSessionModel that = (ClientSessionModel) o;
|
||||
AuthenticatedClientSessionModel that = (AuthenticatedClientSessionModel) o;
|
||||
return that.getId().equals(getId());
|
||||
}
|
||||
|
||||
|
|
|
@ -22,20 +22,12 @@ package org.keycloak.models.session;
|
|||
*/
|
||||
public class PersistentClientSessionModel {
|
||||
|
||||
private String clientSessionId;
|
||||
private String userSessionId;
|
||||
private String clientId;
|
||||
private String userId;
|
||||
private int timestamp;
|
||||
private String data;
|
||||
|
||||
public String getClientSessionId() {
|
||||
return clientSessionId;
|
||||
}
|
||||
|
||||
public void setClientSessionId(String clientSessionId) {
|
||||
this.clientSessionId = clientSessionId;
|
||||
}
|
||||
|
||||
public String getUserSessionId() {
|
||||
return userSessionId;
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.models.session;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -28,7 +27,6 @@ import org.keycloak.util.JsonSerialization;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -39,7 +37,7 @@ public class PersistentUserSessionAdapter implements UserSessionModel {
|
|||
private final PersistentUserSessionModel model;
|
||||
private final UserModel user;
|
||||
private final RealmModel realm;
|
||||
private final List<ClientSessionModel> clientSessions;
|
||||
private final Map<String, AuthenticatedClientSessionModel> authenticatedClientSessions;
|
||||
|
||||
private PersistentUserSessionData data;
|
||||
|
||||
|
@ -60,14 +58,14 @@ public class PersistentUserSessionAdapter implements UserSessionModel {
|
|||
|
||||
this.user = other.getUser();
|
||||
this.realm = other.getRealm();
|
||||
this.clientSessions = other.getClientSessions();
|
||||
this.authenticatedClientSessions = other.getAuthenticatedClientSessions();
|
||||
}
|
||||
|
||||
public PersistentUserSessionAdapter(PersistentUserSessionModel model, RealmModel realm, UserModel user, List<ClientSessionModel> clientSessions) {
|
||||
public PersistentUserSessionAdapter(PersistentUserSessionModel model, RealmModel realm, UserModel user, Map<String, AuthenticatedClientSessionModel> clientSessions) {
|
||||
this.model = model;
|
||||
this.realm = realm;
|
||||
this.user = user;
|
||||
this.clientSessions = clientSessions;
|
||||
this.authenticatedClientSessions = clientSessions;
|
||||
}
|
||||
|
||||
// Lazily init data
|
||||
|
@ -160,15 +158,9 @@ public class PersistentUserSessionAdapter implements UserSessionModel {
|
|||
model.setLastSessionRefresh(seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientSessionModel> getClientSessions() {
|
||||
return clientSessions;
|
||||
}
|
||||
|
||||
// TODO:mposolda
|
||||
@Override
|
||||
public Map<String, AuthenticatedClientSessionModel> getAuthenticatedClientSessions() {
|
||||
return null;
|
||||
return authenticatedClientSessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -208,6 +200,11 @@ public class PersistentUserSessionAdapter implements UserSessionModel {
|
|||
getData().setState(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restartSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId) {
|
||||
throw new IllegalStateException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -35,7 +35,7 @@ public interface UserSessionPersisterProvider extends Provider {
|
|||
void createUserSession(UserSessionModel userSession, boolean offline);
|
||||
|
||||
// Assuming that corresponding userSession is already persisted
|
||||
void createClientSession(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession, boolean offline);
|
||||
void createClientSession(AuthenticatedClientSessionModel clientSession, boolean offline);
|
||||
|
||||
void updateUserSession(UserSessionModel userSession, boolean offline);
|
||||
|
||||
|
@ -43,7 +43,7 @@ public interface UserSessionPersisterProvider extends Provider {
|
|||
void removeUserSession(String userSessionId, boolean offline);
|
||||
|
||||
// Called during revoke. It will remove userSession too if this was last clientSession attached to it
|
||||
void removeClientSession(String clientSessionId, boolean offline);
|
||||
void removeClientSession(String userSessionId, String clientUUID, boolean offline);
|
||||
|
||||
void onRealmRemoved(RealmModel realm);
|
||||
void onClientRemoved(RealmModel realm, ClientModel client);
|
||||
|
|
|
@ -30,8 +30,8 @@ public interface AuthenticatedClientSessionModel extends CommonClientSessionMode
|
|||
void setUserSession(UserSessionModel userSession);
|
||||
UserSessionModel getUserSession();
|
||||
|
||||
public String getNote(String name);
|
||||
public void setNote(String name, String value);
|
||||
public void removeNote(String name);
|
||||
public Map<String, String> getNotes();
|
||||
String getNote(String name);
|
||||
void setNote(String name, String value);
|
||||
void removeNote(String name);
|
||||
Map<String, String> getNotes();
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
package org.keycloak.models;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -55,9 +54,6 @@ public interface UserSessionModel {
|
|||
|
||||
Map<String, AuthenticatedClientSessionModel> getAuthenticatedClientSessions();
|
||||
|
||||
// TODO: Remove
|
||||
List<ClientSessionModel> getClientSessions();
|
||||
|
||||
public String getNote(String name);
|
||||
public void setNote(String name, String value);
|
||||
public void removeNote(String name);
|
||||
|
@ -68,8 +64,10 @@ public interface UserSessionModel {
|
|||
|
||||
void setUser(UserModel user);
|
||||
|
||||
// Will completely restart whole state of user session. It will just keep same ID.
|
||||
void restartSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId);
|
||||
|
||||
public static enum State {
|
||||
LOGGING_IN, // TODO:mposolda Maybe state "LOGGING_IN" is useless now once userSession is attached after requiredActions
|
||||
LOGGED_IN,
|
||||
LOGGING_OUT,
|
||||
LOGGED_OUT
|
||||
|
|
|
@ -27,10 +27,7 @@ import java.util.List;
|
|||
*/
|
||||
public interface UserSessionProvider extends Provider {
|
||||
|
||||
ClientSessionModel createClientSession(RealmModel realm, ClientModel client);
|
||||
AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession);
|
||||
ClientSessionModel getClientSession(RealmModel realm, String id);
|
||||
ClientSessionModel getClientSession(String id);
|
||||
|
||||
UserSessionModel createUserSession(String id, RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId);
|
||||
UserSessionModel getUserSession(RealmModel realm, String id);
|
||||
|
@ -42,14 +39,13 @@ public interface UserSessionProvider extends Provider {
|
|||
|
||||
long getActiveUserSessions(RealmModel realm, ClientModel client);
|
||||
|
||||
// This will remove attached ClientLoginSessionModels too
|
||||
/** This will remove attached ClientLoginSessionModels too **/
|
||||
void removeUserSession(RealmModel realm, UserSessionModel session);
|
||||
void removeUserSessions(RealmModel realm, UserModel user);
|
||||
|
||||
// Implementation should propagate removal of expired userSessions to userSessionPersister too
|
||||
/** Implementation should propagate removal of expired userSessions to userSessionPersister too **/
|
||||
void removeExpired(RealmModel realm);
|
||||
void removeUserSessions(RealmModel realm);
|
||||
void removeClientSession(RealmModel realm, ClientSessionModel clientSession);
|
||||
|
||||
UserLoginFailureModel getUserLoginFailure(RealmModel realm, String userId);
|
||||
UserLoginFailureModel addUserLoginFailure(RealmModel realm, String userId);
|
||||
|
@ -59,25 +55,22 @@ public interface UserSessionProvider extends Provider {
|
|||
void onRealmRemoved(RealmModel realm);
|
||||
void onClientRemoved(RealmModel realm, ClientModel client);
|
||||
|
||||
/** Newly created userSession won't contain attached AuthenticatedClientSessions **/
|
||||
UserSessionModel createOfflineUserSession(UserSessionModel userSession);
|
||||
UserSessionModel getOfflineUserSession(RealmModel realm, String userSessionId);
|
||||
|
||||
// Removes the attached clientSessions as well
|
||||
/** Removes the attached clientSessions as well **/
|
||||
void removeOfflineUserSession(RealmModel realm, UserSessionModel userSession);
|
||||
|
||||
AuthenticatedClientSessionModel createOfflineClientSession(AuthenticatedClientSessionModel clientSession);
|
||||
ClientSessionModel getOfflineClientSession(RealmModel realm, String clientSessionId);
|
||||
/** Will automatically attach newly created offline client session to the offlineUserSession **/
|
||||
AuthenticatedClientSessionModel createOfflineClientSession(AuthenticatedClientSessionModel clientSession, UserSessionModel offlineUserSession);
|
||||
List<UserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user);
|
||||
|
||||
// Don't remove userSession even if it's last userSession
|
||||
void removeOfflineClientSession(RealmModel realm, String clientSessionId);
|
||||
|
||||
long getOfflineSessionsCount(RealmModel realm, ClientModel client);
|
||||
List<UserSessionModel> getOfflineUserSessions(RealmModel realm, ClientModel client, int first, int max);
|
||||
|
||||
// Triggered by persister during pre-load
|
||||
UserSessionModel importUserSession(UserSessionModel persistentUserSession, boolean offline);
|
||||
ClientSessionModel importClientSession(ClientSessionModel persistentClientSession, boolean offline);
|
||||
/** Triggered by persister during pre-load. It optionally imports authenticatedClientSessions too if requested. Otherwise the imported UserSession will have empty list of AuthenticationSessionModel **/
|
||||
UserSessionModel importUserSession(UserSessionModel persistentUserSession, boolean offline, boolean importAuthenticatedClientSessions);
|
||||
|
||||
ClientInitialAccessModel createClientInitialAccessModel(RealmModel realm, int expiration, int count);
|
||||
ClientInitialAccessModel getClientInitialAccessModel(RealmModel realm, String id);
|
||||
|
|
|
@ -35,7 +35,6 @@ public interface AuthenticationSessionProvider extends Provider {
|
|||
|
||||
void removeAuthenticationSession(RealmModel realm, AuthenticationSessionModel authenticationSession);
|
||||
|
||||
// TODO: test and add to scheduler
|
||||
void removeExpired(RealmModel realm);
|
||||
void onRealmRemoved(RealmModel realm);
|
||||
void onClientRemoved(RealmModel realm, ClientModel client);
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.keycloak.models.AuthenticationFlowModel;
|
|||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -53,11 +52,11 @@ import org.keycloak.services.managers.ClientSessionCode;
|
|||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.LoginActionsService;
|
||||
import org.keycloak.services.util.CacheControlUtil;
|
||||
import org.keycloak.services.util.PageExpiredRedirect;
|
||||
import org.keycloak.services.util.AuthenticationFlowURLHelper;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.CommonClientSessionModel;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
|
@ -73,7 +72,6 @@ public class AuthenticationProcessor {
|
|||
public static final String LAST_PROCESSED_EXECUTION = "last.processed.execution";
|
||||
public static final String CURRENT_FLOW_PATH = "current.flow.path";
|
||||
public static final String FORKED_FROM = "forked.from";
|
||||
public static final String FORWARDED_ERROR_MESSAGE_NOTE = "forwardedErrorMessage";
|
||||
|
||||
public static final String BROKER_SESSION_ID = "broker.session.id";
|
||||
public static final String BROKER_USER_ID = "broker.user.id";
|
||||
|
@ -595,9 +593,9 @@ public class AuthenticationProcessor {
|
|||
}
|
||||
|
||||
public boolean isSuccessful(AuthenticationExecutionModel model) {
|
||||
ClientSessionModel.ExecutionStatus status = authenticationSession.getExecutionStatus().get(model.getId());
|
||||
AuthenticationSessionModel.ExecutionStatus status = authenticationSession.getExecutionStatus().get(model.getId());
|
||||
if (status == null) return false;
|
||||
return status == ClientSessionModel.ExecutionStatus.SUCCESS;
|
||||
return status == AuthenticationSessionModel.ExecutionStatus.SUCCESS;
|
||||
}
|
||||
|
||||
public Response handleBrowserException(Exception failure) {
|
||||
|
@ -629,7 +627,7 @@ public class AuthenticationProcessor {
|
|||
} else if (e.getError() == AuthenticationFlowError.FORK_FLOW) {
|
||||
ForkFlowException reset = (ForkFlowException)e;
|
||||
AuthenticationSessionModel clone = clone(session, authenticationSession);
|
||||
clone.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
clone.setAction(AuthenticationSessionModel.Action.AUTHENTICATE.name());
|
||||
setAuthenticationSession(clone);
|
||||
|
||||
AuthenticationProcessor processor = new AuthenticationProcessor();
|
||||
|
@ -726,9 +724,9 @@ public class AuthenticationProcessor {
|
|||
|
||||
|
||||
public Response redirectToFlow() {
|
||||
URI redirect = new PageExpiredRedirect(session, realm, uriInfo).getLastExecutionUrl(authenticationSession);
|
||||
URI redirect = new AuthenticationFlowURLHelper(session, realm, uriInfo).getLastExecutionUrl(authenticationSession);
|
||||
|
||||
logger.info("Redirecting to URL: " + redirect.toString());
|
||||
logger.debug("Redirecting to URL: " + redirect.toString());
|
||||
|
||||
return Response.status(302).location(redirect).build();
|
||||
|
||||
|
@ -750,6 +748,8 @@ public class AuthenticationProcessor {
|
|||
authSession.clearUserSessionNotes();
|
||||
authSession.clearAuthNotes();
|
||||
|
||||
authSession.setAction(CommonClientSessionModel.Action.AUTHENTICATE.name());
|
||||
|
||||
authSession.setAuthNote(CURRENT_FLOW_PATH, flowPath);
|
||||
}
|
||||
|
||||
|
@ -766,7 +766,7 @@ public class AuthenticationProcessor {
|
|||
clone.setTimestamp(Time.currentTime());
|
||||
|
||||
clone.setAuthNote(FORKED_FROM, authSession.getId());
|
||||
logger.infof("Forked authSession %s from authSession %s", clone.getId(), authSession.getId());
|
||||
logger.debugf("Forked authSession %s from authSession %s", clone.getId(), authSession.getId());
|
||||
|
||||
return clone;
|
||||
|
||||
|
@ -777,10 +777,9 @@ public class AuthenticationProcessor {
|
|||
logger.debug("authenticationAction");
|
||||
checkClientSession(true);
|
||||
String current = authenticationSession.getAuthNote(CURRENT_AUTHENTICATION_EXECUTION);
|
||||
if (!execution.equals(current)) {
|
||||
// TODO:mposolda debug
|
||||
logger.info("Current execution does not equal executed execution. Might be a page refresh");
|
||||
return new PageExpiredRedirect(session, realm, uriInfo).showPageExpired(authenticationSession);
|
||||
if (execution == null || !execution.equals(current)) {
|
||||
logger.debug("Current execution does not equal executed execution. Might be a page refresh");
|
||||
return new AuthenticationFlowURLHelper(session, realm, uriInfo).showPageExpired(authenticationSession);
|
||||
}
|
||||
UserModel authUser = authenticationSession.getAuthenticatedUser();
|
||||
validateUser(authUser);
|
||||
|
@ -812,7 +811,7 @@ public class AuthenticationProcessor {
|
|||
ClientSessionCode code = new ClientSessionCode(session, realm, authenticationSession);
|
||||
|
||||
if (checkAction) {
|
||||
String action = ClientSessionModel.Action.AUTHENTICATE.name();
|
||||
String action = AuthenticationSessionModel.Action.AUTHENTICATE.name();
|
||||
if (!code.isValidAction(action)) {
|
||||
throw new AuthenticationFlowException(AuthenticationFlowError.INVALID_CLIENT_SESSION);
|
||||
}
|
||||
|
@ -862,26 +861,29 @@ public class AuthenticationProcessor {
|
|||
if (attemptedUsername != null) username = attemptedUsername;
|
||||
String rememberMe = authSession.getAuthNote(Details.REMEMBER_ME);
|
||||
boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("true");
|
||||
String brokerSessionId = authSession.getAuthNote(BROKER_SESSION_ID);
|
||||
String brokerUserId = authSession.getAuthNote(BROKER_USER_ID);
|
||||
|
||||
if (userSession == null) { // if no authenticator attached a usersession
|
||||
|
||||
userSession = session.sessions().getUserSession(realm, authSession.getId());
|
||||
if (userSession == null) {
|
||||
String brokerSessionId = authSession.getAuthNote(BROKER_SESSION_ID);
|
||||
String brokerUserId = authSession.getAuthNote(BROKER_USER_ID);
|
||||
userSession = session.sessions().createUserSession(authSession.getId(), realm, authSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), authSession.getProtocol()
|
||||
, remember, brokerSessionId, brokerUserId);
|
||||
} else if (userSession.getUser() == null || !AuthenticationManager.isSessionValid(realm, userSession)) {
|
||||
userSession.restartSession(realm, authSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), authSession.getProtocol()
|
||||
, remember, brokerSessionId, brokerUserId);
|
||||
} else {
|
||||
// We have existing userSession even if it wasn't attached to authenticator. Could happen if SSO authentication was ignored (eg. prompt=login) and in some other cases.
|
||||
// We need to handle case when different user was used and update that (TODO:mposolda evaluate this again and corner cases like token refresh etc. AND ROLES!!! LIKELY ERROR SHOULD BE SHOWN IF ATTEMPT TO AUTHENTICATE AS DIFFERENT USER)
|
||||
logger.info("No SSO login, but found existing userSession with ID '%s' after finished authentication.");
|
||||
// We need to handle case when different user was used
|
||||
logger.debugf("No SSO login, but found existing userSession with ID '%s' after finished authentication.", userSession.getId());
|
||||
if (!authSession.getAuthenticatedUser().equals(userSession.getUser())) {
|
||||
event.detail(Details.EXISTING_USER, userSession.getUser().getId());
|
||||
event.error(Errors.DIFFERENT_USER_AUTHENTICATED);
|
||||
throw new ErrorPageException(session, Messages.DIFFERENT_USER_AUTHENTICATED, userSession.getUser().getUsername());
|
||||
}
|
||||
}
|
||||
userSession.setState(UserSessionModel.State.LOGGING_IN);
|
||||
userSession.setState(UserSessionModel.State.LOGGED_IN);
|
||||
}
|
||||
|
||||
if (remember) {
|
||||
|
|
|
@ -20,9 +20,9 @@ package org.keycloak.authentication;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Iterator;
|
||||
|
@ -51,11 +51,11 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
|||
|
||||
protected boolean isProcessed(AuthenticationExecutionModel model) {
|
||||
if (model.isDisabled()) return true;
|
||||
ClientSessionModel.ExecutionStatus status = processor.getAuthenticationSession().getExecutionStatus().get(model.getId());
|
||||
AuthenticationSessionModel.ExecutionStatus status = processor.getAuthenticationSession().getExecutionStatus().get(model.getId());
|
||||
if (status == null) return false;
|
||||
return status == ClientSessionModel.ExecutionStatus.SUCCESS || status == ClientSessionModel.ExecutionStatus.SKIPPED
|
||||
|| status == ClientSessionModel.ExecutionStatus.ATTEMPTED
|
||||
|| status == ClientSessionModel.ExecutionStatus.SETUP_REQUIRED;
|
||||
return status == AuthenticationSessionModel.ExecutionStatus.SUCCESS || status == AuthenticationSessionModel.ExecutionStatus.SKIPPED
|
||||
|| status == AuthenticationSessionModel.ExecutionStatus.ATTEMPTED
|
||||
|| status == AuthenticationSessionModel.ExecutionStatus.SETUP_REQUIRED;
|
||||
}
|
||||
|
||||
|
||||
|
@ -75,7 +75,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
|||
AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
|
||||
Response flowChallenge = authenticationFlow.processAction(actionExecution);
|
||||
if (flowChallenge == null) {
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.SUCCESS);
|
||||
if (model.isAlternative()) alternativeSuccessful = true;
|
||||
return processFlow();
|
||||
} else {
|
||||
|
@ -115,7 +115,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
|||
}
|
||||
if (model.isAlternative() && alternativeSuccessful) {
|
||||
logger.debug("Skip alternative execution");
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.SKIPPED);
|
||||
continue;
|
||||
}
|
||||
if (model.isAuthenticatorFlow()) {
|
||||
|
@ -123,7 +123,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
|||
AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
|
||||
Response flowChallenge = authenticationFlow.processFlow();
|
||||
if (flowChallenge == null) {
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.SUCCESS);
|
||||
if (model.isAlternative()) alternativeSuccessful = true;
|
||||
continue;
|
||||
} else {
|
||||
|
@ -131,13 +131,13 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
|||
alternativeChallenge = flowChallenge;
|
||||
challengedAlternativeExecution = model;
|
||||
} else if (model.isRequired()) {
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.CHALLENGED);
|
||||
return flowChallenge;
|
||||
} else if (model.isOptional()) {
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.SKIPPED);
|
||||
continue;
|
||||
} else {
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.SKIPPED);
|
||||
continue;
|
||||
}
|
||||
return flowChallenge;
|
||||
|
@ -154,7 +154,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
|||
|
||||
if (authenticator.requiresUser() && authUser == null) {
|
||||
if (alternativeChallenge != null) {
|
||||
processor.getAuthenticationSession().setExecutionStatus(challengedAlternativeExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(challengedAlternativeExecution.getId(), AuthenticationSessionModel.ExecutionStatus.CHALLENGED);
|
||||
return alternativeChallenge;
|
||||
}
|
||||
throw new AuthenticationFlowException("authenticator: " + factory.getId(), AuthenticationFlowError.UNKNOWN_USER);
|
||||
|
@ -166,14 +166,14 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
|||
if (model.isRequired()) {
|
||||
if (factory.isUserSetupAllowed()) {
|
||||
logger.debugv("authenticator SETUP_REQUIRED: {0}", factory.getId());
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.SETUP_REQUIRED);
|
||||
authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser());
|
||||
continue;
|
||||
} else {
|
||||
throw new AuthenticationFlowException(AuthenticationFlowError.CREDENTIAL_SETUP_REQUIRED);
|
||||
}
|
||||
} else if (model.isOptional()) {
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.SKIPPED);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -198,13 +198,13 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
|||
switch (status) {
|
||||
case SUCCESS:
|
||||
logger.debugv("authenticator SUCCESS: {0}", execution.getAuthenticator());
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.SUCCESS);
|
||||
if (execution.isAlternative()) alternativeSuccessful = true;
|
||||
return null;
|
||||
case FAILED:
|
||||
logger.debugv("authenticator FAILED: {0}", execution.getAuthenticator());
|
||||
processor.logFailure();
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.FAILED);
|
||||
if (result.getChallenge() != null) {
|
||||
return sendChallenge(result, execution);
|
||||
}
|
||||
|
@ -214,37 +214,37 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
|||
processor.getAuthenticationSession().setAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
|
||||
throw new ForkFlowException(result.getSuccessMessage(), result.getErrorMessage());
|
||||
case FORCE_CHALLENGE:
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.CHALLENGED);
|
||||
return sendChallenge(result, execution);
|
||||
case CHALLENGE:
|
||||
logger.debugv("authenticator CHALLENGE: {0}", execution.getAuthenticator());
|
||||
if (execution.isRequired()) {
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.CHALLENGED);
|
||||
return sendChallenge(result, execution);
|
||||
}
|
||||
UserModel authenticatedUser = processor.getAuthenticationSession().getAuthenticatedUser();
|
||||
if (execution.isOptional() && authenticatedUser != null && result.getAuthenticator().configuredFor(processor.getSession(), processor.getRealm(), authenticatedUser)) {
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.CHALLENGED);
|
||||
return sendChallenge(result, execution);
|
||||
}
|
||||
if (execution.isAlternative()) {
|
||||
alternativeChallenge = result.getChallenge();
|
||||
challengedAlternativeExecution = execution;
|
||||
} else {
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.SKIPPED);
|
||||
}
|
||||
return null;
|
||||
case FAILURE_CHALLENGE:
|
||||
logger.debugv("authenticator FAILURE_CHALLENGE: {0}", execution.getAuthenticator());
|
||||
processor.logFailure();
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.CHALLENGED);
|
||||
return sendChallenge(result, execution);
|
||||
case ATTEMPTED:
|
||||
logger.debugv("authenticator ATTEMPTED: {0}", execution.getAuthenticator());
|
||||
if (execution.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
|
||||
throw new AuthenticationFlowException(AuthenticationFlowError.INVALID_CREDENTIALS);
|
||||
}
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
|
||||
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.ATTEMPTED);
|
||||
return null;
|
||||
case FLOW_RESET:
|
||||
processor.resetFlow();
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.keycloak.events.EventBuilder;
|
|||
import org.keycloak.forms.login.LoginFormsProvider;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -167,13 +166,13 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
|
|||
if (!actionExecution.equals(formExecution.getId())) {
|
||||
throw new AuthenticationFlowException("action is not current execution", AuthenticationFlowError.INTERNAL_ERROR);
|
||||
}
|
||||
Map<String, ClientSessionModel.ExecutionStatus> executionStatus = new HashMap<>();
|
||||
Map<String, AuthenticationSessionModel.ExecutionStatus> executionStatus = new HashMap<>();
|
||||
List<FormAction> requiredActions = new LinkedList<>();
|
||||
List<ValidationContextImpl> successes = new LinkedList<>();
|
||||
List<ValidationContextImpl> errors = new LinkedList<>();
|
||||
for (AuthenticationExecutionModel formActionExecution : formActionExecutions) {
|
||||
if (!formActionExecution.isEnabled()) {
|
||||
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||
executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SKIPPED);
|
||||
continue;
|
||||
}
|
||||
FormActionFactory factory = (FormActionFactory)processor.getSession().getKeycloakSessionFactory().getProviderFactory(FormAction.class, formActionExecution.getAuthenticator());
|
||||
|
@ -190,14 +189,14 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
|
|||
if (formActionExecution.isRequired()) {
|
||||
if (factory.isUserSetupAllowed()) {
|
||||
AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", formExecution.getAuthenticator());
|
||||
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
|
||||
executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SETUP_REQUIRED);
|
||||
requiredActions.add(action);
|
||||
continue;
|
||||
} else {
|
||||
throw new AuthenticationFlowException(AuthenticationFlowError.CREDENTIAL_SETUP_REQUIRED);
|
||||
}
|
||||
} else if (formActionExecution.isOptional()) {
|
||||
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||
executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SKIPPED);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -206,10 +205,10 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
|
|||
ValidationContextImpl result = new ValidationContextImpl(formActionExecution, action);
|
||||
action.validate(result);
|
||||
if (result.success) {
|
||||
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
|
||||
executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SUCCESS);
|
||||
successes.add(result);
|
||||
} else {
|
||||
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||
executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.CHALLENGED);
|
||||
errors.add(result);
|
||||
}
|
||||
}
|
||||
|
@ -235,14 +234,14 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
|
|||
context.action.success(context);
|
||||
}
|
||||
// set status and required actions only if form is fully successful
|
||||
for (Map.Entry<String, ClientSessionModel.ExecutionStatus> entry : executionStatus.entrySet()) {
|
||||
for (Map.Entry<String, AuthenticationSessionModel.ExecutionStatus> entry : executionStatus.entrySet()) {
|
||||
processor.getAuthenticationSession().setExecutionStatus(entry.getKey(), entry.getValue());
|
||||
}
|
||||
for (FormAction action : requiredActions) {
|
||||
action.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser());
|
||||
|
||||
}
|
||||
processor.getAuthenticationSession().setExecutionStatus(actionExecution, ClientSessionModel.ExecutionStatus.SUCCESS);
|
||||
processor.getAuthenticationSession().setExecutionStatus(actionExecution, AuthenticationSessionModel.ExecutionStatus.SUCCESS);
|
||||
processor.getAuthenticationSession().removeAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -20,12 +20,14 @@ import org.keycloak.TokenVerifier.Predicate;
|
|||
import org.keycloak.authentication.AuthenticationProcessor;
|
||||
import org.keycloak.authentication.actiontoken.*;
|
||||
import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator;
|
||||
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.managers.AuthenticationSessionManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.LoginActionsService;
|
||||
import org.keycloak.services.resources.LoginActionsServiceChecks.IsActionRequired;
|
||||
import org.keycloak.sessions.CommonClientSessionModel.Action;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
@ -61,7 +63,7 @@ public class ResetCredentialsActionTokenHandler extends AbstractActionTokenHande
|
|||
|
||||
@Override
|
||||
public Response handleToken(ResetCredentialsActionToken token, ActionTokenContext tokenContext, ProcessFlow processFlow) {
|
||||
AuthenticationProcessor authProcessor = new ResetCredsAuthenticationProcessor(tokenContext);
|
||||
AuthenticationProcessor authProcessor = new ResetCredsAuthenticationProcessor();
|
||||
|
||||
return processFlow.processFlow(
|
||||
false,
|
||||
|
@ -87,34 +89,31 @@ public class ResetCredentialsActionTokenHandler extends AbstractActionTokenHande
|
|||
|
||||
public static class ResetCredsAuthenticationProcessor extends AuthenticationProcessor {
|
||||
|
||||
private final ActionTokenContext tokenContext;
|
||||
|
||||
public ResetCredsAuthenticationProcessor(ActionTokenContext tokenContext) {
|
||||
this.tokenContext = tokenContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Response authenticationComplete() {
|
||||
boolean firstBrokerLoginInProgress = (tokenContext.getAuthenticationSession().getAuthNote(AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE) != null);
|
||||
boolean firstBrokerLoginInProgress = (authenticationSession.getAuthNote(AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE) != null);
|
||||
if (firstBrokerLoginInProgress) {
|
||||
|
||||
UserModel linkingUser = AbstractIdpAuthenticator.getExistingUser(session, tokenContext.getRealm(), tokenContext.getAuthenticationSession());
|
||||
if (!linkingUser.getId().equals(tokenContext.getAuthenticationSession().getAuthenticatedUser().getId())) {
|
||||
UserModel linkingUser = AbstractIdpAuthenticator.getExistingUser(session, realm, authenticationSession);
|
||||
if (!linkingUser.getId().equals(authenticationSession.getAuthenticatedUser().getId())) {
|
||||
return ErrorPage.error(session,
|
||||
Messages.IDENTITY_PROVIDER_DIFFERENT_USER_MESSAGE,
|
||||
tokenContext.getAuthenticationSession().getAuthenticatedUser().getUsername(),
|
||||
authenticationSession.getAuthenticatedUser().getUsername(),
|
||||
linkingUser.getUsername()
|
||||
);
|
||||
}
|
||||
|
||||
logger.debugf("Forget-password flow finished when authenticated user '%s' after first broker login.", linkingUser.getUsername());
|
||||
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromAuthenticationSession(authenticationSession, AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE);
|
||||
authenticationSession.setAuthNote(AbstractIdpAuthenticator.FIRST_BROKER_LOGIN_SUCCESS, serializedCtx.getIdentityProviderId());
|
||||
|
||||
// TODO:mposolda Isn't this a bug that we redirect to 'afterBrokerLoginEndpoint' without rather continue with firstBrokerLogin and other authenticators like OTP?
|
||||
//return redirectToAfterBrokerLoginEndpoint(authSession, true);
|
||||
return null;
|
||||
logger.debugf("Forget-password flow finished when authenticated user '%s' after first broker login with identity provider '%s'.",
|
||||
linkingUser.getUsername(), serializedCtx.getIdentityProviderId());
|
||||
|
||||
return LoginActionsService.redirectToAfterBrokerLoginEndpoint(session, realm, uriInfo, authenticationSession, true);
|
||||
} else {
|
||||
return super.authenticationComplete();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ import java.util.Map;
|
|||
* <li>{@code realm} the {@link RealmModel}</li>
|
||||
* <li>{@code user} the current {@link UserModel}</li>
|
||||
* <li>{@code session} the active {@link KeycloakSession}</li>
|
||||
* <li>{@code clientSession} the current {@link org.keycloak.models.ClientSessionModel}</li>
|
||||
* <li>{@code clientSession} the current {@link org.keycloak.sessions.AuthenticationSessionModel}</li>
|
||||
* <li>{@code httpRequest} the current {@link org.jboss.resteasy.spi.HttpRequest}</li>
|
||||
* <li>{@code LOG} a {@link org.jboss.logging.Logger} scoped to {@link ScriptBasedAuthenticator}/li>
|
||||
* </ol>
|
||||
|
|
|
@ -69,17 +69,14 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
|
|||
return;
|
||||
}
|
||||
|
||||
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(context.generateCode())
|
||||
.setAuthenticationSession(authSession)
|
||||
.setUser(context.getUser());
|
||||
LoginFormsProvider loginFormsProvider = context.form();
|
||||
Response challenge;
|
||||
|
||||
// Do not allow resending e-mail by simple page refresh, i.e. when e-mail sent, it should be resent properly via email-verification endpoint
|
||||
if (! Objects.equals(authSession.getAuthNote(Constants.VERIFY_EMAIL_KEY), email)) {
|
||||
authSession.setAuthNote(Constants.VERIFY_EMAIL_KEY, email);
|
||||
context.getEvent().clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, email).success();
|
||||
challenge = sendVerifyEmail(context.getSession(), context.generateCode(), context.getUser(), context.getAuthenticationSession());
|
||||
challenge = sendVerifyEmail(context.getSession(), loginFormsProvider, context.getUser(), context.getAuthenticationSession());
|
||||
} else {
|
||||
challenge = loginFormsProvider.createResponse(UserModel.RequiredAction.VERIFY_EMAIL);
|
||||
}
|
||||
|
@ -87,9 +84,15 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
|
|||
context.challenge(challenge);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processAction(RequiredActionContext context) {
|
||||
context.failure();
|
||||
logger.infof("Re-sending email requested for user: %s", context.getUser().getUsername());
|
||||
|
||||
// This will allow user to re-send email again
|
||||
context.getAuthenticationSession().removeAuthNote(Constants.VERIFY_EMAIL_KEY);
|
||||
|
||||
requiredActionChallenge(context);
|
||||
}
|
||||
|
||||
|
||||
|
@ -124,15 +127,10 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
|
|||
return UserModel.RequiredAction.VERIFY_EMAIL.name();
|
||||
}
|
||||
|
||||
public static Response sendVerifyEmail(KeycloakSession session, String clientCode, UserModel user, AuthenticationSessionModel authSession) throws UriBuilderException, IllegalArgumentException {
|
||||
private Response sendVerifyEmail(KeycloakSession session, LoginFormsProvider forms, UserModel user, AuthenticationSessionModel authSession) throws UriBuilderException, IllegalArgumentException {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
UriInfo uriInfo = session.getContext().getUri();
|
||||
|
||||
LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(clientCode)
|
||||
.setAuthenticationSession(authSession)
|
||||
.setUser(authSession.getAuthenticatedUser());
|
||||
|
||||
int validityInSecs = realm.getAccessCodeLifespanUserAction();
|
||||
int absoluteExpirationInSecs = Time.currentTime() + validityInSecs;
|
||||
// ExecuteActionsActionToken token = new ExecuteActionsActionToken(user.getId(), absoluteExpirationInSecs, null,
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.keycloak.authorization.attribute.Attributes;
|
|||
import org.keycloak.authorization.identity.Identity;
|
||||
import org.keycloak.authorization.util.Tokens;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -118,8 +117,8 @@ public class KeycloakIdentity implements Identity {
|
|||
@Override
|
||||
public String getId() {
|
||||
if (isResourceServer()) {
|
||||
ClientSessionModel clientSession = this.keycloakSession.sessions().getClientSession(this.accessToken.getClientSession());
|
||||
return clientSession.getClient().getId();
|
||||
ClientModel client = getTargetClient();
|
||||
return client==null ? null : client.getId();
|
||||
}
|
||||
|
||||
return this.accessToken.getSubject();
|
||||
|
@ -137,20 +136,10 @@ public class KeycloakIdentity implements Identity {
|
|||
private boolean isResourceServer() {
|
||||
UserModel clientUser = null;
|
||||
|
||||
if (this.accessToken.getClientSession() != null) {
|
||||
ClientSessionModel clientSession = this.keycloakSession.sessions().getClientSession(this.accessToken.getClientSession());
|
||||
ClientModel clientModel = getTargetClient();
|
||||
|
||||
if (clientSession != null) {
|
||||
clientUser = this.keycloakSession.users().getServiceAccount(clientSession.getClient());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.accessToken.getIssuedFor() != null) {
|
||||
ClientModel clientModel = this.keycloakSession.realms().getClientById(this.accessToken.getIssuedFor(), this.realm);
|
||||
|
||||
if (clientModel != null) {
|
||||
clientUser = this.keycloakSession.users().getServiceAccount(clientModel);
|
||||
}
|
||||
if (clientModel != null) {
|
||||
clientUser = this.keycloakSession.users().getServiceAccount(clientModel);
|
||||
}
|
||||
|
||||
if (clientUser == null) {
|
||||
|
@ -159,4 +148,17 @@ public class KeycloakIdentity implements Identity {
|
|||
|
||||
return this.accessToken.getSubject().equals(clientUser.getId());
|
||||
}
|
||||
|
||||
private ClientModel getTargetClient() {
|
||||
if (this.accessToken.getIssuedFor() != null) {
|
||||
return realm.getClientByClientId(accessToken.getIssuedFor());
|
||||
}
|
||||
|
||||
if (this.accessToken.getAudience() != null && this.accessToken.getAudience().length > 0) {
|
||||
String audience = this.accessToken.getAudience()[0];
|
||||
return realm.getClientByClientId(audience);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.keycloak.jose.jws.JWSInput;
|
|||
import org.keycloak.jose.jws.JWSInputException;
|
||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.keys.loader.PublicKeyStorageManager;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.keycloak.dom.saml.v2.assertion.SubjectType;
|
|||
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.keys.RsaKeyMetadata;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.KeyManager;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.forms.account.freemarker.model;
|
|||
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
||||
|
@ -79,8 +78,8 @@ public class SessionsBean {
|
|||
|
||||
public Set<String> getClients() {
|
||||
Set<String> clients = new HashSet<String>();
|
||||
for (ClientSessionModel clientSession : session.getClientSessions()) {
|
||||
ClientModel client = clientSession.getClient();
|
||||
for (String clientUUID : session.getAuthenticatedClientSessions().keySet()) {
|
||||
ClientModel client = realm.getClientById(clientUUID);
|
||||
clients.add(client.getClientId());
|
||||
}
|
||||
return clients;
|
||||
|
|
|
@ -90,7 +90,6 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
|
||||
private UserModel user;
|
||||
|
||||
private AuthenticationSessionModel authenticationSession;
|
||||
private final Map<String, Object> attributes = new HashMap<String, Object>();
|
||||
|
||||
public FreeMarkerLoginFormsProvider(KeycloakSession session, FreeMarkerUtil freeMarker) {
|
||||
|
@ -156,6 +155,13 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
uriBuilder.replaceQuery(null);
|
||||
}
|
||||
|
||||
URI baseUri = uriBuilder.build();
|
||||
|
||||
if (accessCode != null) {
|
||||
uriBuilder.queryParam(OAuth2Constants.CODE, accessCode);
|
||||
}
|
||||
URI baseUriWithCode = uriBuilder.build();
|
||||
|
||||
for (String k : queryParameterMap.keySet()) {
|
||||
|
||||
Object[] objects = queryParameterMap.get(k).toArray();
|
||||
|
@ -163,13 +169,6 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
uriBuilder.replaceQueryParam(k, objects);
|
||||
}
|
||||
|
||||
// TODO:hmlnarik Why was the following removed in https://github.com/hmlnarik/keycloak/commit/6df8f13109d6ea77b455e04d884994e5831ea52b#diff-d795b851c2db89d5198c897aba4c40c9
|
||||
if (accessCode != null) {
|
||||
uriBuilder.replaceQueryParam(OAuth2Constants.CODE, accessCode);
|
||||
}
|
||||
|
||||
URI baseUri = uriBuilder.build();
|
||||
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
Theme theme;
|
||||
try {
|
||||
|
@ -221,7 +220,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
|
||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||
identityProviders = LoginFormsUtil.filterIdentityProviders(identityProviders, session, realm, attributes, formData);
|
||||
attributes.put("social", new IdentityProviderBean(realm, session, identityProviders, baseUri));
|
||||
attributes.put("social", new IdentityProviderBean(realm, session, identityProviders, baseUriWithCode));
|
||||
|
||||
attributes.put("url", new UrlBean(realm, theme, baseUri, this.actionUri));
|
||||
|
||||
|
@ -313,9 +312,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
if (objects.length == 1 && objects[0] == null) continue; //
|
||||
uriBuilder.replaceQueryParam(k, objects);
|
||||
}
|
||||
if (accessCode != null) {
|
||||
uriBuilder.replaceQueryParam(OAuth2Constants.CODE, accessCode);
|
||||
}
|
||||
|
||||
URI baseUri = uriBuilder.build();
|
||||
|
||||
if (accessCode != null) {
|
||||
|
@ -572,12 +569,6 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setAuthenticationSession(AuthenticationSessionModel authSession) {
|
||||
this.authenticationSession = authSession;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested, List<ProtocolMapperModel> protocolMappersRequested) {
|
||||
this.realmRolesRequested = realmRolesRequested;
|
||||
|
|
|
@ -85,10 +85,6 @@ public class UrlBean {
|
|||
return Urls.loginUsernameReminder(baseURI, realm).toString();
|
||||
}
|
||||
|
||||
public String getLoginEmailVerificationUrl() {
|
||||
return Urls.loginActionEmailVerification(baseURI, realm).toString();
|
||||
}
|
||||
|
||||
public String getFirstBrokerLoginUrl() {
|
||||
return Urls.firstBrokerLoginProcessor(baseURI, realm).toString();
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import org.jboss.logging.Logger;
|
|||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.authentication.AuthenticationProcessor;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.common.util.ObjectUtil;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
|
@ -35,7 +34,7 @@ import org.keycloak.services.managers.AuthenticationSessionManager;
|
|||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.resources.LoginActionsService;
|
||||
import org.keycloak.services.util.CacheControlUtil;
|
||||
import org.keycloak.services.util.PageExpiredRedirect;
|
||||
import org.keycloak.services.util.AuthenticationFlowURLHelper;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
|
||||
import javax.ws.rs.core.Context;
|
||||
|
@ -159,7 +158,7 @@ public abstract class AuthorizationEndpointBase {
|
|||
ClientSessionCode<AuthenticationSessionModel> check = new ClientSessionCode<>(session, realm, authSession);
|
||||
if (!check.isActionActive(ClientSessionCode.ActionType.LOGIN)) {
|
||||
|
||||
logger.infof("Authentication session '%s' exists, but is expired. Restart existing authentication session", authSession.getId());
|
||||
logger.debugf("Authentication session '%s' exists, but is expired. Restart existing authentication session", authSession.getId());
|
||||
authSession.restartSession(realm, client);
|
||||
return new AuthorizationEndpointChecks(authSession);
|
||||
|
||||
|
@ -167,10 +166,10 @@ public abstract class AuthorizationEndpointBase {
|
|||
// Check if we have lastProcessedExecution and restart the session just if yes. Otherwise update just client information from the AuthorizationEndpoint request.
|
||||
// This difference is needed, because of logout from JS applications in multiple browser tabs.
|
||||
if (hasProcessedExecution(authSession)) {
|
||||
logger.info("New request from application received, but authentication session already exists. Restart existing authentication session");
|
||||
logger.debug("New request from application received, but authentication session already exists. Restart existing authentication session");
|
||||
authSession.restartSession(realm, client);
|
||||
} else {
|
||||
logger.info("New request from application received, but authentication session already exists. Update client information in existing authentication session");
|
||||
logger.debug("New request from application received, but authentication session already exists. Update client information in existing authentication session");
|
||||
authSession.clearClientNotes(); // update client data
|
||||
authSession.updateClient(client);
|
||||
}
|
||||
|
@ -178,7 +177,7 @@ public abstract class AuthorizationEndpointBase {
|
|||
return new AuthorizationEndpointChecks(authSession);
|
||||
|
||||
} else {
|
||||
logger.info("Re-sent some previous request to Authorization endpoint. Likely browser 'back' or 'refresh' button.");
|
||||
logger.debug("Re-sent some previous request to Authorization endpoint. Likely browser 'back' or 'refresh' button.");
|
||||
|
||||
// See if we have lastProcessedExecution note. If yes, we are expired. Also if we are in different flow than initial one. Otherwise it is browser refresh of initial username/password form
|
||||
if (!shouldShowExpirePage(authSession)) {
|
||||
|
@ -186,7 +185,7 @@ public abstract class AuthorizationEndpointBase {
|
|||
} else {
|
||||
CacheControlUtil.noBackButtonCacheControlHeader();
|
||||
|
||||
Response response = new PageExpiredRedirect(session, realm, uriInfo)
|
||||
Response response = new AuthenticationFlowURLHelper(session, realm, uriInfo)
|
||||
.showPageExpired(authSession);
|
||||
return new AuthorizationEndpointChecks(response);
|
||||
}
|
||||
|
@ -196,11 +195,11 @@ public abstract class AuthorizationEndpointBase {
|
|||
UserSessionModel userSession = authSessionId==null ? null : session.sessions().getUserSession(realm, authSessionId);
|
||||
|
||||
if (userSession != null) {
|
||||
logger.infof("Sent request to authz endpoint. We don't have authentication session with ID '%s' but we have userSession. Will re-create authentication session with same ID", authSessionId);
|
||||
logger.debugf("Sent request to authz endpoint. We don't have authentication session with ID '%s' but we have userSession. Will re-create authentication session with same ID", authSessionId);
|
||||
authSession = session.authenticationSessions().createAuthenticationSession(authSessionId, realm, client);
|
||||
} else {
|
||||
authSession = manager.createAuthenticationSession(realm, client, true);
|
||||
logger.infof("Sent request to authz endpoint. Created new authentication session with ID '%s'", authSession.getId());
|
||||
logger.debugf("Sent request to authz endpoint. Created new authentication session with ID '%s'", authSession.getId());
|
||||
}
|
||||
|
||||
return new AuthorizationEndpointChecks(authSession);
|
||||
|
@ -224,17 +223,13 @@ public abstract class AuthorizationEndpointBase {
|
|||
}
|
||||
|
||||
String lastFlow = authSession.getAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH);
|
||||
// Check if we transitted between flows (eg. clicking "register" on login screen)
|
||||
if (!initialFlow.equals(lastFlow)) {
|
||||
logger.infof("Transition between flows! Current flow: %s, Previous flow: %s", initialFlow, lastFlow);
|
||||
// Check if we transitted between flows (eg. clicking "register" on login screen and then clicking browser 'back', which showed this page)
|
||||
if (!initialFlow.equals(lastFlow) && AuthenticationSessionModel.Action.AUTHENTICATE.toString().equals(authSession.getAction())) {
|
||||
logger.debugf("Transition between flows! Current flow: %s, Previous flow: %s", initialFlow, lastFlow);
|
||||
|
||||
if (lastFlow == null || LoginActionsService.isFlowTransitionAllowed(initialFlow, lastFlow)) {
|
||||
authSession.setAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH, initialFlow);
|
||||
authSession.removeAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
authSession.setAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH, initialFlow);
|
||||
authSession.removeAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -575,7 +575,6 @@ public class TokenManager {
|
|||
|
||||
protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel user, UserSessionModel session, AuthenticatedClientSessionModel clientSession, UriInfo uriInfo) {
|
||||
AccessToken token = new AccessToken();
|
||||
token.clientSession(clientSession.getId());
|
||||
token.id(KeycloakModelUtils.generateId());
|
||||
token.type(TokenUtil.TOKEN_TYPE_BEARER);
|
||||
token.subject(user.getId());
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.keycloak.events.EventBuilder;
|
|||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.protocol.AuthorizationEndpointBase;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
|
@ -41,7 +40,6 @@ import org.keycloak.protocol.oidc.utils.RedirectUtils;
|
|||
import org.keycloak.services.ErrorPageException;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AuthenticationSessionManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.LoginActionsService;
|
||||
import org.keycloak.services.util.CacheControlUtil;
|
||||
|
@ -387,7 +385,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
|
|||
private void updateAuthenticationSession() {
|
||||
authenticationSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
authenticationSession.setRedirectUri(redirectUri);
|
||||
authenticationSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
authenticationSession.setAction(AuthenticationSessionModel.Action.AUTHENTICATE.name());
|
||||
authenticationSession.setClientNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, request.getResponseType());
|
||||
authenticationSession.setClientNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, request.getRedirectUriParam());
|
||||
authenticationSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
|
||||
|
|
|
@ -210,12 +210,13 @@ public class TokenEndpoint {
|
|||
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Missing parameter: " + OAuth2Constants.CODE, Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
String[] parts = code.split("\\.");
|
||||
if (parts.length == 4) {
|
||||
event.detail(Details.CODE_ID, parts[2]);
|
||||
}
|
||||
|
||||
ClientSessionCode.ParseResult<AuthenticatedClientSessionModel> parseResult = ClientSessionCode.parseResult(code, session, realm, AuthenticatedClientSessionModel.class);
|
||||
if (parseResult.isAuthSessionNotFound() || parseResult.isIllegalHash()) {
|
||||
String[] parts = code.split("\\.");
|
||||
if (parts.length == 2) {
|
||||
event.detail(Details.CODE_ID, parts[1]);
|
||||
}
|
||||
event.error(Errors.INVALID_CODE);
|
||||
|
||||
// Attempt to use same code twice should invalidate existing clientSession
|
||||
|
@ -228,17 +229,16 @@ public class TokenEndpoint {
|
|||
}
|
||||
|
||||
AuthenticatedClientSessionModel clientSession = parseResult.getClientSession();
|
||||
event.detail(Details.CODE_ID, clientSession.getId());
|
||||
|
||||
if (!parseResult.getCode().isValid(AuthenticatedClientSessionModel.Action.CODE_TO_TOKEN.name(), ClientSessionCode.ActionType.CLIENT)) {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT, "Code is expired", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// TODO: This shouldn't be needed to write into the clientLoginSessionModel itself
|
||||
// TODO: This shouldn't be needed to write into the AuthenticatedClientSessionModel itself
|
||||
parseResult.getCode().setAction(null);
|
||||
|
||||
// TODO: Maybe rather create userSession even at this stage? Not sure...
|
||||
// TODO: Maybe rather create userSession even at this stage?
|
||||
UserSessionModel userSession = clientSession.getUserSession();
|
||||
|
||||
if (userSession == null) {
|
||||
|
|
|
@ -93,9 +93,9 @@ abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper
|
|||
// get a set of all realm roles assigned to the user or its group
|
||||
Stream<RoleModel> clientUserRoles = getAllUserRolesStream(user).filter(restriction);
|
||||
|
||||
boolean dontLimitScope = userSession.getClientSessions().stream().anyMatch(cs -> cs.getClient().isFullScopeAllowed());
|
||||
boolean dontLimitScope = userSession.getAuthenticatedClientSessions().values().stream().anyMatch(cs -> cs.getClient().isFullScopeAllowed());
|
||||
if (! dontLimitScope) {
|
||||
Set<RoleModel> clientRoles = userSession.getClientSessions().stream()
|
||||
Set<RoleModel> clientRoles = userSession.getAuthenticatedClientSessions().values().stream()
|
||||
.flatMap(cs -> cs.getClient().getScopeMappings().stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
|
|
|
@ -376,8 +376,12 @@ public class SamlProtocol implements LoginProtocol {
|
|||
clientSession.setNote(SAML_NAME_ID_FORMAT, nameIdFormat);
|
||||
|
||||
SAML2LoginResponseBuilder builder = new SAML2LoginResponseBuilder();
|
||||
builder.requestID(requestID).destination(redirectUri).issuer(responseIssuer).assertionExpiration(realm.getAccessCodeLifespan()).subjectExpiration(realm.getAccessTokenLifespan()).sessionIndex(clientSession.getId())
|
||||
builder.requestID(requestID).destination(redirectUri).issuer(responseIssuer).assertionExpiration(realm.getAccessCodeLifespan()).subjectExpiration(realm.getAccessTokenLifespan())
|
||||
.requestIssuer(clientSession.getClient().getClientId()).nameIdentifier(nameIdFormat, nameId).authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
|
||||
|
||||
String sessionIndex = SamlSessionUtils.getSessionIndex(clientSession);
|
||||
builder.sessionIndex(sessionIndex);
|
||||
|
||||
if (!samlClient.includeAuthnStatement()) {
|
||||
builder.disableAuthnStatement(true);
|
||||
}
|
||||
|
@ -682,8 +686,12 @@ public class SamlProtocol implements LoginProtocol {
|
|||
|
||||
protected SAML2LogoutRequestBuilder createLogoutRequest(String logoutUrl, AuthenticatedClientSessionModel clientSession, ClientModel client) {
|
||||
// build userPrincipal with subject used at login
|
||||
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder().assertionExpiration(realm.getAccessCodeLifespan()).issuer(getResponseIssuer(realm)).sessionIndex(clientSession.getId())
|
||||
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder().assertionExpiration(realm.getAccessCodeLifespan()).issuer(getResponseIssuer(realm))
|
||||
.userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT)).destination(logoutUrl);
|
||||
|
||||
String sessionIndex = SamlSessionUtils.getSessionIndex(clientSession);
|
||||
logoutBuilder.sessionIndex(sessionIndex);
|
||||
|
||||
return logoutBuilder;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,8 +37,8 @@ import org.keycloak.events.Errors;
|
|||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.keys.RsaKeyMetadata;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeyManager;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -55,7 +55,6 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
|||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.AuthenticationSessionManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.services.util.CacheControlUtil;
|
||||
|
@ -99,11 +98,6 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
|
||||
protected static final Logger logger = Logger.getLogger(SamlService.class);
|
||||
|
||||
@Context
|
||||
protected KeycloakSession session;
|
||||
|
||||
private String requestRelayState;
|
||||
|
||||
public SamlService(RealmModel realm, EventBuilder event) {
|
||||
super(realm, event);
|
||||
}
|
||||
|
@ -283,7 +277,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
|
||||
authSession.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
|
||||
authSession.setRedirectUri(redirect);
|
||||
authSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
authSession.setAction(AuthenticationSessionModel.Action.AUTHENTICATE.name());
|
||||
authSession.setClientNote(SamlProtocol.SAML_BINDING, bindingType);
|
||||
authSession.setClientNote(GeneralConstants.RELAY_STATE, relayState);
|
||||
authSession.setClientNote(SamlProtocol.SAML_REQUEST_ID, requestAbstractType.getID());
|
||||
|
@ -378,31 +372,22 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
userSession.setNote(SamlProtocol.SAML_LOGOUT_CANONICALIZATION, samlClient.getCanonicalizationMethod());
|
||||
userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL);
|
||||
// remove client from logout requests
|
||||
for (ClientSessionModel clientSession : userSession.getClientSessions()) {
|
||||
if (clientSession.getClient().getId().equals(client.getId())) {
|
||||
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
|
||||
}
|
||||
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(client.getId());
|
||||
if (clientSession != null) {
|
||||
clientSession.setAction(AuthenticationSessionModel.Action.LOGGED_OUT.name());
|
||||
}
|
||||
logger.debug("browser Logout");
|
||||
return authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
|
||||
} else if (logoutRequest.getSessionIndex() != null) {
|
||||
for (String sessionIndex : logoutRequest.getSessionIndex()) {
|
||||
ClientSessionModel clientSession = session.sessions().getClientSession(realm, sessionIndex);
|
||||
|
||||
AuthenticatedClientSessionModel clientSession = SamlSessionUtils.getClientSession(session, realm, sessionIndex);
|
||||
if (clientSession == null)
|
||||
continue;
|
||||
UserSessionModel userSession = clientSession.getUserSession();
|
||||
if (clientSession.getClient().getClientId().equals(client.getClientId())) {
|
||||
// remove requesting client from logout
|
||||
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
|
||||
|
||||
// Remove also other clientSessions of this client as there could be more in this UserSession
|
||||
if (userSession != null) {
|
||||
for (ClientSessionModel clientSession2 : userSession.getClientSessions()) {
|
||||
if (clientSession2.getClient().getId().equals(client.getId())) {
|
||||
clientSession2.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
clientSession.setAction(AuthenticationSessionModel.Action.LOGGED_OUT.name());
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -609,6 +594,10 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
return ErrorPage.error(session, Messages.CLIENT_NOT_FOUND);
|
||||
}
|
||||
if (!client.isEnabled()) {
|
||||
event.error(Errors.CLIENT_DISABLED);
|
||||
return ErrorPage.error(session, Messages.CLIENT_DISABLED);
|
||||
}
|
||||
if (client.getManagementUrl() == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE) == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE) == null) {
|
||||
logger.error("SAML assertion consumer url not set up");
|
||||
event.error(Errors.INVALID_REDIRECT_URI);
|
||||
|
@ -654,7 +643,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
|
||||
AuthenticationSessionModel authSession = checks.authSession;
|
||||
authSession.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
|
||||
authSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
authSession.setAction(AuthenticationSessionModel.Action.AUTHENTICATE.name());
|
||||
authSession.setClientNote(SamlProtocol.SAML_BINDING, SamlProtocol.SAML_POST_BINDING);
|
||||
authSession.setClientNote(SamlProtocol.SAML_IDP_INITIATED_LOGIN, "true");
|
||||
authSession.setRedirectUri(redirect);
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.protocol.saml;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class SamlSessionUtils {
|
||||
|
||||
private static final String DELIMITER = "::";
|
||||
|
||||
// Just perf optimization
|
||||
private static final Pattern PATTERN = Pattern.compile(DELIMITER);
|
||||
|
||||
|
||||
public static String getSessionIndex(AuthenticatedClientSessionModel clientSession) {
|
||||
UserSessionModel userSession = clientSession.getUserSession();
|
||||
ClientModel client = clientSession.getClient();
|
||||
|
||||
return userSession.getId() + DELIMITER + client.getId();
|
||||
}
|
||||
|
||||
|
||||
public static AuthenticatedClientSessionModel getClientSession(KeycloakSession session, RealmModel realm, String sessionIndex) {
|
||||
if (sessionIndex == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] parts = PATTERN.split(sessionIndex);
|
||||
if (parts.length != 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, parts[0]);
|
||||
if (userSession == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return userSession.getAuthenticatedClientSessions().get(parts[1]);
|
||||
}
|
||||
|
||||
}
|
|
@ -37,6 +37,8 @@ public class ErrorPageException extends WebApplicationException {
|
|||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Response getResponse() {
|
||||
return ErrorPage.error(session, errorMessage, parameters);
|
||||
|
|
|
@ -537,7 +537,7 @@ public class AuthenticationManager {
|
|||
.createInfoPage();
|
||||
return response;
|
||||
|
||||
// TODO:mposolda doublecheck if restart-cookie and authentication session are cleared in this flow
|
||||
// Don't remove authentication session for now, to ensure that browser buttons (back/refresh) will still work fine.
|
||||
|
||||
}
|
||||
RealmModel realm = authSession.getRealm();
|
||||
|
|
|
@ -74,20 +74,17 @@ public class AuthenticationSessionManager {
|
|||
boolean sslRequired = realm.getSslRequired().isRequired(session.getContext().getConnection());
|
||||
CookieHelper.addCookie(AUTH_SESSION_ID, authSessionId, cookiePath, null, null, -1, sslRequired, true);
|
||||
|
||||
// TODO trace with isTraceEnabled
|
||||
log.infof("Set AUTH_SESSION_ID cookie with value %s", authSessionId);
|
||||
log.debugf("Set AUTH_SESSION_ID cookie with value %s", authSessionId);
|
||||
}
|
||||
|
||||
|
||||
public String getAuthSessionCookie() {
|
||||
String cookieVal = CookieHelper.getCookieValue(AUTH_SESSION_ID);
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
if (cookieVal != null) {
|
||||
log.tracef("Found AUTH_SESSION_ID cookie with value %s", cookieVal);
|
||||
} else {
|
||||
log.tracef("Not found AUTH_SESSION_ID cookie");
|
||||
}
|
||||
if (cookieVal != null) {
|
||||
log.debugf("Found AUTH_SESSION_ID cookie with value %s", cookieVal);
|
||||
} else {
|
||||
log.debugf("Not found AUTH_SESSION_ID cookie");
|
||||
}
|
||||
|
||||
return cookieVal;
|
||||
|
@ -95,7 +92,7 @@ public class AuthenticationSessionManager {
|
|||
|
||||
|
||||
public void removeAuthenticationSession(RealmModel realm, AuthenticationSessionModel authSession, boolean expireRestartCookie) {
|
||||
log.infof("Removing authSession '%s'. Expire restart cookie: %b", authSession.getId(), expireRestartCookie);
|
||||
log.debugf("Removing authSession '%s'. Expire restart cookie: %b", authSession.getId(), expireRestartCookie);
|
||||
session.authenticationSessions().removeAuthenticationSession(realm, authSession);
|
||||
|
||||
// expire restart cookie
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
|
|||
import org.keycloak.representations.adapters.config.BaseRealmConfig;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
|
@ -104,6 +105,11 @@ public class ClientManager {
|
|||
sessionsPersister.onClientRemoved(realm, client);
|
||||
}
|
||||
|
||||
AuthenticationSessionProvider authSessions = realmManager.getSession().authenticationSessions();
|
||||
if (authSessions != null) {
|
||||
authSessions.onClientRemoved(realm, client);
|
||||
}
|
||||
|
||||
UserModel serviceAccountUser = realmManager.getSession().users().getServiceAccount(client);
|
||||
if (serviceAccountUser != null) {
|
||||
new UserManager(realmManager.getSession()).removeUser(realm, serviceAccountUser);
|
||||
|
|
|
@ -112,11 +112,6 @@ public class ClientSessionCode<CLIENT_SESSION extends CommonClientSessionModel>
|
|||
CommonClientSessionModel clientSessionn = CodeGenerateUtil.getParser(sessionClass).parseSession(code, session, realm);;
|
||||
CLIENT_SESSION clientSession = sessionClass.cast(clientSessionn);
|
||||
|
||||
// TODO:mposolda Move this to somewhere else? Maybe LoginActionsService.sessionCodeChecks should be somehow even for non-action URLs...
|
||||
if (clientSession != null) {
|
||||
session.getContext().setClient(clientSession.getClient());
|
||||
}
|
||||
|
||||
return clientSession;
|
||||
}
|
||||
|
||||
|
@ -168,8 +163,12 @@ public class ClientSessionCode<CLIENT_SESSION extends CommonClientSessionModel>
|
|||
|
||||
|
||||
public Set<RoleModel> getRequestedRoles() {
|
||||
return getRequestedRoles(commonLoginSession, realm);
|
||||
}
|
||||
|
||||
public static Set<RoleModel> getRequestedRoles(CommonClientSessionModel clientSession, RealmModel realm) {
|
||||
Set<RoleModel> requestedRoles = new HashSet<>();
|
||||
for (String roleId : commonLoginSession.getRoles()) {
|
||||
for (String roleId : clientSession.getRoles()) {
|
||||
RoleModel role = realm.getRoleById(roleId);
|
||||
if (role != null) {
|
||||
requestedRoles.add(role);
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.util.Map;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
@ -41,7 +40,6 @@ class CodeGenerateUtil {
|
|||
private static final Map<Class<? extends CommonClientSessionModel>, ClientSessionParser> PARSERS = new HashMap<>();
|
||||
|
||||
static {
|
||||
PARSERS.put(ClientSessionModel.class, new ClientSessionModelParser());
|
||||
PARSERS.put(AuthenticationSessionModel.class, new AuthenticationSessionModelParser());
|
||||
PARSERS.put(AuthenticatedClientSessionModel.class, new AuthenticatedClientSessionModelParser());
|
||||
}
|
||||
|
@ -78,54 +76,6 @@ class CodeGenerateUtil {
|
|||
// IMPLEMENTATIONS
|
||||
|
||||
|
||||
// TODO: remove
|
||||
private static class ClientSessionModelParser implements ClientSessionParser<ClientSessionModel> {
|
||||
|
||||
|
||||
@Override
|
||||
public ClientSessionModel parseSession(String code, KeycloakSession session, RealmModel realm) {
|
||||
try {
|
||||
String[] parts = code.split("\\.");
|
||||
String id = parts[2];
|
||||
return session.sessions().getClientSession(realm, id);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateCode(ClientSessionModel clientSession, String actionId) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("cls.");
|
||||
sb.append(actionId);
|
||||
sb.append('.');
|
||||
sb.append(clientSession.getId());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeExpiredSession(KeycloakSession session, ClientSessionModel clientSession) {
|
||||
session.sessions().removeClientSession(clientSession.getRealm(), clientSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNote(ClientSessionModel clientSession, String name) {
|
||||
return clientSession.getNote(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNote(ClientSessionModel clientSession, String name) {
|
||||
clientSession.removeNote(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNote(ClientSessionModel clientSession, String name, String value) {
|
||||
clientSession.setNote(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class AuthenticationSessionModelParser implements ClientSessionParser<AuthenticationSessionModel> {
|
||||
|
||||
@Override
|
||||
|
@ -193,20 +143,6 @@ class CodeGenerateUtil {
|
|||
sb.append('.');
|
||||
sb.append(clientUUID);
|
||||
|
||||
// TODO:mposolda codeChallengeMethod is not used anywhere. Not sure if it's bug of PKCE contribution. Doublecheck the PKCE specification what should be done regarding code
|
||||
// https://tools.ietf.org/html/rfc7636#section-4
|
||||
String codeChallenge = clientSession.getNote(OAuth2Constants.CODE_CHALLENGE);
|
||||
String codeChallengeMethod = clientSession.getNote(OAuth2Constants.CODE_CHALLENGE_METHOD);
|
||||
if (codeChallenge != null) {
|
||||
logger.debugf("PKCE received codeChallenge = %s", codeChallenge);
|
||||
if (codeChallengeMethod == null) {
|
||||
logger.debug("PKCE not received codeChallengeMethod, treating plain");
|
||||
codeChallengeMethod = OAuth2Constants.PKCE_METHOD_PLAIN;
|
||||
} else {
|
||||
logger.debugf("PKCE received codeChallengeMethod = %s", codeChallengeMethod);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.keycloak.representations.idm.OAuthClientRepresentation;
|
|||
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.services.clientregistration.policy.DefaultClientRegistrationPolicies;
|
||||
|
||||
|
@ -248,6 +249,11 @@ public class RealmManager {
|
|||
sessionsPersister.onRealmRemoved(realm);
|
||||
}
|
||||
|
||||
AuthenticationSessionProvider authSessions = session.authenticationSessions();
|
||||
if (authSessions != null) {
|
||||
authSessions.onRealmRemoved(realm);
|
||||
}
|
||||
|
||||
// Refresh periodic sync tasks for configured storageProviders
|
||||
List<UserStorageProviderModel> storageProviders = realm.getUserStorageProviders();
|
||||
UserStorageSyncManager storageSync = new UserStorageSyncManager();
|
||||
|
|
|
@ -104,8 +104,8 @@ public class UserSessionManager {
|
|||
user.getUsername(), client.getClientId());
|
||||
}
|
||||
|
||||
userSession.getAuthenticatedClientSessions().remove(client.getClientId());
|
||||
persister.removeClientSession(clientSession.getId(), true);
|
||||
clientSession.setUserSession(null);
|
||||
persister.removeClientSession(userSession.getId(), client.getId(), true);
|
||||
checkOfflineUserSessionHasClientSessions(realm, user, userSession);
|
||||
anyRemoved = true;
|
||||
}
|
||||
|
@ -148,9 +148,8 @@ public class UserSessionManager {
|
|||
clientSession.getId(), offlineUserSession.getId(), user.getUsername(), clientSession.getClient().getClientId());
|
||||
}
|
||||
|
||||
AuthenticatedClientSessionModel offlineClientSession = kcSession.sessions().createOfflineClientSession(clientSession);
|
||||
offlineUserSession.getAuthenticatedClientSessions().put(clientSession.getClient().getId(), offlineClientSession);
|
||||
persister.createClientSession(offlineUserSession, clientSession, true);
|
||||
kcSession.sessions().createOfflineClientSession(clientSession, offlineUserSession);
|
||||
persister.createClientSession(clientSession, true);
|
||||
}
|
||||
|
||||
// Check if userSession has any offline clientSessions attached to it. Remove userSession if not
|
||||
|
|
|
@ -75,6 +75,7 @@ import org.keycloak.services.managers.AuthenticationSessionManager;
|
|||
import org.keycloak.services.managers.BruteForceProtector;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.util.BrowserHistoryHelper;
|
||||
import org.keycloak.services.util.CacheControlUtil;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
|
@ -210,6 +211,8 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
throw new ErrorPageException(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
|
||||
event.detail(Details.REDIRECT_URI, redirectUri);
|
||||
|
||||
if (nonce == null || hash == null) {
|
||||
event.error(Errors.INVALID_REDIRECT_URI);
|
||||
throw new ErrorPageException(session, Messages.INVALID_REQUEST);
|
||||
|
@ -239,7 +242,10 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
return Response.status(302).location(builder.build()).build();
|
||||
}
|
||||
|
||||
|
||||
cookieResult.getSession();
|
||||
event.session(cookieResult.getSession());
|
||||
event.user(cookieResult.getUser());
|
||||
event.detail(Details.USERNAME, cookieResult.getUser().getUsername());
|
||||
|
||||
AuthenticatedClientSessionModel clientSession = null;
|
||||
for (AuthenticatedClientSessionModel cs : cookieResult.getSession().getAuthenticatedClientSessions().values()) {
|
||||
|
@ -264,7 +270,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
throw new ErrorPageException(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
|
||||
|
||||
event.detail(Details.IDENTITY_PROVIDER, providerId);
|
||||
|
||||
ClientModel accountService = this.realmModel.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
|
||||
if (!accountService.getId().equals(client.getId())) {
|
||||
|
@ -307,6 +313,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
authSession.setClientNote(OIDCLoginProtocol.STATE_PARAM, UUID.randomUUID().toString());
|
||||
authSession.setAuthNote(LINKING_IDENTITY_PROVIDER, cookieResult.getSession().getId() + clientId + providerId);
|
||||
|
||||
event.detail(Details.CODE_ID, userSession.getId());
|
||||
event.success();
|
||||
|
||||
try {
|
||||
|
@ -508,6 +515,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
|
||||
this.event.event(EventType.IDENTITY_PROVIDER_LOGIN)
|
||||
.detail(Details.REDIRECT_URI, authenticationSession.getRedirectUri())
|
||||
.detail(Details.IDENTITY_PROVIDER, providerId)
|
||||
.detail(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername());
|
||||
|
||||
UserModel federatedUser = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, this.realmModel);
|
||||
|
@ -786,6 +794,9 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
authSession.setUserSessionNote(Details.IDENTITY_PROVIDER, providerId);
|
||||
authSession.setUserSessionNote(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername());
|
||||
|
||||
event.detail(Details.IDENTITY_PROVIDER, providerId)
|
||||
.detail(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername());
|
||||
|
||||
if (isDebugEnabled()) {
|
||||
logger.debugf("Performing local authentication for user [%s].", federatedUser);
|
||||
}
|
||||
|
@ -961,43 +972,39 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
}
|
||||
|
||||
private ParsedCodeContext parseClientSessionCode(String code) {
|
||||
ClientSessionCode.ParseResult<AuthenticationSessionModel> parseResult = ClientSessionCode.parseResult(code, this.session, this.realmModel, AuthenticationSessionModel.class);
|
||||
ClientSessionCode<AuthenticationSessionModel> clientCode = parseResult.getCode();
|
||||
|
||||
if (clientCode != null) {
|
||||
AuthenticationSessionModel authenticationSession = clientCode.getClientSession();
|
||||
|
||||
ClientModel client = authenticationSession.getClient();
|
||||
|
||||
if (client != null) {
|
||||
|
||||
logger.debugf("Got authorization code from client [%s].", client.getClientId());
|
||||
this.event.client(client);
|
||||
this.session.getContext().setClient(client);
|
||||
|
||||
if (!clientCode.isValid(AuthenticationSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||
logger.debugf("Authorization code is not valid. Client session ID: %s, Client session's action: %s", authenticationSession.getId(), authenticationSession.getAction());
|
||||
|
||||
// Check if error happened during login or during linking from account management
|
||||
Response accountManagementFailedLinking = checkAccountManagementFailedLinking(clientCode.getClientSession(), Messages.STALE_CODE_ACCOUNT);
|
||||
Response staleCodeError = (accountManagementFailedLinking != null) ? accountManagementFailedLinking : redirectToErrorPage(Messages.STALE_CODE);
|
||||
|
||||
|
||||
return ParsedCodeContext.response(staleCodeError);
|
||||
}
|
||||
|
||||
if (isDebugEnabled()) {
|
||||
logger.debugf("Authorization code is valid.");
|
||||
}
|
||||
|
||||
return ParsedCodeContext.clientSessionCode(clientCode);
|
||||
}
|
||||
if (code == null) {
|
||||
logger.debugf("Invalid request. Authorization code was null");
|
||||
Response staleCodeError = redirectToErrorPage(Messages.INVALID_REQUEST);
|
||||
return ParsedCodeContext.response(staleCodeError);
|
||||
}
|
||||
|
||||
// TODO:mposolda rather some different page? Maybe "PageExpired" page?
|
||||
logger.debugf("Authorization code is not valid. Code: %s", code);
|
||||
Response staleCodeError = redirectToErrorPage(Messages.STALE_CODE);
|
||||
return ParsedCodeContext.response(staleCodeError);
|
||||
SessionCodeChecks checks = new SessionCodeChecks(realmModel, uriInfo, clientConnection, session, event, code, null, LoginActionsService.AUTHENTICATE_PATH);
|
||||
checks.initialVerify();
|
||||
if (!checks.verifyActiveAndValidAction(AuthenticationSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||
|
||||
AuthenticationSessionModel authSession = checks.getAuthenticationSession();
|
||||
if (authSession != null) {
|
||||
// Check if error happened during login or during linking from account management
|
||||
Response accountManagementFailedLinking = checkAccountManagementFailedLinking(authSession, Messages.STALE_CODE_ACCOUNT);
|
||||
if (accountManagementFailedLinking != null) {
|
||||
return ParsedCodeContext.response(accountManagementFailedLinking);
|
||||
} else {
|
||||
Response errorResponse = checks.getResponse();
|
||||
|
||||
// Remove "code" from browser history
|
||||
errorResponse = BrowserHistoryHelper.getInstance().saveResponseAndRedirect(session, authSession, errorResponse, true);
|
||||
return ParsedCodeContext.response(errorResponse);
|
||||
}
|
||||
} else {
|
||||
return ParsedCodeContext.response(checks.getResponse());
|
||||
}
|
||||
} else {
|
||||
if (isDebugEnabled()) {
|
||||
logger.debugf("Authorization code is valid.");
|
||||
}
|
||||
|
||||
return ParsedCodeContext.clientSessionCode(checks.getClientCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1022,6 +1029,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
}
|
||||
|
||||
SamlService samlService = new SamlService(realmModel, event);
|
||||
ResteasyProviderFactory.getInstance().injectProperties(samlService);
|
||||
AuthenticationSessionModel authSession = samlService.getOrCreateLoginSessionForIdpInitiatedSso(session, realmModel, oClient.get(), null);
|
||||
|
||||
return ParsedCodeContext.clientSessionCode(new ClientSessionCode<>(session, this.realmModel, authSession));
|
||||
|
@ -1091,16 +1099,6 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
return Response.status(302).location(UriBuilder.fromUri(authSession.getRedirectUri()).build()).build();
|
||||
}
|
||||
|
||||
private Response redirectToLoginPage(Throwable t, ClientSessionCode<AuthenticationSessionModel> clientCode) {
|
||||
String message = t.getMessage();
|
||||
|
||||
if (message == null) {
|
||||
message = Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR;
|
||||
}
|
||||
|
||||
fireErrorEvent(message);
|
||||
return browserAuthentication(clientCode.getClientSession(), message);
|
||||
}
|
||||
|
||||
protected Response browserAuthentication(AuthenticationSessionModel authSession, String errorMessage) {
|
||||
this.event.event(EventType.LOGIN);
|
||||
|
|
|
@ -28,22 +28,20 @@ import org.keycloak.authentication.RequiredActionFactory;
|
|||
import org.keycloak.authentication.RequiredActionProvider;
|
||||
import org.keycloak.TokenVerifier;
|
||||
import org.keycloak.authentication.actiontoken.*;
|
||||
import org.keycloak.authentication.actiontoken.resetcred.ResetCredentialsActionTokenHandler;
|
||||
import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator;
|
||||
import org.keycloak.authentication.authenticators.broker.util.PostBrokerLoginConstants;
|
||||
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
|
||||
import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
|
||||
import org.keycloak.authentication.requiredactions.VerifyEmail;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.ObjectUtil;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.exceptions.TokenNotActiveException;
|
||||
import org.keycloak.forms.login.LoginFormsProvider;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
@ -55,13 +53,10 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserModel.RequiredAction;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.utils.FormMessage;
|
||||
import org.keycloak.protocol.AuthorizationEndpointBase;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
import org.keycloak.protocol.LoginProtocol.Error;
|
||||
import org.keycloak.protocol.RestartLoginCookie;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.utils.OIDCResponseMode;
|
||||
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
|
||||
|
@ -74,10 +69,9 @@ import org.keycloak.services.managers.ClientSessionCode;
|
|||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.LoginActionsServiceChecks.RestartFlowException;
|
||||
import org.keycloak.services.util.CacheControlUtil;
|
||||
import org.keycloak.services.util.PageExpiredRedirect;
|
||||
import org.keycloak.services.util.AuthenticationFlowURLHelper;
|
||||
import org.keycloak.services.util.BrowserHistoryHelper;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.CommonClientSessionModel;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
|
@ -94,6 +88,8 @@ import javax.ws.rs.core.UriBuilder;
|
|||
import javax.ws.rs.core.UriInfo;
|
||||
import javax.ws.rs.ext.Providers;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.core.*;
|
||||
import static org.keycloak.authentication.actiontoken.DefaultActionToken.ACTION_TOKEN_BASIC_CHECKS;
|
||||
|
||||
|
@ -185,333 +181,14 @@ public class LoginActionsService {
|
|||
}
|
||||
|
||||
private SessionCodeChecks checksForCode(String code, String execution, String flowPath) {
|
||||
SessionCodeChecks res = new SessionCodeChecks(code, execution, flowPath);
|
||||
SessionCodeChecks res = new SessionCodeChecks(realm, uriInfo, clientConnection, session, event, code, execution, flowPath);
|
||||
res.initialVerify();
|
||||
return res;
|
||||
}
|
||||
|
||||
private SessionCodeChecks checksForCodeRefreshNotAllowed(String code, String execution, String flowPath) {
|
||||
SessionCodeChecks res = new SessionCodeChecks(code, execution, flowPath);
|
||||
res.setAllowRefresh(false);
|
||||
res.initialVerify();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class SessionCodeChecks {
|
||||
ClientSessionCode<AuthenticationSessionModel> clientCode;
|
||||
Response response;
|
||||
ClientSessionCode.ParseResult<AuthenticationSessionModel> result;
|
||||
private boolean actionRequest;
|
||||
private boolean allowRefresh = true;
|
||||
|
||||
private final String code;
|
||||
private final String execution;
|
||||
private final String flowPath;
|
||||
|
||||
public SessionCodeChecks(String code, String execution, String flowPath) {
|
||||
this.code = code;
|
||||
this.execution = execution;
|
||||
this.flowPath = flowPath;
|
||||
}
|
||||
|
||||
public AuthenticationSessionModel getAuthenticationSession() {
|
||||
return clientCode == null ? null : clientCode.getClientSession();
|
||||
}
|
||||
|
||||
public boolean passed() {
|
||||
return response == null;
|
||||
}
|
||||
|
||||
public boolean failed() {
|
||||
return response != null;
|
||||
}
|
||||
|
||||
public boolean isAllowRefresh() {
|
||||
return allowRefresh;
|
||||
}
|
||||
|
||||
public void setAllowRefresh(boolean allowRefresh) {
|
||||
this.allowRefresh = allowRefresh;
|
||||
}
|
||||
|
||||
|
||||
boolean verifyCode(String expectedAction, ClientSessionCode.ActionType actionType) {
|
||||
if (failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isActionActive(actionType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!clientCode.isValidAction(expectedAction)) {
|
||||
AuthenticationSessionModel authSession = getAuthenticationSession();
|
||||
if (ClientSessionModel.Action.REQUIRED_ACTIONS.name().equals(authSession.getAction())) {
|
||||
// TODO:mposolda debug or trace
|
||||
logger.info("Incorrect flow '%s' . User authenticated already. Redirecting to requiredActions now.");
|
||||
response = redirectToRequiredActions(null);
|
||||
return false;
|
||||
} else {
|
||||
// TODO:mposolda could this happen? Doublecheck if we use other AuthenticationSession.Action besides AUTHENTICATE and REQUIRED_ACTIONS
|
||||
logger.errorf("Bad action. Expected action '%s', current action '%s'", expectedAction, authSession.getAction());
|
||||
response = ErrorPage.error(session, Messages.EXPIRED_CODE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private boolean isActionActive(ClientSessionCode.ActionType actionType) {
|
||||
if (!clientCode.isActionActive(actionType)) {
|
||||
event.client(getAuthenticationSession().getClient());
|
||||
event.clone().error(Errors.EXPIRED_CODE);
|
||||
|
||||
AuthenticationSessionModel authSession = getAuthenticationSession();
|
||||
AuthenticationProcessor.resetFlow(authSession, AUTHENTICATE_PATH);
|
||||
response = processAuthentication(false, null, authSession, Messages.LOGIN_TIMEOUT);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private AuthenticationSessionModel initialVerifyAuthSession() {
|
||||
// Basic realm checks
|
||||
if (!checkSsl()) {
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
response = ErrorPage.error(session, Messages.HTTPS_REQUIRED);
|
||||
return null;
|
||||
}
|
||||
if (!realm.isEnabled()) {
|
||||
event.error(Errors.REALM_DISABLED);
|
||||
response = ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
|
||||
return null;
|
||||
}
|
||||
|
||||
// object retrieve
|
||||
AuthenticationSessionModel authSession = ClientSessionCode.getClientSession(code, session, realm, AuthenticationSessionModel.class);
|
||||
if (authSession != null) {
|
||||
return authSession;
|
||||
}
|
||||
|
||||
// See if we are already authenticated and userSession with same ID exists.
|
||||
String sessionId = new AuthenticationSessionManager(session).getCurrentAuthenticationSessionId(realm);
|
||||
if (sessionId != null) {
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
|
||||
if (userSession != null) {
|
||||
|
||||
LoginFormsProvider loginForm = session.getProvider(LoginFormsProvider.class)
|
||||
.setSuccess(Messages.ALREADY_LOGGED_IN);
|
||||
|
||||
ClientModel client = null;
|
||||
String lastClientUuid = userSession.getNote(AuthenticationManager.LAST_AUTHENTICATED_CLIENT);
|
||||
if (lastClientUuid != null) {
|
||||
client = realm.getClientById(lastClientUuid);
|
||||
}
|
||||
|
||||
if (client != null) {
|
||||
session.getContext().setClient(client);
|
||||
} else {
|
||||
loginForm.setAttribute("skipLink", true);
|
||||
}
|
||||
|
||||
response = loginForm.createInfoPage();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise just try to restart from the cookie
|
||||
response = restartAuthenticationSessionFromCookie();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private boolean initialVerify() {
|
||||
// Basic realm checks and authenticationSession retrieve
|
||||
AuthenticationSessionModel authSession = initialVerifyAuthSession();
|
||||
if (authSession == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check cached response from previous action request
|
||||
response = BrowserHistoryHelper.getInstance().loadSavedResponse(session, authSession);
|
||||
if (response != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Client checks
|
||||
event.detail(Details.CODE_ID, authSession.getId());
|
||||
ClientModel client = authSession.getClient();
|
||||
if (client == null) {
|
||||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
response = ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
|
||||
clientCode.removeExpiredClientSession();
|
||||
return false;
|
||||
}
|
||||
if (!client.isEnabled()) {
|
||||
event.error(Errors.CLIENT_DISABLED);
|
||||
response = ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
|
||||
clientCode.removeExpiredClientSession();
|
||||
return false;
|
||||
}
|
||||
session.getContext().setClient(client);
|
||||
|
||||
|
||||
// Check if it's action or not
|
||||
if (code == null) {
|
||||
String lastExecFromSession = authSession.getAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
|
||||
String lastFlow = authSession.getAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH);
|
||||
|
||||
// Check if we transitted between flows (eg. clicking "register" on login screen)
|
||||
if (execution==null && !flowPath.equals(lastFlow)) {
|
||||
logger.infof("Transition between flows! Current flow: %s, Previous flow: %s", flowPath, lastFlow);
|
||||
|
||||
if (lastFlow == null || isFlowTransitionAllowed(flowPath, lastFlow)) {
|
||||
authSession.setAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH, flowPath);
|
||||
authSession.removeAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
|
||||
lastExecFromSession = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (ObjectUtil.isEqualOrBothNull(execution, lastExecFromSession)) {
|
||||
// Allow refresh of previous page
|
||||
clientCode = new ClientSessionCode<>(session, realm, authSession);
|
||||
actionRequest = false;
|
||||
return true;
|
||||
} else {
|
||||
response = showPageExpired(authSession);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
result = ClientSessionCode.parseResult(code, session, realm, AuthenticationSessionModel.class);
|
||||
clientCode = result.getCode();
|
||||
if (clientCode == null) {
|
||||
|
||||
// In case that is replayed action, but sent to the same FORM like actual FORM, we just re-render the page
|
||||
if (allowRefresh && ObjectUtil.isEqualOrBothNull(execution, authSession.getAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION))) {
|
||||
String latestFlowPath = authSession.getAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH);
|
||||
URI redirectUri = getLastExecutionUrl(latestFlowPath, execution);
|
||||
|
||||
logger.infof("Invalid action code, but execution matches. So just redirecting to %s", redirectUri);
|
||||
authSession.setAuthNote(FORWARDED_ERROR_MESSAGE_NOTE, Messages.EXPIRED_ACTION);
|
||||
response = Response.status(Response.Status.FOUND).location(redirectUri).build();
|
||||
} else {
|
||||
response = showPageExpired(authSession);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
actionRequest = execution != null;
|
||||
authSession.setAuthNote(AuthenticationProcessor.LAST_PROCESSED_EXECUTION, execution);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean verifyRequiredAction(String executedAction) {
|
||||
if (failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!clientCode.isValidAction(ClientSessionModel.Action.REQUIRED_ACTIONS.name())) {
|
||||
// TODO:mposolda debug or trace
|
||||
logger.infof("Expected required action, but session action is '%s' . Showing expired page now.", getAuthenticationSession().getAction());
|
||||
event.client(getAuthenticationSession().getClient());
|
||||
event.error(Errors.INVALID_CODE);
|
||||
|
||||
response = showPageExpired(getAuthenticationSession());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isActionActive(ClientSessionCode.ActionType.USER)) return false;
|
||||
|
||||
final AuthenticationSessionModel authSession = getAuthenticationSession();
|
||||
|
||||
if (actionRequest) {
|
||||
String currentRequiredAction = authSession.getAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
|
||||
if (executedAction == null || !executedAction.equals(currentRequiredAction)) {
|
||||
logger.debug("required action doesn't match current required action");
|
||||
response = redirectToRequiredActions(currentRequiredAction);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean isFlowTransitionAllowed(String currentFlow, String previousFlow) {
|
||||
if (currentFlow.equals(AUTHENTICATE_PATH) && (previousFlow.equals(REGISTRATION_PATH) || previousFlow.equals(RESET_CREDENTIALS_PATH))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (currentFlow.equals(REGISTRATION_PATH) && (previousFlow.equals(AUTHENTICATE_PATH))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (currentFlow.equals(RESET_CREDENTIALS_PATH) && (previousFlow.equals(AUTHENTICATE_PATH) || previousFlow.equals(FIRST_BROKER_LOGIN_PATH))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (currentFlow.equals(FIRST_BROKER_LOGIN_PATH) && (previousFlow.equals(AUTHENTICATE_PATH) || previousFlow.equals(POST_BROKER_LOGIN_PATH))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (currentFlow.equals(POST_BROKER_LOGIN_PATH) && (previousFlow.equals(AUTHENTICATE_PATH) || previousFlow.equals(FIRST_BROKER_LOGIN_PATH))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
protected Response restartAuthenticationSessionFromCookie() {
|
||||
logger.info("Authentication session not found. Trying to restart from cookie.");
|
||||
AuthenticationSessionModel authSession = null;
|
||||
try {
|
||||
authSession = RestartLoginCookie.restartSession(session, realm);
|
||||
} catch (Exception e) {
|
||||
ServicesLogger.LOGGER.failedToParseRestartLoginCookie(e);
|
||||
}
|
||||
|
||||
if (authSession != null) {
|
||||
|
||||
event.clone();
|
||||
event.detail(Details.RESTART_AFTER_TIMEOUT, "true");
|
||||
event.error(Errors.EXPIRED_CODE);
|
||||
|
||||
String warningMessage = Messages.LOGIN_TIMEOUT;
|
||||
authSession.setAuthNote(FORWARDED_ERROR_MESSAGE_NOTE, warningMessage);
|
||||
|
||||
String flowPath = authSession.getClientNote(AuthorizationEndpointBase.APP_INITIATED_FLOW);
|
||||
if (flowPath == null) {
|
||||
flowPath = AUTHENTICATE_PATH;
|
||||
}
|
||||
|
||||
URI redirectUri = getLastExecutionUrl(flowPath, null);
|
||||
logger.infof("Authentication session restart from cookie succeeded. Redirecting to %s", redirectUri);
|
||||
return Response.status(Response.Status.FOUND).location(redirectUri).build();
|
||||
} else {
|
||||
event.error(Errors.INVALID_CODE);
|
||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected Response showPageExpired(AuthenticationSessionModel authSession) {
|
||||
return new PageExpiredRedirect(session, realm, uriInfo)
|
||||
.showPageExpired(authSession);
|
||||
}
|
||||
|
||||
|
||||
protected URI getLastExecutionUrl(String flowPath, String executionId) {
|
||||
return new PageExpiredRedirect(session, realm, uriInfo)
|
||||
return new AuthenticationFlowURLHelper(session, realm, uriInfo)
|
||||
.getLastExecutionUrl(flowPath, executionId);
|
||||
}
|
||||
|
||||
|
@ -525,11 +202,11 @@ public class LoginActionsService {
|
|||
@GET
|
||||
public Response restartSession() {
|
||||
event.event(EventType.RESTART_AUTHENTICATION);
|
||||
SessionCodeChecks checks = new SessionCodeChecks(null, null, null);
|
||||
SessionCodeChecks checks = new SessionCodeChecks(realm, uriInfo, clientConnection, session, event, null, null, null);
|
||||
|
||||
AuthenticationSessionModel authSession = checks.initialVerifyAuthSession();
|
||||
if (authSession == null) {
|
||||
return checks.response;
|
||||
return checks.getResponse();
|
||||
}
|
||||
|
||||
String flowPath = authSession.getClientNote(AuthorizationEndpointBase.APP_INITIATED_FLOW);
|
||||
|
@ -539,11 +216,8 @@ public class LoginActionsService {
|
|||
|
||||
AuthenticationProcessor.resetFlow(authSession, flowPath);
|
||||
|
||||
// TODO:mposolda Check it's better to put it to AuthenticationProcessor.resetFlow (with consider of brokering)
|
||||
authSession.setAction(CommonClientSessionModel.Action.AUTHENTICATE.name());
|
||||
|
||||
URI redirectUri = getLastExecutionUrl(flowPath, null);
|
||||
logger.infof("Flow restart requested. Redirecting to %s", redirectUri);
|
||||
logger.debugf("Flow restart requested. Redirecting to %s", redirectUri);
|
||||
return Response.status(Response.Status.FOUND).location(redirectUri).build();
|
||||
}
|
||||
|
||||
|
@ -561,12 +235,12 @@ public class LoginActionsService {
|
|||
event.event(EventType.LOGIN);
|
||||
|
||||
SessionCodeChecks checks = checksForCode(code, execution, AUTHENTICATE_PATH);
|
||||
if (!checks.verifyCode(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||
return checks.response;
|
||||
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||
return checks.getResponse();
|
||||
}
|
||||
|
||||
AuthenticationSessionModel authSession = checks.getAuthenticationSession();
|
||||
boolean actionRequest = checks.actionRequest;
|
||||
boolean actionRequest = checks.isActionRequest();
|
||||
|
||||
return processAuthentication(actionRequest, execution, authSession, null);
|
||||
}
|
||||
|
@ -605,6 +279,9 @@ public class LoginActionsService {
|
|||
} else {
|
||||
response = processor.authenticate();
|
||||
}
|
||||
} catch (WebApplicationException e) {
|
||||
response = e.getResponse();
|
||||
authSession = processor.getAuthenticationSession();
|
||||
} catch (Exception e) {
|
||||
response = processor.handleBrowserException(e);
|
||||
authSession = processor.getAuthenticationSession(); // Could be changed (eg. Forked flow)
|
||||
|
@ -696,30 +373,26 @@ public class LoginActionsService {
|
|||
*/
|
||||
protected Response resetCredentials(String code, String execution) {
|
||||
SessionCodeChecks checks = checksForCode(code, execution, RESET_CREDENTIALS_PATH);
|
||||
if (!checks.verifyCode(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.USER)) {
|
||||
return checks.response;
|
||||
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.USER)) {
|
||||
return checks.getResponse();
|
||||
}
|
||||
final AuthenticationSessionModel authSession = checks.getAuthenticationSession();
|
||||
|
||||
if (!realm.isResetPasswordAllowed()) {
|
||||
if (authSession != null) {
|
||||
event.client(authSession.getClient());
|
||||
}
|
||||
event.error(Errors.NOT_ALLOWED);
|
||||
return ErrorPage.error(session, Messages.RESET_CREDENTIAL_NOT_ALLOWED);
|
||||
|
||||
}
|
||||
|
||||
return processResetCredentials(checks.actionRequest, execution, authSession);
|
||||
return processResetCredentials(checks.isActionRequest(), execution, authSession);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a given token using the given token handler. If there is any {@link VerificationException} thrown
|
||||
* in the handler, it is handled automatically here to reduce boilerplate code.
|
||||
*
|
||||
* @param tokenString Original token string
|
||||
* @param eventError
|
||||
* @param defaultErrorMessage
|
||||
* @param key
|
||||
* @param execution
|
||||
* @return
|
||||
*/
|
||||
@Path("action-token")
|
||||
|
@ -888,30 +561,7 @@ public class LoginActionsService {
|
|||
}
|
||||
|
||||
protected Response processResetCredentials(boolean actionRequest, String execution, AuthenticationSessionModel authSession) {
|
||||
AuthenticationProcessor authProcessor = new AuthenticationProcessor() {
|
||||
|
||||
@Override
|
||||
protected Response authenticationComplete() {
|
||||
boolean firstBrokerLoginInProgress = (authenticationSession.getAuthNote(AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE) != null);
|
||||
if (firstBrokerLoginInProgress) {
|
||||
|
||||
UserModel linkingUser = AbstractIdpAuthenticator.getExistingUser(session, realm, authenticationSession);
|
||||
if (!linkingUser.getId().equals(authenticationSession.getAuthenticatedUser().getId())) {
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_DIFFERENT_USER_MESSAGE, authenticationSession.getAuthenticatedUser().getUsername(), linkingUser.getUsername());
|
||||
}
|
||||
|
||||
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromAuthenticationSession(authSession, AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE);
|
||||
authSession.setAuthNote(AbstractIdpAuthenticator.FIRST_BROKER_LOGIN_SUCCESS, serializedCtx.getIdentityProviderId());
|
||||
|
||||
logger.debugf("Forget-password flow finished when authenticated user '%s' after first broker login with identity provider '%s'.",
|
||||
linkingUser.getUsername(), serializedCtx.getIdentityProviderId());
|
||||
|
||||
return redirectToAfterBrokerLoginEndpoint(authSession, true);
|
||||
} else {
|
||||
return super.authenticationComplete();
|
||||
}
|
||||
}
|
||||
};
|
||||
AuthenticationProcessor authProcessor = new ResetCredentialsActionTokenHandler.ResetCredsAuthenticationProcessor();
|
||||
|
||||
return processFlow(actionRequest, execution, authSession, RESET_CREDENTIALS_PATH, realm.getResetCredentialsFlow(), null, authProcessor);
|
||||
}
|
||||
|
@ -958,19 +608,15 @@ public class LoginActionsService {
|
|||
}
|
||||
|
||||
SessionCodeChecks checks = checksForCode(code, execution, REGISTRATION_PATH);
|
||||
if (!checks.verifyCode(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||
return checks.response;
|
||||
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||
return checks.getResponse();
|
||||
}
|
||||
|
||||
ClientSessionCode<AuthenticationSessionModel> clientSessionCode = checks.clientCode;
|
||||
AuthenticationSessionModel clientSession = clientSessionCode.getClientSession();
|
||||
AuthenticationSessionModel authSession = checks.getAuthenticationSession();
|
||||
|
||||
// TODO:mposolda any consequences to do this for POST request too?
|
||||
if (!isPostRequest) {
|
||||
AuthenticationManager.expireIdentityCookie(realm, uriInfo, clientConnection);
|
||||
}
|
||||
AuthenticationManager.expireIdentityCookie(realm, uriInfo, clientConnection);
|
||||
|
||||
return processRegistration(checks.actionRequest, execution, clientSession, null);
|
||||
return processRegistration(checks.isActionRequest(), execution, authSession, null);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1010,8 +656,8 @@ public class LoginActionsService {
|
|||
event.event(eventType);
|
||||
|
||||
SessionCodeChecks checks = checksForCode(code, execution, flowPath);
|
||||
if (!checks.verifyCode(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||
return checks.response;
|
||||
if (!checks.verifyActiveAndValidAction(ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
|
||||
return checks.getResponse();
|
||||
}
|
||||
event.detail(Details.CODE_ID, code);
|
||||
final AuthenticationSessionModel authSession = checks.getAuthenticationSession();
|
||||
|
@ -1056,10 +702,14 @@ public class LoginActionsService {
|
|||
|
||||
};
|
||||
|
||||
return processFlow(checks.actionRequest, execution, authSession, flowPath, brokerLoginFlow, null, processor);
|
||||
return processFlow(checks.isActionRequest(), execution, authSession, flowPath, brokerLoginFlow, null, processor);
|
||||
}
|
||||
|
||||
private Response redirectToAfterBrokerLoginEndpoint(AuthenticationSessionModel authSession, boolean firstBrokerLogin) {
|
||||
return redirectToAfterBrokerLoginEndpoint(session, realm, uriInfo, authSession, firstBrokerLogin);
|
||||
}
|
||||
|
||||
public static Response redirectToAfterBrokerLoginEndpoint(KeycloakSession session, RealmModel realm, UriInfo uriInfo, AuthenticationSessionModel authSession, boolean firstBrokerLogin) {
|
||||
ClientSessionCode<AuthenticationSessionModel> accessCode = new ClientSessionCode<>(session, realm, authSession);
|
||||
authSession.setTimestamp(Time.currentTime());
|
||||
|
||||
|
@ -1085,11 +735,10 @@ public class LoginActionsService {
|
|||
String code = formData.getFirst("code");
|
||||
SessionCodeChecks checks = checksForCode(code, null, REQUIRED_ACTION);
|
||||
if (!checks.verifyRequiredAction(ClientSessionModel.Action.OAUTH_GRANT.name())) {
|
||||
return checks.response;
|
||||
return checks.getResponse();
|
||||
}
|
||||
|
||||
ClientSessionCode<AuthenticationSessionModel> accessCode = checks.clientCode;
|
||||
AuthenticationSessionModel authSession = accessCode.getClientSession();
|
||||
AuthenticationSessionModel authSession = checks.getAuthenticationSession();
|
||||
|
||||
initLoginEvent(authSession);
|
||||
|
||||
|
@ -1113,10 +762,10 @@ public class LoginActionsService {
|
|||
grantedConsent = new UserConsentModel(client);
|
||||
session.users().addConsent(realm, user.getId(), grantedConsent);
|
||||
}
|
||||
for (RoleModel role : accessCode.getRequestedRoles()) {
|
||||
for (RoleModel role : ClientSessionCode.getRequestedRoles(authSession, realm)) {
|
||||
grantedConsent.addGrantedRole(role);
|
||||
}
|
||||
for (ProtocolMapperModel protocolMapper : accessCode.getRequestedProtocolMappers()) {
|
||||
for (ProtocolMapperModel protocolMapper : ClientSessionCode.getRequestedProtocolMappers(authSession.getProtocolMappers(), client)) {
|
||||
if (protocolMapper.isConsentRequired() && protocolMapper.getConsentText() != null) {
|
||||
grantedConsent.addGrantedProtocolMapper(protocolMapper);
|
||||
}
|
||||
|
@ -1130,23 +779,6 @@ public class LoginActionsService {
|
|||
return AuthenticationManager.redirectAfterSuccessfulFlow(session, realm, clientSession.getUserSession(), clientSession, request, uriInfo, clientConnection, event, authSession.getProtocol());
|
||||
}
|
||||
|
||||
@Path("email-verification")
|
||||
@GET
|
||||
public Response emailVerification(@QueryParam("code") String code, @QueryParam("execution") String execution) {
|
||||
event.event(EventType.SEND_VERIFY_EMAIL);
|
||||
|
||||
SessionCodeChecks checks = checksForCodeRefreshNotAllowed(code, execution, REQUIRED_ACTION);
|
||||
if (!checks.verifyCode(ClientSessionModel.Action.REQUIRED_ACTIONS.name(), ClientSessionCode.ActionType.USER)) {
|
||||
return checks.response;
|
||||
}
|
||||
ClientSessionCode accessCode = checks.clientCode;
|
||||
AuthenticationSessionModel authSession = checks.getAuthenticationSession();
|
||||
initLoginEvent(authSession);
|
||||
|
||||
event.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, authSession.getAuthenticatedUser().getEmail()).success();
|
||||
|
||||
return VerifyEmail.sendVerifyEmail(session, accessCode.getCode(), authSession.getAuthenticatedUser(), authSession);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiated by admin, not the user on login
|
||||
|
@ -1210,11 +842,12 @@ public class LoginActionsService {
|
|||
}
|
||||
event.detail(Details.REMEMBER_ME, rememberMe);
|
||||
|
||||
// TODO:mposolda Fix if this is called at firstBroker or postBroker login
|
||||
/*
|
||||
.detail(Details.IDENTITY_PROVIDER, userSession.getNote(Details.IDENTITY_PROVIDER))
|
||||
.detail(Details.IDENTITY_PROVIDER_USERNAME, userSession.getNote(Details.IDENTITY_PROVIDER_USERNAME));
|
||||
*/
|
||||
Map<String, String> userSessionNotes = authSession.getUserSessionNotes();
|
||||
String identityProvider = userSessionNotes.get(Details.IDENTITY_PROVIDER);
|
||||
if (identityProvider != null) {
|
||||
event.detail(Details.IDENTITY_PROVIDER, identityProvider)
|
||||
.detail(Details.IDENTITY_PROVIDER_USERNAME, userSessionNotes.get(Details.IDENTITY_PROVIDER_USERNAME));
|
||||
}
|
||||
}
|
||||
|
||||
@Path(REQUIRED_ACTION)
|
||||
|
@ -1236,11 +869,11 @@ public class LoginActionsService {
|
|||
|
||||
SessionCodeChecks checks = checksForCode(code, action, REQUIRED_ACTION);
|
||||
if (!checks.verifyRequiredAction(action)) {
|
||||
return checks.response;
|
||||
return checks.getResponse();
|
||||
}
|
||||
|
||||
AuthenticationSessionModel authSession = checks.getAuthenticationSession();
|
||||
if (!checks.actionRequest) {
|
||||
if (!checks.isActionRequest()) {
|
||||
initLoginEvent(authSession);
|
||||
event.event(EventType.CUSTOM_REQUIRED_ACTION);
|
||||
return AuthenticationManager.nextActionAfterAuthentication(session, authSession, clientConnection, request, uriInfo, event);
|
||||
|
@ -1268,7 +901,9 @@ public class LoginActionsService {
|
|||
Response response;
|
||||
provider.processAction(context);
|
||||
|
||||
authSession.setAuthNote(AuthenticationProcessor.LAST_PROCESSED_EXECUTION, action);
|
||||
if (action != null) {
|
||||
authSession.setAuthNote(AuthenticationProcessor.LAST_PROCESSED_EXECUTION, action);
|
||||
}
|
||||
|
||||
if (context.getStatus() == RequiredActionContext.Status.SUCCESS) {
|
||||
event.clone().success();
|
||||
|
@ -1298,15 +933,4 @@ public class LoginActionsService {
|
|||
return BrowserHistoryHelper.getInstance().saveResponseAndRedirect(session, authSession, response, true);
|
||||
}
|
||||
|
||||
private Response redirectToRequiredActions(String action) {
|
||||
UriBuilder uriBuilder = LoginActionsService.loginActionsBaseUrl(uriInfo)
|
||||
.path(LoginActionsService.REQUIRED_ACTION);
|
||||
|
||||
if (action != null) {
|
||||
uriBuilder.queryParam("execution", action);
|
||||
}
|
||||
URI redirect = uriBuilder.build(realm.getName());
|
||||
return Response.status(302).location(redirect).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.services.resources;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authentication.AuthenticationProcessor;
|
||||
import org.keycloak.common.ClientConnection;
|
||||
import org.keycloak.common.util.ObjectUtil;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.forms.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.AuthorizationEndpointBase;
|
||||
import org.keycloak.protocol.RestartLoginCookie;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.AuthenticationSessionManager;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.util.BrowserHistoryHelper;
|
||||
import org.keycloak.services.util.AuthenticationFlowURLHelper;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
|
||||
|
||||
public class SessionCodeChecks {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SessionCodeChecks.class);
|
||||
|
||||
private AuthenticationSessionModel authSession;
|
||||
private ClientSessionCode<AuthenticationSessionModel> clientCode;
|
||||
private Response response;
|
||||
private boolean actionRequest;
|
||||
|
||||
private final RealmModel realm;
|
||||
private final UriInfo uriInfo;
|
||||
private final ClientConnection clientConnection;
|
||||
private final KeycloakSession session;
|
||||
private final EventBuilder event;
|
||||
|
||||
private final String code;
|
||||
private final String execution;
|
||||
private final String flowPath;
|
||||
|
||||
|
||||
public SessionCodeChecks(RealmModel realm, UriInfo uriInfo, ClientConnection clientConnection, KeycloakSession session, EventBuilder event, String code, String execution, String flowPath) {
|
||||
this.realm = realm;
|
||||
this.uriInfo = uriInfo;
|
||||
this.clientConnection = clientConnection;
|
||||
this.session = session;
|
||||
this.event = event;
|
||||
|
||||
this.code = code;
|
||||
this.execution = execution;
|
||||
this.flowPath = flowPath;
|
||||
}
|
||||
|
||||
|
||||
public AuthenticationSessionModel getAuthenticationSession() {
|
||||
return authSession;
|
||||
}
|
||||
|
||||
|
||||
private boolean failed() {
|
||||
return response != null;
|
||||
}
|
||||
|
||||
|
||||
public Response getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
public ClientSessionCode<AuthenticationSessionModel> getClientCode() {
|
||||
return clientCode;
|
||||
}
|
||||
|
||||
public boolean isActionRequest() {
|
||||
return actionRequest;
|
||||
}
|
||||
|
||||
|
||||
private boolean checkSsl() {
|
||||
if (uriInfo.getBaseUri().getScheme().equals("https")) {
|
||||
return true;
|
||||
} else {
|
||||
return !realm.getSslRequired().isRequired(clientConnection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public AuthenticationSessionModel initialVerifyAuthSession() {
|
||||
// Basic realm checks
|
||||
if (!checkSsl()) {
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
response = ErrorPage.error(session, Messages.HTTPS_REQUIRED);
|
||||
return null;
|
||||
}
|
||||
if (!realm.isEnabled()) {
|
||||
event.error(Errors.REALM_DISABLED);
|
||||
response = ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
|
||||
return null;
|
||||
}
|
||||
|
||||
// object retrieve
|
||||
AuthenticationSessionModel authSession = ClientSessionCode.getClientSession(code, session, realm, AuthenticationSessionModel.class);
|
||||
if (authSession != null) {
|
||||
return authSession;
|
||||
}
|
||||
|
||||
// See if we are already authenticated and userSession with same ID exists.
|
||||
String sessionId = new AuthenticationSessionManager(session).getCurrentAuthenticationSessionId(realm);
|
||||
if (sessionId != null) {
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
|
||||
if (userSession != null) {
|
||||
|
||||
LoginFormsProvider loginForm = session.getProvider(LoginFormsProvider.class)
|
||||
.setSuccess(Messages.ALREADY_LOGGED_IN);
|
||||
|
||||
ClientModel client = null;
|
||||
String lastClientUuid = userSession.getNote(AuthenticationManager.LAST_AUTHENTICATED_CLIENT);
|
||||
if (lastClientUuid != null) {
|
||||
client = realm.getClientById(lastClientUuid);
|
||||
}
|
||||
|
||||
if (client != null) {
|
||||
session.getContext().setClient(client);
|
||||
} else {
|
||||
loginForm.setAttribute("skipLink", true);
|
||||
}
|
||||
|
||||
response = loginForm.createInfoPage();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise just try to restart from the cookie
|
||||
response = restartAuthenticationSessionFromCookie();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public boolean initialVerify() {
|
||||
// Basic realm checks and authenticationSession retrieve
|
||||
authSession = initialVerifyAuthSession();
|
||||
if (authSession == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check cached response from previous action request
|
||||
response = BrowserHistoryHelper.getInstance().loadSavedResponse(session, authSession);
|
||||
if (response != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Client checks
|
||||
event.detail(Details.CODE_ID, authSession.getId());
|
||||
ClientModel client = authSession.getClient();
|
||||
if (client == null) {
|
||||
event.error(Errors.CLIENT_NOT_FOUND);
|
||||
response = ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
|
||||
clientCode.removeExpiredClientSession();
|
||||
return false;
|
||||
}
|
||||
|
||||
event.client(client);
|
||||
session.getContext().setClient(client);
|
||||
|
||||
if (!client.isEnabled()) {
|
||||
event.error(Errors.CLIENT_DISABLED);
|
||||
response = ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
|
||||
clientCode.removeExpiredClientSession();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Check if it's action or not
|
||||
if (code == null) {
|
||||
String lastExecFromSession = authSession.getAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
|
||||
String lastFlow = authSession.getAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH);
|
||||
|
||||
// Check if we transitted between flows (eg. clicking "register" on login screen)
|
||||
if (execution==null && !flowPath.equals(lastFlow)) {
|
||||
logger.debugf("Transition between flows! Current flow: %s, Previous flow: %s", flowPath, lastFlow);
|
||||
|
||||
// Don't allow moving to different flow if I am on requiredActions already
|
||||
if (ClientSessionModel.Action.AUTHENTICATE.name().equals(authSession.getAction())) {
|
||||
authSession.setAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH, flowPath);
|
||||
authSession.removeAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
|
||||
lastExecFromSession = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (ObjectUtil.isEqualOrBothNull(execution, lastExecFromSession)) {
|
||||
// Allow refresh of previous page
|
||||
clientCode = new ClientSessionCode<>(session, realm, authSession);
|
||||
actionRequest = false;
|
||||
return true;
|
||||
} else {
|
||||
response = showPageExpired(authSession);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ClientSessionCode.ParseResult<AuthenticationSessionModel> result = ClientSessionCode.parseResult(code, session, realm, AuthenticationSessionModel.class);
|
||||
clientCode = result.getCode();
|
||||
if (clientCode == null) {
|
||||
|
||||
// In case that is replayed action, but sent to the same FORM like actual FORM, we just re-render the page
|
||||
if (ObjectUtil.isEqualOrBothNull(execution, authSession.getAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION))) {
|
||||
String latestFlowPath = authSession.getAuthNote(AuthenticationProcessor.CURRENT_FLOW_PATH);
|
||||
URI redirectUri = getLastExecutionUrl(latestFlowPath, execution);
|
||||
|
||||
logger.debugf("Invalid action code, but execution matches. So just redirecting to %s", redirectUri);
|
||||
authSession.setAuthNote(LoginActionsService.FORWARDED_ERROR_MESSAGE_NOTE, Messages.EXPIRED_ACTION);
|
||||
response = Response.status(Response.Status.FOUND).location(redirectUri).build();
|
||||
} else {
|
||||
response = showPageExpired(authSession);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
actionRequest = true;
|
||||
if (execution != null) {
|
||||
authSession.setAuthNote(AuthenticationProcessor.LAST_PROCESSED_EXECUTION, execution);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean verifyActiveAndValidAction(String expectedAction, ClientSessionCode.ActionType actionType) {
|
||||
if (failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isActionActive(actionType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!clientCode.isValidAction(expectedAction)) {
|
||||
AuthenticationSessionModel authSession = getAuthenticationSession();
|
||||
if (ClientSessionModel.Action.REQUIRED_ACTIONS.name().equals(authSession.getAction())) {
|
||||
logger.debugf("Incorrect action '%s' . User authenticated already.", authSession.getAction());
|
||||
response = showPageExpired(authSession);
|
||||
return false;
|
||||
} else {
|
||||
logger.errorf("Bad action. Expected action '%s', current action '%s'", expectedAction, authSession.getAction());
|
||||
response = ErrorPage.error(session, Messages.EXPIRED_CODE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private boolean isActionActive(ClientSessionCode.ActionType actionType) {
|
||||
if (!clientCode.isActionActive(actionType)) {
|
||||
event.clone().error(Errors.EXPIRED_CODE);
|
||||
|
||||
AuthenticationProcessor.resetFlow(authSession, LoginActionsService.AUTHENTICATE_PATH);
|
||||
|
||||
authSession.setAuthNote(LoginActionsService.FORWARDED_ERROR_MESSAGE_NOTE, Messages.LOGIN_TIMEOUT);
|
||||
|
||||
URI redirectUri = getLastExecutionUrl(LoginActionsService.AUTHENTICATE_PATH, null);
|
||||
logger.debugf("Flow restart after timeout. Redirecting to %s", redirectUri);
|
||||
response = Response.status(Response.Status.FOUND).location(redirectUri).build();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public boolean verifyRequiredAction(String executedAction) {
|
||||
if (failed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!clientCode.isValidAction(ClientSessionModel.Action.REQUIRED_ACTIONS.name())) {
|
||||
logger.debugf("Expected required action, but session action is '%s' . Showing expired page now.", authSession.getAction());
|
||||
event.error(Errors.INVALID_CODE);
|
||||
|
||||
response = showPageExpired(authSession);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isActionActive(ClientSessionCode.ActionType.USER)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actionRequest) {
|
||||
String currentRequiredAction = authSession.getAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
|
||||
if (executedAction == null || !executedAction.equals(currentRequiredAction)) {
|
||||
logger.debug("required action doesn't match current required action");
|
||||
response = redirectToRequiredActions(currentRequiredAction);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private Response restartAuthenticationSessionFromCookie() {
|
||||
logger.debug("Authentication session not found. Trying to restart from cookie.");
|
||||
AuthenticationSessionModel authSession = null;
|
||||
try {
|
||||
authSession = RestartLoginCookie.restartSession(session, realm);
|
||||
} catch (Exception e) {
|
||||
ServicesLogger.LOGGER.failedToParseRestartLoginCookie(e);
|
||||
}
|
||||
|
||||
if (authSession != null) {
|
||||
|
||||
event.clone();
|
||||
event.detail(Details.RESTART_AFTER_TIMEOUT, "true");
|
||||
event.error(Errors.EXPIRED_CODE);
|
||||
|
||||
String warningMessage = Messages.LOGIN_TIMEOUT;
|
||||
authSession.setAuthNote(LoginActionsService.FORWARDED_ERROR_MESSAGE_NOTE, warningMessage);
|
||||
|
||||
String flowPath = authSession.getClientNote(AuthorizationEndpointBase.APP_INITIATED_FLOW);
|
||||
if (flowPath == null) {
|
||||
flowPath = LoginActionsService.AUTHENTICATE_PATH;
|
||||
}
|
||||
|
||||
URI redirectUri = getLastExecutionUrl(flowPath, null);
|
||||
logger.debugf("Authentication session restart from cookie succeeded. Redirecting to %s", redirectUri);
|
||||
return Response.status(Response.Status.FOUND).location(redirectUri).build();
|
||||
} else {
|
||||
// Finally need to show error as all the fallbacks failed
|
||||
event.error(Errors.INVALID_CODE);
|
||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Response redirectToRequiredActions(String action) {
|
||||
UriBuilder uriBuilder = LoginActionsService.loginActionsBaseUrl(uriInfo)
|
||||
.path(LoginActionsService.REQUIRED_ACTION);
|
||||
|
||||
if (action != null) {
|
||||
uriBuilder.queryParam("execution", action);
|
||||
}
|
||||
URI redirect = uriBuilder.build(realm.getName());
|
||||
return Response.status(302).location(redirect).build();
|
||||
}
|
||||
|
||||
|
||||
private URI getLastExecutionUrl(String flowPath, String executionId) {
|
||||
return new AuthenticationFlowURLHelper(session, realm, uriInfo)
|
||||
.getLastExecutionUrl(flowPath, executionId);
|
||||
}
|
||||
|
||||
|
||||
private Response showPageExpired(AuthenticationSessionModel authSession) {
|
||||
return new AuthenticationFlowURLHelper(session, realm, uriInfo)
|
||||
.showPageExpired(authSession);
|
||||
}
|
||||
|
||||
}
|
|
@ -25,8 +25,8 @@ import org.keycloak.common.Profile;
|
|||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
|
@ -492,8 +492,11 @@ public class ClientResource {
|
|||
UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(userSession);
|
||||
|
||||
// Update lastSessionRefresh with the timestamp from clientSession
|
||||
for (ClientSessionModel clientSession : userSession.getClientSessions()) {
|
||||
if (client.getId().equals(clientSession.getClient().getId())) {
|
||||
for (Map.Entry<String, AuthenticatedClientSessionModel> csEntry : userSession.getAuthenticatedClientSessions().entrySet()) {
|
||||
String clientUuid = csEntry.getKey();
|
||||
AuthenticatedClientSessionModel clientSession = csEntry.getValue();
|
||||
|
||||
if (client.getId().equals(clientUuid)) {
|
||||
rep.setLastAccess(Time.toMillis(clientSession.getTimestamp()));
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ public class ClearExpiredUserSessions implements ScheduledTask {
|
|||
UserSessionProvider sessions = session.sessions();
|
||||
for (RealmModel realm : session.realms().getRealms()) {
|
||||
sessions.removeExpired(realm);
|
||||
session.authenticationSessions().removeExpired(realm);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,15 +35,15 @@ import org.keycloak.sessions.AuthenticationSessionModel;
|
|||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class PageExpiredRedirect {
|
||||
public class AuthenticationFlowURLHelper {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(PageExpiredRedirect.class);
|
||||
protected static final Logger logger = Logger.getLogger(AuthenticationFlowURLHelper.class);
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final RealmModel realm;
|
||||
private final UriInfo uriInfo;
|
||||
|
||||
public PageExpiredRedirect(KeycloakSession session, RealmModel realm, UriInfo uriInfo) {
|
||||
public AuthenticationFlowURLHelper(KeycloakSession session, RealmModel realm, UriInfo uriInfo) {
|
||||
this.session = session;
|
||||
this.realm = realm;
|
||||
this.uriInfo = uriInfo;
|
||||
|
@ -53,7 +53,7 @@ public class PageExpiredRedirect {
|
|||
public Response showPageExpired(AuthenticationSessionModel authSession) {
|
||||
URI lastStepUrl = getLastExecutionUrl(authSession);
|
||||
|
||||
logger.infof("Redirecting to 'page expired' now. Will use last step URL: %s", lastStepUrl);
|
||||
logger.debugf("Redirecting to 'page expired' now. Will use last step URL: %s", lastStepUrl);
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setActionUri(lastStepUrl)
|
|
@ -77,7 +77,7 @@ public abstract class BrowserHistoryHelper {
|
|||
if (entity instanceof String) {
|
||||
String responseString = (String) entity;
|
||||
|
||||
URI lastExecutionURL = new PageExpiredRedirect(session, session.getContext().getRealm(), session.getContext().getUri()).getLastExecutionUrl(authSession);
|
||||
URI lastExecutionURL = new AuthenticationFlowURLHelper(session, session.getContext().getRealm(), session.getContext().getUri()).getLastExecutionUrl(authSession);
|
||||
|
||||
// Inject javascript for history "replaceState"
|
||||
String responseWithJavascript = responseWithJavascript(responseString, lastExecutionURL.toString());
|
||||
|
@ -124,6 +124,7 @@ public abstract class BrowserHistoryHelper {
|
|||
}
|
||||
|
||||
|
||||
// This impl is limited ATM. Saved request doesn't save response HTTP headers, so they may not be fully restored..
|
||||
private static class RedirectAfterPostHelper extends BrowserHistoryHelper {
|
||||
|
||||
private static final String CACHED_RESPONSE = "cached.response";
|
||||
|
@ -141,10 +142,11 @@ public abstract class BrowserHistoryHelper {
|
|||
String responseString = (String) entity;
|
||||
authSession.setAuthNote(CACHED_RESPONSE, responseString);
|
||||
|
||||
URI lastExecutionURL = new PageExpiredRedirect(session, session.getContext().getRealm(), session.getContext().getUri()).getLastExecutionUrl(authSession);
|
||||
URI lastExecutionURL = new AuthenticationFlowURLHelper(session, session.getContext().getRealm(), session.getContext().getUri()).getLastExecutionUrl(authSession);
|
||||
|
||||
// TODO:mposolda trace
|
||||
logger.infof("Saved response challenge and redirect to %s", lastExecutionURL);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracef("Saved response challenge and redirect to %s", lastExecutionURL);
|
||||
}
|
||||
|
||||
return Response.status(302).location(lastExecutionURL).build();
|
||||
}
|
||||
|
@ -160,11 +162,12 @@ public abstract class BrowserHistoryHelper {
|
|||
if (savedResponse != null) {
|
||||
authSession.removeAuthNote(CACHED_RESPONSE);
|
||||
|
||||
// TODO:mposolda trace
|
||||
logger.infof("Restored previously saved request");
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracef("Restored previously saved request");
|
||||
}
|
||||
|
||||
Response.ResponseBuilder builder = Response.status(200).type(MediaType.TEXT_HTML_UTF_8).entity(savedResponse);
|
||||
BrowserSecurityHeaderSetup.headers(builder, session.getContext().getRealm()); // TODO:mposolda cRather all the headers from the saved response should be added here.
|
||||
BrowserSecurityHeaderSetup.headers(builder, session.getContext().getRealm()); // TODO rather all the headers from the saved response should be added here.
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -119,6 +119,8 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
|||
return callback.cancelled(state);
|
||||
}
|
||||
|
||||
Response errorResponse = null;
|
||||
|
||||
try {
|
||||
Twitter twitter = new TwitterFactory().getInstance();
|
||||
|
||||
|
@ -155,17 +157,21 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
|||
|
||||
return callback.authenticated(identity);
|
||||
} catch (WebApplicationException e) {
|
||||
sendErrorEvent();
|
||||
return e.getResponse();
|
||||
} catch (Exception e) {
|
||||
logger.error("Could get user profile from twitter.", e);
|
||||
sendErrorEvent();
|
||||
return ErrorPage.error(session, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendErrorEvent() {
|
||||
EventBuilder event = new EventBuilder(realm, session, clientConnection);
|
||||
event.event(EventType.LOGIN);
|
||||
event.error("twitter_login_failed");
|
||||
return ErrorPage.error(session, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -163,7 +163,9 @@ public class OAuthClient {
|
|||
realm = "test";
|
||||
clientId = "test-app";
|
||||
redirectUri = APP_ROOT + "/auth";
|
||||
state = KeycloakModelUtils::generateId;
|
||||
state = () -> {
|
||||
return KeycloakModelUtils.generateId();
|
||||
};
|
||||
scope = null;
|
||||
uiLocales = null;
|
||||
clientSessionState = null;
|
||||
|
|
|
@ -122,9 +122,9 @@ public class AssertEvents implements TestRule {
|
|||
.session(isUUID());
|
||||
}
|
||||
|
||||
// TODO:mposolda codeId is not needed anymore
|
||||
public ExpectedEvent expectCodeToToken(String codeId, String sessionId) {
|
||||
return expect(EventType.CODE_TO_TOKEN)
|
||||
.detail(Details.CODE_ID, codeId)
|
||||
.detail(Details.TOKEN_ID, isUUID())
|
||||
.detail(Details.REFRESH_TOKEN_ID, isUUID())
|
||||
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
|
||||
|
|
|
@ -63,46 +63,50 @@ public class RequiredActionMultipleActionsTest extends AbstractTestRealmKeycloak
|
|||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
||||
String sessionId = null;
|
||||
String codeId = null;
|
||||
if (changePasswordPage.isCurrent()) {
|
||||
sessionId = updatePassword(sessionId);
|
||||
codeId = updatePassword(codeId);
|
||||
|
||||
updateProfilePage.assertCurrent();
|
||||
updateProfile(sessionId);
|
||||
updateProfile(codeId);
|
||||
} else if (updateProfilePage.isCurrent()) {
|
||||
sessionId = updateProfile(sessionId);
|
||||
codeId = updateProfile(codeId);
|
||||
|
||||
changePasswordPage.assertCurrent();
|
||||
updatePassword(sessionId);
|
||||
updatePassword(codeId);
|
||||
} else {
|
||||
Assert.fail("Expected to update password and profile before login");
|
||||
}
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().session(sessionId).assertEvent();
|
||||
events.expectLogin().session(codeId).assertEvent();
|
||||
}
|
||||
|
||||
public String updatePassword(String sessionId) {
|
||||
public String updatePassword(String codeId) {
|
||||
changePasswordPage.changePassword("new-password", "new-password");
|
||||
|
||||
AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction(EventType.UPDATE_PASSWORD);
|
||||
if (sessionId != null) {
|
||||
expectedEvent.session(sessionId);
|
||||
if (codeId != null) {
|
||||
expectedEvent.detail(Details.CODE_ID, codeId);
|
||||
}
|
||||
return expectedEvent.assertEvent().getSessionId();
|
||||
return expectedEvent.assertEvent().getDetails().get(Details.CODE_ID);
|
||||
}
|
||||
|
||||
public String updateProfile(String sessionId) {
|
||||
public String updateProfile(String codeId) {
|
||||
updateProfilePage.update("New first", "New last", "new@email.com", "test-user@localhost");
|
||||
|
||||
AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com");
|
||||
if (sessionId != null) {
|
||||
expectedEvent.session(sessionId);
|
||||
AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction(EventType.UPDATE_EMAIL)
|
||||
.detail(Details.PREVIOUS_EMAIL, "test-user@localhost")
|
||||
.detail(Details.UPDATED_EMAIL, "new@email.com");
|
||||
if (codeId != null) {
|
||||
expectedEvent.detail(Details.CODE_ID, codeId);
|
||||
}
|
||||
sessionId = expectedEvent.assertEvent().getSessionId();
|
||||
events.expectRequiredAction(EventType.UPDATE_PROFILE).session(sessionId).assertEvent();
|
||||
return sessionId;
|
||||
codeId = expectedEvent.assertEvent().getDetails().get(Details.CODE_ID);
|
||||
events.expectRequiredAction(EventType.UPDATE_PROFILE)
|
||||
.detail(Details.CODE_ID, codeId)
|
||||
.assertEvent();
|
||||
return codeId;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -127,11 +127,12 @@ public class RequiredActionTotpSetupTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
totpPage.configure(totp.generateTOTP(totpPage.getTotpSecret()));
|
||||
|
||||
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setuptotp").assertEvent().getSessionId();
|
||||
String authSessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setuptotp").assertEvent()
|
||||
.getDetails().get(Details.CODE_ID);
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().user(userId).session(sessionId).detail(Details.USERNAME, "setuptotp").assertEvent();
|
||||
events.expectLogin().user(userId).session(authSessionId).detail(Details.USERNAME, "setuptotp").assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -145,15 +146,16 @@ public class RequiredActionTotpSetupTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
totpPage.configure(totp.generateTOTP(totpSecret));
|
||||
|
||||
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).assertEvent().getSessionId();
|
||||
String authSessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).assertEvent()
|
||||
.getDetails().get(Details.CODE_ID);
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
EventRepresentation loginEvent = events.expectLogin().session(sessionId).assertEvent();
|
||||
EventRepresentation loginEvent = events.expectLogin().session(authSessionId).assertEvent();
|
||||
|
||||
oauth.openLogout();
|
||||
|
||||
events.expectLogout(loginEvent.getSessionId()).assertEvent();
|
||||
events.expectLogout(authSessionId).assertEvent();
|
||||
|
||||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
@ -229,7 +231,8 @@ public class RequiredActionTotpSetupTest extends AbstractTestRealmKeycloakTest {
|
|||
totpPage.assertCurrent();
|
||||
totpPage.configure(totp.generateTOTP(totpPage.getTotpSecret()));
|
||||
|
||||
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setupTotp2").assertEvent().getSessionId();
|
||||
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).user(userId).detail(Details.USERNAME, "setupTotp2").assertEvent()
|
||||
.getDetails().get(Details.CODE_ID);
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
|
@ -260,7 +263,8 @@ public class RequiredActionTotpSetupTest extends AbstractTestRealmKeycloakTest {
|
|||
TimeBasedOTP timeBased = new TimeBasedOTP(HmacOTP.HMAC_SHA1, 8, 30, 1);
|
||||
totpPage.configure(timeBased.generateTOTP(totpSecret));
|
||||
|
||||
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).assertEvent().getSessionId();
|
||||
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).assertEvent()
|
||||
.getDetails().get(Details.CODE_ID);
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
|
@ -311,7 +315,8 @@ public class RequiredActionTotpSetupTest extends AbstractTestRealmKeycloakTest {
|
|||
HmacOTP otpgen = new HmacOTP(6, HmacOTP.HMAC_SHA1, 1);
|
||||
totpPage.configure(otpgen.generateHOTP(totpSecret, 0));
|
||||
String uri = driver.getCurrentUrl();
|
||||
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).assertEvent().getSessionId();
|
||||
String sessionId = events.expectRequiredAction(EventType.UPDATE_TOTP).assertEvent()
|
||||
.getDetails().get(Details.CODE_ID);
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.actions;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
@ -89,12 +90,12 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
|||
|
||||
updateProfilePage.update("New first", "New last", "new@email.com", "test-user@localhost");
|
||||
|
||||
String sessionId = events.expectRequiredAction(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent().getSessionId();
|
||||
events.expectRequiredAction(EventType.UPDATE_PROFILE).session(sessionId).assertEvent();
|
||||
events.expectRequiredAction(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
||||
events.expectRequiredAction(EventType.UPDATE_PROFILE).assertEvent();
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().session(sessionId).assertEvent();
|
||||
events.expectLogin().assertEvent();
|
||||
|
||||
// assert user is really updated in persistent store
|
||||
UserRepresentation user = ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost");
|
||||
|
@ -116,19 +117,17 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
|||
|
||||
updateProfilePage.update("New first", "New last", "john-doh@localhost", "new");
|
||||
|
||||
String sessionId = events
|
||||
.expectLogin()
|
||||
events.expectLogin()
|
||||
.event(EventType.UPDATE_PROFILE)
|
||||
.detail(Details.USERNAME, "john-doh@localhost")
|
||||
.user(userId)
|
||||
.session(AssertEvents.isUUID())
|
||||
.session(Matchers.nullValue(String.class))
|
||||
.removeDetail(Details.CONSENT)
|
||||
.assertEvent()
|
||||
.getSessionId();
|
||||
.assertEvent();
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().detail(Details.USERNAME, "john-doh@localhost").user(userId).session(sessionId).assertEvent();
|
||||
events.expectLogin().detail(Details.USERNAME, "john-doh@localhost").user(userId).assertEvent();
|
||||
|
||||
// assert user is really updated in persistent store
|
||||
UserRepresentation user = ActionUtil.findUserWithAdminClient(adminClient, "new");
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.actions;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
@ -86,11 +87,11 @@ public class TermsAndConditionsTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
termsPage.acceptTerms();
|
||||
|
||||
String sessionId = events.expectRequiredAction(EventType.CUSTOM_REQUIRED_ACTION).removeDetail(Details.REDIRECT_URI).detail(Details.CUSTOM_REQUIRED_ACTION, TermsAndConditions.PROVIDER_ID).assertEvent().getSessionId();
|
||||
events.expectRequiredAction(EventType.CUSTOM_REQUIRED_ACTION).removeDetail(Details.REDIRECT_URI).detail(Details.CUSTOM_REQUIRED_ACTION, TermsAndConditions.PROVIDER_ID).assertEvent();
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().session(sessionId).assertEvent();
|
||||
events.expectLogin().assertEvent();
|
||||
|
||||
// assert user attribute is properly set
|
||||
UserRepresentation user = ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost");
|
||||
|
@ -123,6 +124,7 @@ public class TermsAndConditionsTest extends AbstractTestRealmKeycloakTest {
|
|||
events.expectLogin().event(EventType.CUSTOM_REQUIRED_ACTION_ERROR).detail(Details.CUSTOM_REQUIRED_ACTION, TermsAndConditions.PROVIDER_ID)
|
||||
.error(Errors.REJECTED_BY_USER)
|
||||
.removeDetail(Details.CONSENT)
|
||||
.session(Matchers.nullValue(String.class))
|
||||
.assertEvent();
|
||||
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.apache.http.auth.AuthScope;
|
|||
import org.apache.http.auth.Credentials;
|
||||
import org.apache.http.client.params.AuthPolicy;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
import org.apache.http.impl.client.AbstractHttpClient;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.ietf.jgss.GSSCredential;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
|
@ -347,7 +348,10 @@ public abstract class AbstractKerberosTest extends AbstractAuthTest {
|
|||
cleanupApacheHttpClient();
|
||||
}
|
||||
|
||||
DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
|
||||
DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder()
|
||||
.disableCookieCache(false)
|
||||
.build();
|
||||
|
||||
httpClient.getAuthSchemes().register(AuthPolicy.SPNEGO, spnegoSchemeFactory);
|
||||
|
||||
if (useSpnego) {
|
||||
|
|
|
@ -17,18 +17,23 @@
|
|||
|
||||
package org.keycloak.testsuite.federation.kerberos;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.constants.KerberosConstants;
|
||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.federation.kerberos.CommonKerberosConfig;
|
||||
import org.keycloak.federation.kerberos.KerberosConfig;
|
||||
|
@ -148,15 +153,24 @@ public class KerberosStandaloneTest extends AbstractKerberosTest {
|
|||
Response spnegoResponse = spnegoLogin("hnelson", "secret");
|
||||
String context = spnegoResponse.readEntity(String.class);
|
||||
spnegoResponse.close();
|
||||
|
||||
Assert.assertTrue(context.contains("Log in to test"));
|
||||
|
||||
Pattern pattern = Pattern.compile("action=\"([^\"]+)\"");
|
||||
Matcher m = pattern.matcher(context);
|
||||
Assert.assertTrue(m.find());
|
||||
String url = m.group(1);
|
||||
driver.navigate().to(url);
|
||||
Assert.assertTrue(loginPage.isCurrent());
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
String pageSource = driver.getPageSource();
|
||||
assertAuthenticationSuccess(driver.getCurrentUrl());
|
||||
|
||||
|
||||
// Follow login with HttpClient. Improve if needed
|
||||
MultivaluedMap<String, String> params = new javax.ws.rs.core.MultivaluedHashMap<>();
|
||||
params.putSingle("username", "test-user@localhost");
|
||||
params.putSingle("password", "password");
|
||||
Response response = client.target(url).request()
|
||||
.post(Entity.form(params));
|
||||
|
||||
URI redirectUri = response.getLocation();
|
||||
assertAuthenticationSuccess(redirectUri.toString());
|
||||
|
||||
events.clear();
|
||||
testRealmResource().components().add(kerberosProvider);
|
||||
|
|
|
@ -254,7 +254,7 @@ public class BrowserButtonsTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
// KEYCLOAK-4670 - Flow 5
|
||||
@Test
|
||||
public void clickBackButtonAfterReturnFromRegister() {
|
||||
public void clickBackButtonAfterReturnFromRegister() throws Exception {
|
||||
loginPage.open();
|
||||
loginPage.clickRegister();
|
||||
registerPage.assertCurrent();
|
||||
|
|
|
@ -21,6 +21,8 @@ import org.jboss.arquillian.graphene.page.Page;
|
|||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
@ -28,6 +30,7 @@ import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
|||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.InfoPage;
|
||||
|
@ -43,7 +46,7 @@ import org.keycloak.testsuite.util.GreenMailRule;
|
|||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
|
||||
/**
|
||||
* Tries to test multiple browser tabs
|
||||
* Tries to simulate testing with multiple browser tabs
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
|
@ -245,4 +248,43 @@ public class MultipleTabsLoginTest extends AbstractTestRealmKeycloakTest {
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void loginActionWithoutExecution() throws Exception {
|
||||
oauth.openLoginForm();
|
||||
|
||||
// Manually remove execution from the URL and try to simulate the request just with "code" parameter
|
||||
String actionUrl = driver.getPageSource().split("action=\"")[1].split("\"")[0].replaceAll("&", "&");
|
||||
actionUrl = actionUrl.replaceFirst("&execution=.*", "");
|
||||
|
||||
driver.navigate().to(actionUrl);
|
||||
|
||||
loginExpiredPage.assertCurrent();
|
||||
}
|
||||
|
||||
|
||||
// Same like "loginActionWithoutExecution", but AuthenticationSession is in REQUIRED_ACTIONS action
|
||||
@Test
|
||||
public void loginActionWithoutExecutionInRequiredActions() throws Exception {
|
||||
oauth.openLoginForm();
|
||||
loginPage.assertCurrent();
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
updatePasswordPage.assertCurrent();
|
||||
|
||||
// Manually remove execution from the URL and try to simulate the request just with "code" parameter
|
||||
String actionUrl = driver.getPageSource().split("action=\"")[1].split("\"")[0].replaceAll("&", "&");
|
||||
actionUrl = actionUrl.replaceFirst("&execution=.*", "");
|
||||
|
||||
driver.navigate().to(actionUrl);
|
||||
|
||||
// Back on updatePasswordPage now
|
||||
updatePasswordPage.assertCurrent();
|
||||
|
||||
updatePasswordPage.changePassword("password", "password");
|
||||
updateProfilePage.update("John", "Doe3", "john@doe3.com");
|
||||
appPage.assertCurrent();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -210,12 +210,7 @@ public class AccessTokenTest extends AbstractKeycloakTest {
|
|||
oauth.redirectUri(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth/admin/test/console/nosuch.html");
|
||||
oauth.openLoginForm();
|
||||
|
||||
String actionUrl = driver.getPageSource().split("action=\"")[1].split("\"")[0].replaceAll("&", "&");
|
||||
actionUrl = actionUrl.replaceFirst("&execution=.*", "");
|
||||
|
||||
String loginPageCode = actionUrl.split("code=")[1].split("&")[0];
|
||||
|
||||
driver.navigate().to(actionUrl);
|
||||
String loginPageCode = driver.getPageSource().split("code=")[1].split("&")[0].split("\"")[0];
|
||||
|
||||
oauth.fillLoginForm("test-user@localhost", "password");
|
||||
|
||||
|
@ -452,7 +447,7 @@ public class AccessTokenTest extends AbstractKeycloakTest {
|
|||
Assert.assertEquals(400, response.getStatusCode());
|
||||
|
||||
EventRepresentation event = events.poll();
|
||||
assertNotNull(event.getDetails().get(Details.CODE_ID));
|
||||
assertNull(event.getDetails().get(Details.CODE_ID));
|
||||
|
||||
UserManager.realm(adminClient.realm("test")).user(user).removeRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE.toString());
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
|
|||
}
|
||||
|
||||
private void assertCode(String expectedCodeId, String actualCode) {
|
||||
assertEquals(expectedCodeId, actualCode.split("\\.")[1]);
|
||||
assertEquals(expectedCodeId, actualCode.split("\\.")[2]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.oauth;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
@ -155,6 +155,7 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
|
|||
.client(THIRD_PARTY_APP)
|
||||
.error("rejected_by_user")
|
||||
.removeDetail(Details.CONSENT)
|
||||
.session(Matchers.nullValue(String.class))
|
||||
.assertEvent();
|
||||
}
|
||||
|
||||
|
@ -309,6 +310,7 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
|
|||
.client(THIRD_PARTY_APP)
|
||||
.error("rejected_by_user")
|
||||
.removeDetail(Details.CONSENT)
|
||||
.session(Matchers.nullValue(String.class))
|
||||
.assertEvent();
|
||||
|
||||
oauth.scope("foo-role third-party/bar-role");
|
||||
|
|
|
@ -304,6 +304,31 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void promptLoginDifferentUser() throws Exception {
|
||||
String sss = oauth.getLoginFormUrl();
|
||||
System.out.println(sss);
|
||||
|
||||
// Login user
|
||||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
EventRepresentation loginEvent = events.expectLogin().detail(Details.USERNAME, "test-user@localhost").assertEvent();
|
||||
IDToken idToken = sendTokenRequestAndGetIDToken(loginEvent);
|
||||
|
||||
// Assert need to re-authenticate with prompt=login
|
||||
driver.navigate().to(oauth.getLoginFormUrl() + "&prompt=login");
|
||||
|
||||
// Authenticate as different user
|
||||
loginPage.assertCurrent();
|
||||
loginPage.login("john-doh@localhost", "password");
|
||||
|
||||
errorPage.assertCurrent();
|
||||
Assert.assertTrue(errorPage.getError().startsWith("You are already authenticated as different user"));
|
||||
}
|
||||
|
||||
// DISPLAY & OTHERS
|
||||
|
||||
@Test
|
||||
|
|
|
@ -12,7 +12,7 @@ function authenticate(context) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (clientSession.getAuthMethod() != "${authMethod}") {
|
||||
if (clientSession.getProtocol() != "${authMethod}") {
|
||||
context.failure(AuthenticationFlowError.INVALID_CLIENT_SESSION);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.keycloak.common.util.PemUtils;
|
|||
import org.keycloak.constants.AdapterConstants;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.RefreshToken;
|
||||
|
@ -69,7 +70,9 @@ public class OAuthClient {
|
|||
|
||||
private String redirectUri = "http://localhost:8081/app/auth";
|
||||
|
||||
private String state = "mystate";
|
||||
private StateParamProvider state = () -> {
|
||||
return KeycloakModelUtils.generateId();
|
||||
};
|
||||
|
||||
private String scope;
|
||||
|
||||
|
@ -438,7 +441,7 @@ public class OAuthClient {
|
|||
b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
|
||||
}
|
||||
if (state != null) {
|
||||
b.queryParam(OAuth2Constants.STATE, state);
|
||||
b.queryParam(OAuth2Constants.STATE, state.getState());
|
||||
}
|
||||
if(uiLocales != null){
|
||||
b.queryParam(OAuth2Constants.UI_LOCALES_PARAM, uiLocales);
|
||||
|
@ -509,8 +512,17 @@ public class OAuthClient {
|
|||
return this;
|
||||
}
|
||||
|
||||
public OAuthClient state(String state) {
|
||||
this.state = state;
|
||||
public OAuthClient stateParamHardcoded(String value) {
|
||||
this.state = () -> {
|
||||
return value;
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
public OAuthClient stateParamRandom() {
|
||||
this.state = () -> {
|
||||
return KeycloakModelUtils.generateId();
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -639,4 +651,10 @@ public class OAuthClient {
|
|||
}
|
||||
}
|
||||
|
||||
private interface StateParamProvider {
|
||||
|
||||
String getState();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,12 +33,16 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
|||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.pages.IdpConfirmLinkPage;
|
||||
import org.keycloak.testsuite.pages.IdpLinkEmailPage;
|
||||
import org.keycloak.testsuite.pages.InfoPage;
|
||||
import org.keycloak.testsuite.pages.LoginExpiredPage;
|
||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
||||
import org.keycloak.testsuite.pages.LoginUpdateProfileEditUsernameAllowedPage;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.NoSuchElementException;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
@ -71,6 +75,9 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractIdentityProvi
|
|||
@WebResource
|
||||
protected LoginPasswordUpdatePage passwordUpdatePage;
|
||||
|
||||
@WebResource
|
||||
protected LoginExpiredPage loginExpiredPage;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
@ -360,6 +367,101 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractIdentityProvi
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Variation of previous test, which uses browser buttons (back, refresh etc)
|
||||
*/
|
||||
@Test
|
||||
public void testLinkAccountByReauthenticationWithPassword_browserButtons() throws Exception {
|
||||
// Remove smtp config. The reauthentication by username+password screen will be automatically used then
|
||||
final Map<String, String> smtpConfig = new HashMap<>();
|
||||
brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
|
||||
setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_OFF);
|
||||
smtpConfig.putAll(realmWithBroker.getSmtpConfig());
|
||||
realmWithBroker.setSmtpConfig(Collections.<String, String>emptyMap());
|
||||
}
|
||||
|
||||
}, APP_REALM_ID);
|
||||
|
||||
|
||||
// Use invalid username for the first time
|
||||
loginIDP("foo");
|
||||
assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
|
||||
this.loginPage.login("pedroigor", "password");
|
||||
|
||||
|
||||
this.idpConfirmLinkPage.assertCurrent();
|
||||
Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
|
||||
|
||||
// Click browser 'back' and then 'forward' and then continue
|
||||
driver.navigate().back();
|
||||
Assert.assertTrue(driver.getPageSource().contains("You are already logged in."));
|
||||
driver.navigate().forward();
|
||||
this.loginExpiredPage.assertCurrent();
|
||||
this.loginExpiredPage.clickLoginContinueLink();
|
||||
this.idpConfirmLinkPage.assertCurrent();
|
||||
|
||||
// Click browser 'back' on review profile page
|
||||
this.idpConfirmLinkPage.clickReviewProfile();
|
||||
this.updateProfilePage.assertCurrent();
|
||||
driver.navigate().back();
|
||||
this.loginExpiredPage.assertCurrent();
|
||||
this.loginExpiredPage.clickLoginContinueLink();
|
||||
this.updateProfilePage.assertCurrent();
|
||||
this.updateProfilePage.update("Pedro", "Igor", "psilva@redhat.com");
|
||||
|
||||
this.idpConfirmLinkPage.assertCurrent();
|
||||
this.idpConfirmLinkPage.clickLinkAccount();
|
||||
|
||||
// Login screen shown. Username is prefilled and disabled. Registration link and social buttons are not shown
|
||||
Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
|
||||
Assert.assertEquals("pedroigor", this.loginPage.getUsername());
|
||||
Assert.assertFalse(this.loginPage.isUsernameInputEnabled());
|
||||
|
||||
Assert.assertEquals("Authenticate as pedroigor to link your account with " + getProviderId(), this.loginPage.getInfoMessage());
|
||||
|
||||
try {
|
||||
this.loginPage.findSocialButton(getProviderId());
|
||||
Assert.fail("Not expected to see social button with " + getProviderId());
|
||||
} catch (NoSuchElementException expected) {
|
||||
}
|
||||
|
||||
try {
|
||||
this.loginPage.clickRegister();
|
||||
Assert.fail("Not expected to see register link");
|
||||
} catch (NoSuchElementException expected) {
|
||||
}
|
||||
|
||||
// Use bad password first
|
||||
this.loginPage.login("password1");
|
||||
Assert.assertEquals("Invalid username or password.", this.loginPage.getError());
|
||||
|
||||
// Click browser 'back' and then continue
|
||||
this.driver.navigate().back();
|
||||
this.loginExpiredPage.assertCurrent();
|
||||
this.loginExpiredPage.clickLoginContinueLink();
|
||||
|
||||
// Use correct password now
|
||||
this.loginPage.login("password");
|
||||
|
||||
// authenticated and redirected to app. User is linked with identity provider
|
||||
assertFederatedUser("pedroigor", "psilva@redhat.com", "pedroigor");
|
||||
|
||||
|
||||
// Restore smtp config
|
||||
brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
|
||||
realmWithBroker.setSmtpConfig(smtpConfig);
|
||||
}
|
||||
|
||||
}, APP_REALM_ID);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests that duplication is detected and user wants to link federatedIdentity with existing account. He will confirm link by reauthentication (confirm password on login screen)
|
||||
* and additionally he goes through "forget password"
|
||||
|
@ -418,6 +520,93 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractIdentityProvi
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Same like above, but "forget password" link is opened in different browser
|
||||
*/
|
||||
@Test
|
||||
public void testLinkAccountByReauthentication_forgetPassword_differentBrowser() throws Throwable {
|
||||
brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
|
||||
setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW,
|
||||
IdpEmailVerificationAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.DISABLED);
|
||||
|
||||
setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_OFF);
|
||||
}
|
||||
|
||||
}, APP_REALM_ID);
|
||||
|
||||
loginIDP("pedroigor");
|
||||
|
||||
this.idpConfirmLinkPage.assertCurrent();
|
||||
Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
|
||||
this.idpConfirmLinkPage.clickLinkAccount();
|
||||
|
||||
// Click "Forget password" on login page. Email sent directly because username is known
|
||||
Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
|
||||
this.loginPage.resetPassword();
|
||||
|
||||
Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
|
||||
Assert.assertEquals("You should receive an email shortly with further instructions.", this.loginPage.getSuccessMessage());
|
||||
|
||||
// Click on link from email
|
||||
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
|
||||
MimeMessage message = greenMail.getReceivedMessages()[0];
|
||||
String linkFromMail = getVerificationEmailLink(message);
|
||||
|
||||
// Simulate 2nd browser
|
||||
WebRule webRule2 = new WebRule(this);
|
||||
webRule2.before();
|
||||
|
||||
WebDriver driver2 = webRule2.getDriver();
|
||||
LoginPasswordUpdatePage passwordUpdatePage2 = webRule2.getPage(LoginPasswordUpdatePage.class);
|
||||
InfoPage infoPage2 = webRule2.getPage(InfoPage.class);
|
||||
|
||||
driver2.navigate().to(linkFromMail.trim());
|
||||
|
||||
// Need to update password now
|
||||
passwordUpdatePage2.assertCurrent();
|
||||
passwordUpdatePage2.changePassword("password", "password");
|
||||
|
||||
// authenticated, but not redirected to app. Just seeing info page.
|
||||
infoPage2.assertCurrent();
|
||||
Assert.assertEquals("Your account has been updated.", infoPage2.getInfo());
|
||||
|
||||
// User is not yet linked with identity provider. He needs to authenticate again in 1st browser
|
||||
RealmModel realmWithBroker = getRealm();
|
||||
Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(this.session.users().getUserByUsername("pedroigor", realmWithBroker), realmWithBroker);
|
||||
assertEquals(0, federatedIdentities.size());
|
||||
|
||||
// Continue with 1st browser
|
||||
loginIDP("pedroigor");
|
||||
|
||||
this.idpConfirmLinkPage.assertCurrent();
|
||||
Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
|
||||
this.idpConfirmLinkPage.clickLinkAccount();
|
||||
|
||||
Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
|
||||
this.loginPage.login("password");
|
||||
|
||||
// authenticated and redirected to app. User is linked with identity provider
|
||||
assertFederatedUser("pedroigor", "psilva@redhat.com", "pedroigor");
|
||||
|
||||
// Revert everything
|
||||
webRule2.after();
|
||||
|
||||
brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
|
||||
setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW,
|
||||
IdpEmailVerificationAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.ALTERNATIVE);
|
||||
|
||||
}
|
||||
|
||||
}, APP_REALM_ID);
|
||||
}
|
||||
|
||||
|
||||
protected void assertFederatedUser(String expectedUsername, String expectedEmail, String expectedFederatedUsername) {
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
|
||||
UserModel federatedUser = getFederatedUser();
|
||||
|
|
|
@ -74,8 +74,6 @@ public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdent
|
|||
setUpdateProfileFirstLogin(session.realms().getRealmByName("realm-with-broker"), IdentityProviderRepresentation.UPFLM_OFF);
|
||||
brokerServerRule.stopSession(session, true);
|
||||
|
||||
Thread.sleep(10000000);
|
||||
|
||||
driver.navigate().to("http://localhost:8081/test-app");
|
||||
loginPage.clickSocial(getProviderId());
|
||||
loginPage.login("test-user", "password");
|
||||
|
|
|
@ -117,11 +117,9 @@ public class OIDCKeycloakServerBrokerWithConsentTest extends AbstractIdentityPro
|
|||
grantPage.assertCurrent();
|
||||
grantPage.cancel();
|
||||
|
||||
// Assert error page with backToApplication link displayed
|
||||
errorPage.assertCurrent();
|
||||
errorPage.clickBackToApplication();
|
||||
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
|
||||
// Assert login page with "You took too long to login..." message
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/login-actions/authenticate"));
|
||||
Assert.assertEquals("You took too long to login. Login process starting from beginning.", loginPage.getError());
|
||||
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
|
@ -152,14 +150,9 @@ public class OIDCKeycloakServerBrokerWithConsentTest extends AbstractIdentityPro
|
|||
grantPage.assertCurrent();
|
||||
grantPage.cancel();
|
||||
|
||||
// Assert error page without backToApplication link (clientSession expired and was removed on the server)
|
||||
errorPage.assertCurrent();
|
||||
try {
|
||||
errorPage.clickBackToApplication();
|
||||
fail("Not expected to have link backToApplication available");
|
||||
} catch (NoSuchElementException nsee) {
|
||||
// Expected;
|
||||
}
|
||||
// Assert login page with "You took too long to login..." message
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/login-actions/authenticate"));
|
||||
Assert.assertEquals("You took too long to login. Login process starting from beginning.", loginPage.getError());
|
||||
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
|
|
|
@ -22,15 +22,21 @@ import org.junit.Assert;
|
|||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserManager;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.managers.ClientManager;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.CommonClientSessionModel;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
|
@ -53,7 +59,6 @@ public class AuthenticationSessionProviderTest {
|
|||
@After
|
||||
public void after() {
|
||||
resetSession();
|
||||
session.sessions().removeUserSessions(realm);
|
||||
UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
UserModel user2 = session.users().getUserByUsername("user2", realm);
|
||||
|
||||
|
@ -87,7 +92,7 @@ public class AuthenticationSessionProviderTest {
|
|||
|
||||
// Ensure session is here
|
||||
authSession = session.authenticationSessions().getAuthenticationSession(realm, authSession.getId());
|
||||
testLoginSession(authSession, client1.getId(), null, "foo");
|
||||
testAuthenticationSession(authSession, client1.getId(), null, "foo");
|
||||
Assert.assertEquals(100, authSession.getTimestamp());
|
||||
|
||||
// Update and commit
|
||||
|
@ -99,7 +104,7 @@ public class AuthenticationSessionProviderTest {
|
|||
|
||||
// Ensure session was updated
|
||||
authSession = session.authenticationSessions().getAuthenticationSession(realm, authSession.getId());
|
||||
testLoginSession(authSession, client1.getId(), user1.getId(), "foo-updated");
|
||||
testAuthenticationSession(authSession, client1.getId(), user1.getId(), "foo-updated");
|
||||
Assert.assertEquals(200, authSession.getTimestamp());
|
||||
|
||||
// Remove and commit
|
||||
|
@ -113,7 +118,7 @@ public class AuthenticationSessionProviderTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testLoginSessionRestart() {
|
||||
public void testAuthenticationSessionRestart() {
|
||||
ClientModel client1 = realm.getClientByClientId("test-app");
|
||||
UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
|
||||
|
@ -136,7 +141,7 @@ public class AuthenticationSessionProviderTest {
|
|||
resetSession();
|
||||
|
||||
authSession = session.authenticationSessions().getAuthenticationSession(realm, authSession.getId());
|
||||
testLoginSession(authSession, client1.getId(), null, null);
|
||||
testAuthenticationSession(authSession, client1.getId(), null, null);
|
||||
Assert.assertTrue(authSession.getTimestamp() > 0);
|
||||
|
||||
Assert.assertTrue(authSession.getClientNotes().isEmpty());
|
||||
|
@ -145,7 +150,120 @@ public class AuthenticationSessionProviderTest {
|
|||
|
||||
}
|
||||
|
||||
private void testLoginSession(AuthenticationSessionModel authSession, String expectedClientId, String expectedUserId, String expectedAction) {
|
||||
|
||||
@Test
|
||||
public void testExpiredAuthSessions() {
|
||||
try {
|
||||
realm.setAccessCodeLifespan(10);
|
||||
realm.setAccessCodeLifespanUserAction(10);
|
||||
realm.setAccessCodeLifespanLogin(30);
|
||||
|
||||
// Login lifespan is largest
|
||||
String authSessionId = session.authenticationSessions().createAuthenticationSession(realm, realm.getClientByClientId("test-app")).getId();
|
||||
resetSession();
|
||||
|
||||
Time.setOffset(25);
|
||||
session.authenticationSessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNotNull(session.authenticationSessions().getAuthenticationSession(realm, authSessionId));
|
||||
|
||||
Time.setOffset(35);
|
||||
session.authenticationSessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNull(session.authenticationSessions().getAuthenticationSession(realm, authSessionId));
|
||||
|
||||
// User action is largest
|
||||
realm.setAccessCodeLifespanUserAction(40);
|
||||
|
||||
Time.setOffset(0);
|
||||
authSessionId = session.authenticationSessions().createAuthenticationSession(realm, realm.getClientByClientId("test-app")).getId();
|
||||
resetSession();
|
||||
|
||||
Time.setOffset(35);
|
||||
session.authenticationSessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNotNull(session.authenticationSessions().getAuthenticationSession(realm, authSessionId));
|
||||
|
||||
Time.setOffset(45);
|
||||
session.authenticationSessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNull(session.authenticationSessions().getAuthenticationSession(realm, authSessionId));
|
||||
|
||||
// Access code is largest
|
||||
realm.setAccessCodeLifespan(50);
|
||||
|
||||
Time.setOffset(0);
|
||||
authSessionId = session.authenticationSessions().createAuthenticationSession(realm, realm.getClientByClientId("test-app")).getId();
|
||||
resetSession();
|
||||
|
||||
Time.setOffset(45);
|
||||
session.authenticationSessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNotNull(session.authenticationSessions().getAuthenticationSession(realm, authSessionId));
|
||||
|
||||
Time.setOffset(55);
|
||||
session.authenticationSessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNull(session.authenticationSessions().getAuthenticationSession(realm, authSessionId));
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
|
||||
realm.setAccessCodeLifespan(60);
|
||||
realm.setAccessCodeLifespanUserAction(300);
|
||||
realm.setAccessCodeLifespanLogin(1800);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testOnRealmRemoved() {
|
||||
RealmModel fooRealm = session.realms().createRealm("foo-realm");
|
||||
ClientModel fooClient = fooRealm.addClient("foo-client");
|
||||
|
||||
String authSessionId = session.authenticationSessions().createAuthenticationSession(realm, realm.getClientByClientId("test-app")).getId();
|
||||
String authSessionId2 = session.authenticationSessions().createAuthenticationSession(fooRealm, fooClient).getId();
|
||||
|
||||
resetSession();
|
||||
|
||||
new RealmManager(session).removeRealm(session.realms().getRealmByName("foo-realm"));
|
||||
|
||||
resetSession();
|
||||
|
||||
AuthenticationSessionModel authSession = session.authenticationSessions().getAuthenticationSession(realm, authSessionId);
|
||||
testAuthenticationSession(authSession, realm.getClientByClientId("test-app").getId(), null, null);
|
||||
Assert.assertNull(session.authenticationSessions().getAuthenticationSession(realm, authSessionId2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClientRemoved() {
|
||||
String authSessionId = session.authenticationSessions().createAuthenticationSession(realm, realm.getClientByClientId("test-app")).getId();
|
||||
String authSessionId2 = session.authenticationSessions().createAuthenticationSession(realm, realm.getClientByClientId("third-party")).getId();
|
||||
|
||||
String testAppClientUUID = realm.getClientByClientId("test-app").getId();
|
||||
|
||||
resetSession();
|
||||
|
||||
new ClientManager(new RealmManager(session)).removeClient(realm, realm.getClientByClientId("third-party"));
|
||||
|
||||
resetSession();
|
||||
|
||||
AuthenticationSessionModel authSession = session.authenticationSessions().getAuthenticationSession(realm, authSessionId);
|
||||
testAuthenticationSession(authSession, testAppClientUUID, null, null);
|
||||
Assert.assertNull(session.authenticationSessions().getAuthenticationSession(realm, authSessionId2));
|
||||
|
||||
// Revert client
|
||||
realm.addClient("third-party");
|
||||
}
|
||||
|
||||
|
||||
private void testAuthenticationSession(AuthenticationSessionModel authSession, String expectedClientId, String expectedUserId, String expectedAction) {
|
||||
Assert.assertEquals(expectedClientId, authSession.getClient().getId());
|
||||
|
||||
if (expectedUserId == null) {
|
||||
|
|
|
@ -25,14 +25,15 @@ import org.junit.Test;
|
|||
import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.UserSessionProvider;
|
||||
import org.keycloak.models.UserSessionProviderFactory;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.models.UserManager;
|
||||
import org.keycloak.services.managers.UserSessionManager;
|
||||
|
@ -47,129 +48,129 @@ import java.util.Set;
|
|||
*/
|
||||
public class UserSessionInitializerTest {
|
||||
|
||||
// TODO:mposolda
|
||||
// @ClassRule
|
||||
// public static KeycloakRule kc = new KeycloakRule();
|
||||
//
|
||||
// private KeycloakSession session;
|
||||
// private RealmModel realm;
|
||||
// private UserSessionManager sessionManager;
|
||||
//
|
||||
// @Before
|
||||
// public void before() {
|
||||
// session = kc.startSession();
|
||||
// realm = session.realms().getRealm("test");
|
||||
// session.users().addUser(realm, "user1").setEmail("user1@localhost");
|
||||
// session.users().addUser(realm, "user2").setEmail("user2@localhost");
|
||||
// sessionManager = new UserSessionManager(session);
|
||||
// }
|
||||
//
|
||||
// @After
|
||||
// public void after() {
|
||||
// resetSession();
|
||||
// session.sessions().removeUserSessions(realm);
|
||||
// UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
// UserModel user2 = session.users().getUserByUsername("user2", realm);
|
||||
//
|
||||
// UserManager um = new UserManager(session);
|
||||
// um.removeUser(realm, user1);
|
||||
// um.removeUser(realm, user2);
|
||||
// kc.stopSession(session, true);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testUserSessionInitializer() {
|
||||
// UserSessionModel[] origSessions = createSessions();
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Create and persist offline sessions
|
||||
// int started = Time.currentTime();
|
||||
// int serverStartTime = session.getProvider(ClusterProvider.class).getClusterStartupTime();
|
||||
//
|
||||
// for (UserSessionModel origSession : origSessions) {
|
||||
// UserSessionModel userSession = session.sessions().getUserSession(realm, origSession.getId());
|
||||
// for (ClientSessionModel clientSession : userSession.getClientSessions()) {
|
||||
// sessionManager.createOrUpdateOfflineSession(clientSession, userSession);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Delete cache (persisted sessions are still kept)
|
||||
// session.sessions().onRealmRemoved(realm);
|
||||
//
|
||||
// // Clear ispn cache to ensure initializerState is removed as well
|
||||
// InfinispanConnectionProvider infinispan = session.getProvider(InfinispanConnectionProvider.class);
|
||||
// infinispan.getCache(InfinispanConnectionProvider.WORK_CACHE_NAME).clear();
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// ClientModel testApp = realm.getClientByClientId("test-app");
|
||||
// ClientModel thirdparty = realm.getClientByClientId("third-party");
|
||||
// Assert.assertEquals(0, session.sessions().getOfflineSessionsCount(realm, testApp));
|
||||
// Assert.assertEquals(0, session.sessions().getOfflineSessionsCount(realm, thirdparty));
|
||||
//
|
||||
// // Load sessions from persister into infinispan/memory
|
||||
// UserSessionProviderFactory userSessionFactory = (UserSessionProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserSessionProvider.class);
|
||||
// userSessionFactory.loadPersistentSessions(session.getKeycloakSessionFactory(), 1, 2);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert sessions are in
|
||||
// testApp = realm.getClientByClientId("test-app");
|
||||
// Assert.assertEquals(3, session.sessions().getOfflineSessionsCount(realm, testApp));
|
||||
// Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, thirdparty));
|
||||
//
|
||||
// List<UserSessionModel> loadedSessions = session.sessions().getOfflineUserSessions(realm, testApp, 0, 10);
|
||||
// UserSessionProviderTest.assertSessions(loadedSessions, origSessions);
|
||||
//
|
||||
// UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[0].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.1", started, serverStartTime, "test-app", "third-party");
|
||||
// UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[1].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.2", started, serverStartTime, "test-app");
|
||||
// UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[2].getId(), session.users().getUserByUsername("user2", realm), "127.0.0.3", started, serverStartTime, "test-app");
|
||||
// }
|
||||
//
|
||||
// private ClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
|
||||
// ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
|
||||
// if (userSession != null) clientSession.setUserSession(userSession);
|
||||
// clientSession.setRedirectUri(redirect);
|
||||
// if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
|
||||
// if (roles != null) clientSession.setRoles(roles);
|
||||
// if (protocolMappers != null) clientSession.setProtocolMappers(protocolMappers);
|
||||
// return clientSession;
|
||||
// }
|
||||
//
|
||||
// private UserSessionModel[] createSessions() {
|
||||
// UserSessionModel[] sessions = new UserSessionModel[3];
|
||||
// sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
|
||||
//
|
||||
// Set<String> roles = new HashSet<String>();
|
||||
// roles.add("one");
|
||||
// roles.add("two");
|
||||
//
|
||||
// Set<String> protocolMappers = new HashSet<String>();
|
||||
// protocolMappers.add("mapper-one");
|
||||
// protocolMappers.add("mapper-two");
|
||||
//
|
||||
// createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
|
||||
// createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
|
||||
// createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
|
||||
// createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// return sessions;
|
||||
// }
|
||||
//
|
||||
// private void resetSession() {
|
||||
// kc.stopSession(session, true);
|
||||
// session = kc.startSession();
|
||||
// realm = session.realms().getRealm("test");
|
||||
// sessionManager = new UserSessionManager(session);
|
||||
// }
|
||||
|
||||
@ClassRule
|
||||
public static KeycloakRule kc = new KeycloakRule();
|
||||
|
||||
private KeycloakSession session;
|
||||
private RealmModel realm;
|
||||
private UserSessionManager sessionManager;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
session = kc.startSession();
|
||||
realm = session.realms().getRealm("test");
|
||||
session.users().addUser(realm, "user1").setEmail("user1@localhost");
|
||||
session.users().addUser(realm, "user2").setEmail("user2@localhost");
|
||||
sessionManager = new UserSessionManager(session);
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
resetSession();
|
||||
session.sessions().removeUserSessions(realm);
|
||||
UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
UserModel user2 = session.users().getUserByUsername("user2", realm);
|
||||
|
||||
UserManager um = new UserManager(session);
|
||||
um.removeUser(realm, user1);
|
||||
um.removeUser(realm, user2);
|
||||
kc.stopSession(session, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserSessionInitializer() {
|
||||
UserSessionModel[] origSessions = createSessions();
|
||||
|
||||
resetSession();
|
||||
|
||||
// Create and persist offline sessions
|
||||
int started = Time.currentTime();
|
||||
int serverStartTime = session.getProvider(ClusterProvider.class).getClusterStartupTime();
|
||||
|
||||
for (UserSessionModel origSession : origSessions) {
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, origSession.getId());
|
||||
for (AuthenticatedClientSessionModel clientSession : userSession.getAuthenticatedClientSessions().values()) {
|
||||
sessionManager.createOrUpdateOfflineSession(clientSession, userSession);
|
||||
}
|
||||
}
|
||||
|
||||
resetSession();
|
||||
|
||||
// Delete cache (persisted sessions are still kept)
|
||||
session.sessions().onRealmRemoved(realm);
|
||||
|
||||
// Clear ispn cache to ensure initializerState is removed as well
|
||||
InfinispanConnectionProvider infinispan = session.getProvider(InfinispanConnectionProvider.class);
|
||||
infinispan.getCache(InfinispanConnectionProvider.WORK_CACHE_NAME).clear();
|
||||
|
||||
resetSession();
|
||||
|
||||
ClientModel testApp = realm.getClientByClientId("test-app");
|
||||
ClientModel thirdparty = realm.getClientByClientId("third-party");
|
||||
Assert.assertEquals(0, session.sessions().getOfflineSessionsCount(realm, testApp));
|
||||
Assert.assertEquals(0, session.sessions().getOfflineSessionsCount(realm, thirdparty));
|
||||
|
||||
// Load sessions from persister into infinispan/memory
|
||||
UserSessionProviderFactory userSessionFactory = (UserSessionProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserSessionProvider.class);
|
||||
userSessionFactory.loadPersistentSessions(session.getKeycloakSessionFactory(), 1, 2);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert sessions are in
|
||||
testApp = realm.getClientByClientId("test-app");
|
||||
Assert.assertEquals(3, session.sessions().getOfflineSessionsCount(realm, testApp));
|
||||
Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, thirdparty));
|
||||
|
||||
List<UserSessionModel> loadedSessions = session.sessions().getOfflineUserSessions(realm, testApp, 0, 10);
|
||||
UserSessionProviderTest.assertSessions(loadedSessions, origSessions);
|
||||
|
||||
UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[0].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.1", started, serverStartTime, "test-app", "third-party");
|
||||
UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[1].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.2", started, serverStartTime, "test-app");
|
||||
UserSessionPersisterProviderTest.assertSessionLoaded(loadedSessions, origSessions[2].getId(), session.users().getUserByUsername("user2", realm), "127.0.0.3", started, serverStartTime, "test-app");
|
||||
}
|
||||
|
||||
private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
|
||||
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
|
||||
if (userSession != null) clientSession.setUserSession(userSession);
|
||||
clientSession.setRedirectUri(redirect);
|
||||
if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
|
||||
if (roles != null) clientSession.setRoles(roles);
|
||||
if (protocolMappers != null) clientSession.setProtocolMappers(protocolMappers);
|
||||
return clientSession;
|
||||
}
|
||||
|
||||
private UserSessionModel[] createSessions() {
|
||||
UserSessionModel[] sessions = new UserSessionModel[3];
|
||||
sessions[0] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
|
||||
|
||||
Set<String> roles = new HashSet<String>();
|
||||
roles.add("one");
|
||||
roles.add("two");
|
||||
|
||||
Set<String> protocolMappers = new HashSet<String>();
|
||||
protocolMappers.add("mapper-one");
|
||||
protocolMappers.add("mapper-two");
|
||||
|
||||
createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
|
||||
createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
sessions[1] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
|
||||
createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
sessions[2] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
|
||||
createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
resetSession();
|
||||
|
||||
return sessions;
|
||||
}
|
||||
|
||||
private void resetSession() {
|
||||
kc.stopSession(session, true);
|
||||
session = kc.startSession();
|
||||
realm = session.realms().getRealm("test");
|
||||
sessionManager = new UserSessionManager(session);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,13 +23,14 @@ import org.junit.Before;
|
|||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.session.UserSessionPersisterProvider;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.services.managers.ClientManager;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
|
@ -45,398 +46,398 @@ import java.util.Set;
|
|||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserSessionPersisterProviderTest {
|
||||
// TODO:mposolda
|
||||
|
||||
// @ClassRule
|
||||
// public static KeycloakRule kc = new KeycloakRule();
|
||||
//
|
||||
// private KeycloakSession session;
|
||||
// private RealmModel realm;
|
||||
// private UserSessionPersisterProvider persister;
|
||||
//
|
||||
// @Before
|
||||
// public void before() {
|
||||
// session = kc.startSession();
|
||||
// realm = session.realms().getRealm("test");
|
||||
// session.users().addUser(realm, "user1").setEmail("user1@localhost");
|
||||
// session.users().addUser(realm, "user2").setEmail("user2@localhost");
|
||||
// persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
// }
|
||||
//
|
||||
// @After
|
||||
// public void after() {
|
||||
// resetSession();
|
||||
// session.sessions().removeUserSessions(realm);
|
||||
// UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
// UserModel user2 = session.users().getUserByUsername("user2", realm);
|
||||
//
|
||||
// UserManager um = new UserManager(session);
|
||||
// if (user1 != null) {
|
||||
// um.removeUser(realm, user1);
|
||||
// }
|
||||
// if (user2 != null) {
|
||||
// um.removeUser(realm, user2);
|
||||
// }
|
||||
// kc.stopSession(session, true);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testPersistenceWithLoad() {
|
||||
// // Create some sessions in infinispan
|
||||
// int started = Time.currentTime();
|
||||
// UserSessionModel[] origSessions = createSessions();
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Persist 3 created userSessions and clientSessions as offline
|
||||
// ClientModel testApp = realm.getClientByClientId("test-app");
|
||||
// List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, testApp);
|
||||
// for (UserSessionModel userSession : userSessions) {
|
||||
// persistUserSession(userSession, true);
|
||||
// }
|
||||
//
|
||||
// // Persist 1 online session
|
||||
// UserSessionModel userSession = session.sessions().getUserSession(realm, origSessions[0].getId());
|
||||
// persistUserSession(userSession, false);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert online session
|
||||
// List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(false, 1, 1, 1);
|
||||
// UserSessionProviderTest.assertSession(loadedSessions.get(0), session.users().getUserByUsername("user1", realm), "127.0.0.1", started, started, "test-app", "third-party");
|
||||
//
|
||||
// // Assert offline sessions
|
||||
// loadedSessions = loadPersistedSessionsPaginated(true, 2, 2, 3);
|
||||
// UserSessionProviderTest.assertSessions(loadedSessions, origSessions);
|
||||
//
|
||||
// assertSessionLoaded(loadedSessions, origSessions[0].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.1", started, started, "test-app", "third-party");
|
||||
// assertSessionLoaded(loadedSessions, origSessions[1].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.2", started, started, "test-app");
|
||||
// assertSessionLoaded(loadedSessions, origSessions[2].getId(), session.users().getUserByUsername("user2", realm), "127.0.0.3", started, started, "test-app");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testUpdateTimestamps() {
|
||||
// // Create some sessions in infinispan
|
||||
// int started = Time.currentTime();
|
||||
// UserSessionModel[] origSessions = createSessions();
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Persist 3 created userSessions and clientSessions as offline
|
||||
// ClientModel testApp = realm.getClientByClientId("test-app");
|
||||
// List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, testApp);
|
||||
// for (UserSessionModel userSession : userSessions) {
|
||||
// persistUserSession(userSession, true);
|
||||
// }
|
||||
//
|
||||
// // Persist 1 online session
|
||||
// UserSessionModel userSession = session.sessions().getUserSession(realm, origSessions[0].getId());
|
||||
// persistUserSession(userSession, false);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // update timestamps
|
||||
// int newTime = started + 50;
|
||||
// persister.updateAllTimestamps(newTime);
|
||||
//
|
||||
// // Assert online session
|
||||
// List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(false, 1, 1, 1);
|
||||
// Assert.assertEquals(2, assertTimestampsUpdated(loadedSessions, newTime));
|
||||
//
|
||||
// // Assert offline sessions
|
||||
// loadedSessions = loadPersistedSessionsPaginated(true, 2, 2, 3);
|
||||
// Assert.assertEquals(4, assertTimestampsUpdated(loadedSessions, newTime));
|
||||
// }
|
||||
//
|
||||
// private int assertTimestampsUpdated(List<UserSessionModel> loadedSessions, int expectedTime) {
|
||||
// int clientSessionsCount = 0;
|
||||
// for (UserSessionModel loadedSession : loadedSessions) {
|
||||
// Assert.assertEquals(expectedTime, loadedSession.getLastSessionRefresh());
|
||||
// for (ClientSessionModel clientSession : loadedSession.getClientSessions()) {
|
||||
// Assert.assertEquals(expectedTime, clientSession.getTimestamp());
|
||||
// clientSessionsCount++;
|
||||
// }
|
||||
// }
|
||||
// return clientSessionsCount;
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testUpdateAndRemove() {
|
||||
// // Create some sessions in infinispan
|
||||
// int started = Time.currentTime();
|
||||
// UserSessionModel[] origSessions = createSessions();
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Persist 1 offline session
|
||||
// UserSessionModel userSession = session.sessions().getUserSession(realm, origSessions[1].getId());
|
||||
// persistUserSession(userSession, true);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Load offline session
|
||||
// List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(true, 10, 1, 1);
|
||||
// UserSessionModel persistedSession = loadedSessions.get(0);
|
||||
// UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user1", realm), "127.0.0.2", started, started, "test-app");
|
||||
//
|
||||
// // Update userSession
|
||||
// Time.setOffset(10);
|
||||
// try {
|
||||
// persistedSession.setLastSessionRefresh(Time.currentTime());
|
||||
// persistedSession.setNote("foo", "bar");
|
||||
// persistedSession.setState(UserSessionModel.State.LOGGING_IN);
|
||||
// persister.updateUserSession(persistedSession, true);
|
||||
//
|
||||
// // create new clientSession
|
||||
// ClientSessionModel clientSession = createClientSession(realm.getClientByClientId("third-party"), session.sessions().getUserSession(realm, persistedSession.getId()),
|
||||
// "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
// persister.createClientSession(clientSession, true);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert session updated
|
||||
// loadedSessions = loadPersistedSessionsPaginated(true, 10, 1, 1);
|
||||
// persistedSession = loadedSessions.get(0);
|
||||
// UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user1", realm), "127.0.0.2", started, started+10, "test-app", "third-party");
|
||||
// Assert.assertEquals("bar", persistedSession.getNote("foo"));
|
||||
// Assert.assertEquals(UserSessionModel.State.LOGGING_IN, persistedSession.getState());
|
||||
//
|
||||
// // Remove clientSession
|
||||
// persister.removeClientSession(clientSession.getId(), true);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert clientSession removed
|
||||
// loadedSessions = loadPersistedSessionsPaginated(true, 10, 1, 1);
|
||||
// persistedSession = loadedSessions.get(0);
|
||||
// UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user1", realm), "127.0.0.2", started, started + 10, "test-app");
|
||||
//
|
||||
// // Remove userSession
|
||||
// persister.removeUserSession(persistedSession.getId(), true);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert nothing found
|
||||
// loadPersistedSessionsPaginated(true, 10, 0, 0);
|
||||
// } finally {
|
||||
// Time.setOffset(0);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testOnRealmRemoved() {
|
||||
// RealmModel fooRealm = session.realms().createRealm("foo", "foo");
|
||||
// fooRealm.addClient("foo-app");
|
||||
// session.users().addUser(fooRealm, "user3");
|
||||
//
|
||||
// UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
|
||||
// createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Persist offline session
|
||||
// fooRealm = session.realms().getRealm("foo");
|
||||
// userSession = session.sessions().getUserSession(fooRealm, userSession.getId());
|
||||
// persistUserSession(userSession, true);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert session was persisted
|
||||
// loadPersistedSessionsPaginated(true, 10, 1, 1);
|
||||
//
|
||||
// // Remove realm
|
||||
// RealmManager realmMgr = new RealmManager(session);
|
||||
// realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert nothing loaded
|
||||
// loadPersistedSessionsPaginated(true, 10, 0, 0);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testOnClientRemoved() {
|
||||
// int started = Time.currentTime();
|
||||
//
|
||||
// RealmModel fooRealm = session.realms().createRealm("foo", "foo");
|
||||
// fooRealm.addClient("foo-app");
|
||||
// fooRealm.addClient("bar-app");
|
||||
// session.users().addUser(fooRealm, "user3");
|
||||
//
|
||||
// UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
|
||||
// createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
// createClientSession(fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Persist offline session
|
||||
// fooRealm = session.realms().getRealm("foo");
|
||||
// userSession = session.sessions().getUserSession(fooRealm, userSession.getId());
|
||||
// persistUserSession(userSession, true);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// RealmManager realmMgr = new RealmManager(session);
|
||||
// ClientManager clientMgr = new ClientManager(realmMgr);
|
||||
// fooRealm = realmMgr.getRealm("foo");
|
||||
//
|
||||
// // Assert session was persisted with both clientSessions
|
||||
// UserSessionModel persistedSession = loadPersistedSessionsPaginated(true, 10, 1, 1).get(0);
|
||||
// UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user3", fooRealm), "127.0.0.1", started, started, "foo-app", "bar-app");
|
||||
//
|
||||
// // Remove foo-app client
|
||||
// ClientModel client = fooRealm.getClientByClientId("foo-app");
|
||||
// clientMgr.removeClient(fooRealm, client);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// realmMgr = new RealmManager(session);
|
||||
// clientMgr = new ClientManager(realmMgr);
|
||||
// fooRealm = realmMgr.getRealm("foo");
|
||||
//
|
||||
// // Assert just one bar-app clientSession persisted now
|
||||
// persistedSession = loadPersistedSessionsPaginated(true, 10, 1, 1).get(0);
|
||||
// UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user3", fooRealm), "127.0.0.1", started, started, "bar-app");
|
||||
//
|
||||
// // Remove bar-app client
|
||||
// client = fooRealm.getClientByClientId("bar-app");
|
||||
// clientMgr.removeClient(fooRealm, client);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert nothing loaded - userSession was removed as well because it was last userSession
|
||||
// loadPersistedSessionsPaginated(true, 10, 0, 0);
|
||||
//
|
||||
// // Cleanup
|
||||
// realmMgr = new RealmManager(session);
|
||||
// realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testOnUserRemoved() {
|
||||
// // Create some sessions in infinispan
|
||||
// int started = Time.currentTime();
|
||||
// UserSessionModel[] origSessions = createSessions();
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Persist 2 offline sessions of 2 users
|
||||
// UserSessionModel userSession1 = session.sessions().getUserSession(realm, origSessions[1].getId());
|
||||
// UserSessionModel userSession2 = session.sessions().getUserSession(realm, origSessions[2].getId());
|
||||
// persistUserSession(userSession1, true);
|
||||
// persistUserSession(userSession2, true);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Load offline sessions
|
||||
// List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(true, 10, 1, 2);
|
||||
//
|
||||
// // Properly delete user and assert his offlineSession removed
|
||||
// UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
// new UserManager(session).removeUser(realm, user1);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// Assert.assertEquals(1, persister.getUserSessionsCount(true));
|
||||
// loadedSessions = loadPersistedSessionsPaginated(true, 10, 1, 1);
|
||||
// UserSessionModel persistedSession = loadedSessions.get(0);
|
||||
// UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user2", realm), "127.0.0.3", started, started, "test-app");
|
||||
//
|
||||
// // KEYCLOAK-2431 Assert that userSessionPersister is resistent even to situation, when users are deleted "directly"
|
||||
// UserModel user2 = session.users().getUserByUsername("user2", realm);
|
||||
// session.users().removeUser(realm, user2);
|
||||
//
|
||||
// loadedSessions = loadPersistedSessionsPaginated(true, 10, 0, 0);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// // KEYCLOAK-1999
|
||||
// @Test
|
||||
// public void testNoSessions() {
|
||||
// UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
// List<UserSessionModel> sessions = persister.loadUserSessions(0, 1, true);
|
||||
// Assert.assertEquals(0, sessions.size());
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private ClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
|
||||
// ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
|
||||
// if (userSession != null) clientSession.setUserSession(userSession);
|
||||
// clientSession.setRedirectUri(redirect);
|
||||
// if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
|
||||
// if (roles != null) clientSession.setRoles(roles);
|
||||
// if (protocolMappers != null) clientSession.setProtocolMappers(protocolMappers);
|
||||
// return clientSession;
|
||||
// }
|
||||
//
|
||||
// private UserSessionModel[] createSessions() {
|
||||
// UserSessionModel[] sessions = new UserSessionModel[3];
|
||||
// sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
|
||||
//
|
||||
// Set<String> roles = new HashSet<String>();
|
||||
// roles.add("one");
|
||||
// roles.add("two");
|
||||
//
|
||||
// Set<String> protocolMappers = new HashSet<String>();
|
||||
// protocolMappers.add("mapper-one");
|
||||
// protocolMappers.add("mapper-two");
|
||||
//
|
||||
// createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
|
||||
// createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
|
||||
// createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
|
||||
// createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// return sessions;
|
||||
// }
|
||||
//
|
||||
// private void persistUserSession(UserSessionModel userSession, boolean offline) {
|
||||
// persister.createUserSession(userSession, offline);
|
||||
// for (ClientSessionModel clientSession : userSession.getClientSessions()) {
|
||||
// persister.createClientSession(clientSession, offline);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void resetSession() {
|
||||
// kc.stopSession(session, true);
|
||||
// session = kc.startSession();
|
||||
// realm = session.realms().getRealm("test");
|
||||
// persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
// }
|
||||
//
|
||||
// public static void assertSessionLoaded(List<UserSessionModel> sessions, String id, UserModel user, String ipAddress, int started, int lastRefresh, String... clients) {
|
||||
// for (UserSessionModel session : sessions) {
|
||||
// if (session.getId().equals(id)) {
|
||||
// UserSessionProviderTest.assertSession(session, user, ipAddress, started, lastRefresh, clients);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// Assert.fail("Session with ID " + id + " not found in the list");
|
||||
// }
|
||||
//
|
||||
// private List<UserSessionModel> loadPersistedSessionsPaginated(boolean offline, int sessionsPerPage, int expectedPageCount, int expectedSessionsCount) {
|
||||
// int count = persister.getUserSessionsCount(offline);
|
||||
//
|
||||
// int start = 0;
|
||||
// int pageCount = 0;
|
||||
// boolean next = true;
|
||||
// List<UserSessionModel> result = new ArrayList<>();
|
||||
// while (next && start < count) {
|
||||
// List<UserSessionModel> sess = persister.loadUserSessions(start, sessionsPerPage, offline);
|
||||
// if (sess.size() == 0) {
|
||||
// next = false;
|
||||
// } else {
|
||||
// pageCount++;
|
||||
// start += sess.size();
|
||||
// result.addAll(sess);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Assert.assertEquals(pageCount, expectedPageCount);
|
||||
// Assert.assertEquals(result.size(), expectedSessionsCount);
|
||||
// return result;
|
||||
// }
|
||||
|
||||
@ClassRule
|
||||
public static KeycloakRule kc = new KeycloakRule();
|
||||
|
||||
private KeycloakSession session;
|
||||
private RealmModel realm;
|
||||
private UserSessionPersisterProvider persister;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
session = kc.startSession();
|
||||
realm = session.realms().getRealm("test");
|
||||
session.users().addUser(realm, "user1").setEmail("user1@localhost");
|
||||
session.users().addUser(realm, "user2").setEmail("user2@localhost");
|
||||
persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
resetSession();
|
||||
session.sessions().removeUserSessions(realm);
|
||||
UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
UserModel user2 = session.users().getUserByUsername("user2", realm);
|
||||
|
||||
UserManager um = new UserManager(session);
|
||||
if (user1 != null) {
|
||||
um.removeUser(realm, user1);
|
||||
}
|
||||
if (user2 != null) {
|
||||
um.removeUser(realm, user2);
|
||||
}
|
||||
kc.stopSession(session, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistenceWithLoad() {
|
||||
// Create some sessions in infinispan
|
||||
int started = Time.currentTime();
|
||||
UserSessionModel[] origSessions = createSessions();
|
||||
|
||||
resetSession();
|
||||
|
||||
// Persist 3 created userSessions and clientSessions as offline
|
||||
ClientModel testApp = realm.getClientByClientId("test-app");
|
||||
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, testApp);
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
persistUserSession(userSession, true);
|
||||
}
|
||||
|
||||
// Persist 1 online session
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, origSessions[0].getId());
|
||||
persistUserSession(userSession, false);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert online session
|
||||
List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(false, 1, 1, 1);
|
||||
UserSessionProviderTest.assertSession(loadedSessions.get(0), session.users().getUserByUsername("user1", realm), "127.0.0.1", started, started, "test-app", "third-party");
|
||||
|
||||
// Assert offline sessions
|
||||
loadedSessions = loadPersistedSessionsPaginated(true, 2, 2, 3);
|
||||
UserSessionProviderTest.assertSessions(loadedSessions, origSessions);
|
||||
|
||||
assertSessionLoaded(loadedSessions, origSessions[0].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.1", started, started, "test-app", "third-party");
|
||||
assertSessionLoaded(loadedSessions, origSessions[1].getId(), session.users().getUserByUsername("user1", realm), "127.0.0.2", started, started, "test-app");
|
||||
assertSessionLoaded(loadedSessions, origSessions[2].getId(), session.users().getUserByUsername("user2", realm), "127.0.0.3", started, started, "test-app");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateTimestamps() {
|
||||
// Create some sessions in infinispan
|
||||
int started = Time.currentTime();
|
||||
UserSessionModel[] origSessions = createSessions();
|
||||
|
||||
resetSession();
|
||||
|
||||
// Persist 3 created userSessions and clientSessions as offline
|
||||
ClientModel testApp = realm.getClientByClientId("test-app");
|
||||
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, testApp);
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
persistUserSession(userSession, true);
|
||||
}
|
||||
|
||||
// Persist 1 online session
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, origSessions[0].getId());
|
||||
persistUserSession(userSession, false);
|
||||
|
||||
resetSession();
|
||||
|
||||
// update timestamps
|
||||
int newTime = started + 50;
|
||||
persister.updateAllTimestamps(newTime);
|
||||
|
||||
// Assert online session
|
||||
List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(false, 1, 1, 1);
|
||||
Assert.assertEquals(2, assertTimestampsUpdated(loadedSessions, newTime));
|
||||
|
||||
// Assert offline sessions
|
||||
loadedSessions = loadPersistedSessionsPaginated(true, 2, 2, 3);
|
||||
Assert.assertEquals(4, assertTimestampsUpdated(loadedSessions, newTime));
|
||||
}
|
||||
|
||||
private int assertTimestampsUpdated(List<UserSessionModel> loadedSessions, int expectedTime) {
|
||||
int clientSessionsCount = 0;
|
||||
for (UserSessionModel loadedSession : loadedSessions) {
|
||||
Assert.assertEquals(expectedTime, loadedSession.getLastSessionRefresh());
|
||||
for (AuthenticatedClientSessionModel clientSession : loadedSession.getAuthenticatedClientSessions().values()) {
|
||||
Assert.assertEquals(expectedTime, clientSession.getTimestamp());
|
||||
clientSessionsCount++;
|
||||
}
|
||||
}
|
||||
return clientSessionsCount;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAndRemove() {
|
||||
// Create some sessions in infinispan
|
||||
int started = Time.currentTime();
|
||||
UserSessionModel[] origSessions = createSessions();
|
||||
|
||||
resetSession();
|
||||
|
||||
// Persist 1 offline session
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, origSessions[1].getId());
|
||||
persistUserSession(userSession, true);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Load offline session
|
||||
List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(true, 10, 1, 1);
|
||||
UserSessionModel persistedSession = loadedSessions.get(0);
|
||||
UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user1", realm), "127.0.0.2", started, started, "test-app");
|
||||
|
||||
// Update userSession
|
||||
Time.setOffset(10);
|
||||
try {
|
||||
persistedSession.setLastSessionRefresh(Time.currentTime());
|
||||
persistedSession.setNote("foo", "bar");
|
||||
persistedSession.setState(UserSessionModel.State.LOGGED_IN);
|
||||
persister.updateUserSession(persistedSession, true);
|
||||
|
||||
// create new clientSession
|
||||
AuthenticatedClientSessionModel clientSession = createClientSession(realm.getClientByClientId("third-party"), session.sessions().getUserSession(realm, persistedSession.getId()),
|
||||
"http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
persister.createClientSession(clientSession, true);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert session updated
|
||||
loadedSessions = loadPersistedSessionsPaginated(true, 10, 1, 1);
|
||||
persistedSession = loadedSessions.get(0);
|
||||
UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user1", realm), "127.0.0.2", started, started+10, "test-app", "third-party");
|
||||
Assert.assertEquals("bar", persistedSession.getNote("foo"));
|
||||
Assert.assertEquals(UserSessionModel.State.LOGGED_IN, persistedSession.getState());
|
||||
|
||||
// Remove clientSession
|
||||
persister.removeClientSession(userSession.getId(), realm.getClientByClientId("third-party").getId(), true);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert clientSession removed
|
||||
loadedSessions = loadPersistedSessionsPaginated(true, 10, 1, 1);
|
||||
persistedSession = loadedSessions.get(0);
|
||||
UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user1", realm), "127.0.0.2", started, started + 10, "test-app");
|
||||
|
||||
// Remove userSession
|
||||
persister.removeUserSession(persistedSession.getId(), true);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert nothing found
|
||||
loadPersistedSessionsPaginated(true, 10, 0, 0);
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnRealmRemoved() {
|
||||
RealmModel fooRealm = session.realms().createRealm("foo", "foo");
|
||||
fooRealm.addClient("foo-app");
|
||||
session.users().addUser(fooRealm, "user3");
|
||||
|
||||
UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
|
||||
createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
resetSession();
|
||||
|
||||
// Persist offline session
|
||||
fooRealm = session.realms().getRealm("foo");
|
||||
userSession = session.sessions().getUserSession(fooRealm, userSession.getId());
|
||||
persistUserSession(userSession, true);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert session was persisted
|
||||
loadPersistedSessionsPaginated(true, 10, 1, 1);
|
||||
|
||||
// Remove realm
|
||||
RealmManager realmMgr = new RealmManager(session);
|
||||
realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert nothing loaded
|
||||
loadPersistedSessionsPaginated(true, 10, 0, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClientRemoved() {
|
||||
int started = Time.currentTime();
|
||||
|
||||
RealmModel fooRealm = session.realms().createRealm("foo", "foo");
|
||||
fooRealm.addClient("foo-app");
|
||||
fooRealm.addClient("bar-app");
|
||||
session.users().addUser(fooRealm, "user3");
|
||||
|
||||
UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
|
||||
createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
createClientSession(fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
resetSession();
|
||||
|
||||
// Persist offline session
|
||||
fooRealm = session.realms().getRealm("foo");
|
||||
userSession = session.sessions().getUserSession(fooRealm, userSession.getId());
|
||||
persistUserSession(userSession, true);
|
||||
|
||||
resetSession();
|
||||
|
||||
RealmManager realmMgr = new RealmManager(session);
|
||||
ClientManager clientMgr = new ClientManager(realmMgr);
|
||||
fooRealm = realmMgr.getRealm("foo");
|
||||
|
||||
// Assert session was persisted with both clientSessions
|
||||
UserSessionModel persistedSession = loadPersistedSessionsPaginated(true, 10, 1, 1).get(0);
|
||||
UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user3", fooRealm), "127.0.0.1", started, started, "foo-app", "bar-app");
|
||||
|
||||
// Remove foo-app client
|
||||
ClientModel client = fooRealm.getClientByClientId("foo-app");
|
||||
clientMgr.removeClient(fooRealm, client);
|
||||
|
||||
resetSession();
|
||||
|
||||
realmMgr = new RealmManager(session);
|
||||
clientMgr = new ClientManager(realmMgr);
|
||||
fooRealm = realmMgr.getRealm("foo");
|
||||
|
||||
// Assert just one bar-app clientSession persisted now
|
||||
persistedSession = loadPersistedSessionsPaginated(true, 10, 1, 1).get(0);
|
||||
UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user3", fooRealm), "127.0.0.1", started, started, "bar-app");
|
||||
|
||||
// Remove bar-app client
|
||||
client = fooRealm.getClientByClientId("bar-app");
|
||||
clientMgr.removeClient(fooRealm, client);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert nothing loaded - userSession was removed as well because it was last userSession
|
||||
loadPersistedSessionsPaginated(true, 10, 0, 0);
|
||||
|
||||
// Cleanup
|
||||
realmMgr = new RealmManager(session);
|
||||
realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUserRemoved() {
|
||||
// Create some sessions in infinispan
|
||||
int started = Time.currentTime();
|
||||
UserSessionModel[] origSessions = createSessions();
|
||||
|
||||
resetSession();
|
||||
|
||||
// Persist 2 offline sessions of 2 users
|
||||
UserSessionModel userSession1 = session.sessions().getUserSession(realm, origSessions[1].getId());
|
||||
UserSessionModel userSession2 = session.sessions().getUserSession(realm, origSessions[2].getId());
|
||||
persistUserSession(userSession1, true);
|
||||
persistUserSession(userSession2, true);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Load offline sessions
|
||||
List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(true, 10, 1, 2);
|
||||
|
||||
// Properly delete user and assert his offlineSession removed
|
||||
UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
new UserManager(session).removeUser(realm, user1);
|
||||
|
||||
resetSession();
|
||||
|
||||
Assert.assertEquals(1, persister.getUserSessionsCount(true));
|
||||
loadedSessions = loadPersistedSessionsPaginated(true, 10, 1, 1);
|
||||
UserSessionModel persistedSession = loadedSessions.get(0);
|
||||
UserSessionProviderTest.assertSession(persistedSession, session.users().getUserByUsername("user2", realm), "127.0.0.3", started, started, "test-app");
|
||||
|
||||
// KEYCLOAK-2431 Assert that userSessionPersister is resistent even to situation, when users are deleted "directly"
|
||||
UserModel user2 = session.users().getUserByUsername("user2", realm);
|
||||
session.users().removeUser(realm, user2);
|
||||
|
||||
loadedSessions = loadPersistedSessionsPaginated(true, 10, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
// KEYCLOAK-1999
|
||||
@Test
|
||||
public void testNoSessions() {
|
||||
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
List<UserSessionModel> sessions = persister.loadUserSessions(0, 1, true);
|
||||
Assert.assertEquals(0, sessions.size());
|
||||
}
|
||||
|
||||
|
||||
private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
|
||||
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
|
||||
if (userSession != null) clientSession.setUserSession(userSession);
|
||||
clientSession.setRedirectUri(redirect);
|
||||
if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
|
||||
if (roles != null) clientSession.setRoles(roles);
|
||||
if (protocolMappers != null) clientSession.setProtocolMappers(protocolMappers);
|
||||
return clientSession;
|
||||
}
|
||||
|
||||
private UserSessionModel[] createSessions() {
|
||||
UserSessionModel[] sessions = new UserSessionModel[3];
|
||||
sessions[0] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
|
||||
|
||||
Set<String> roles = new HashSet<String>();
|
||||
roles.add("one");
|
||||
roles.add("two");
|
||||
|
||||
Set<String> protocolMappers = new HashSet<String>();
|
||||
protocolMappers.add("mapper-one");
|
||||
protocolMappers.add("mapper-two");
|
||||
|
||||
createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
|
||||
createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
sessions[1] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
|
||||
createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
sessions[2] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
|
||||
createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
return sessions;
|
||||
}
|
||||
|
||||
private void persistUserSession(UserSessionModel userSession, boolean offline) {
|
||||
persister.createUserSession(userSession, offline);
|
||||
for (AuthenticatedClientSessionModel clientSession : userSession.getAuthenticatedClientSessions().values()) {
|
||||
persister.createClientSession(clientSession, offline);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetSession() {
|
||||
kc.stopSession(session, true);
|
||||
session = kc.startSession();
|
||||
realm = session.realms().getRealm("test");
|
||||
persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
}
|
||||
|
||||
public static void assertSessionLoaded(List<UserSessionModel> sessions, String id, UserModel user, String ipAddress, int started, int lastRefresh, String... clients) {
|
||||
for (UserSessionModel session : sessions) {
|
||||
if (session.getId().equals(id)) {
|
||||
UserSessionProviderTest.assertSession(session, user, ipAddress, started, lastRefresh, clients);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Assert.fail("Session with ID " + id + " not found in the list");
|
||||
}
|
||||
|
||||
private List<UserSessionModel> loadPersistedSessionsPaginated(boolean offline, int sessionsPerPage, int expectedPageCount, int expectedSessionsCount) {
|
||||
int count = persister.getUserSessionsCount(offline);
|
||||
|
||||
int start = 0;
|
||||
int pageCount = 0;
|
||||
boolean next = true;
|
||||
List<UserSessionModel> result = new ArrayList<>();
|
||||
while (next && start < count) {
|
||||
List<UserSessionModel> sess = persister.loadUserSessions(start, sessionsPerPage, offline);
|
||||
if (sess.size() == 0) {
|
||||
next = false;
|
||||
} else {
|
||||
pageCount++;
|
||||
start += sess.size();
|
||||
result.addAll(sess);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(pageCount, expectedPageCount);
|
||||
Assert.assertEquals(result.size(), expectedSessionsCount);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,13 +24,14 @@ import org.junit.ClassRule;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.session.UserSessionPersisterProvider;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.services.managers.ClientManager;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
|
@ -41,7 +42,6 @@ import org.keycloak.testsuite.rule.LoggingRule;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -51,410 +51,380 @@ import java.util.Set;
|
|||
*/
|
||||
public class UserSessionProviderOfflineTest {
|
||||
|
||||
// TODO:mposolda
|
||||
// @ClassRule
|
||||
// public static KeycloakRule kc = new KeycloakRule();
|
||||
//
|
||||
// @Rule
|
||||
// public LoggingRule loggingRule = new LoggingRule(this);
|
||||
//
|
||||
// private KeycloakSession session;
|
||||
// private RealmModel realm;
|
||||
// private UserSessionManager sessionManager;
|
||||
// private UserSessionPersisterProvider persister;
|
||||
//
|
||||
// @Before
|
||||
// public void before() {
|
||||
// session = kc.startSession();
|
||||
// realm = session.realms().getRealm("test");
|
||||
// session.users().addUser(realm, "user1").setEmail("user1@localhost");
|
||||
// session.users().addUser(realm, "user2").setEmail("user2@localhost");
|
||||
// sessionManager = new UserSessionManager(session);
|
||||
// persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
// }
|
||||
//
|
||||
// @After
|
||||
// public void after() {
|
||||
// resetSession();
|
||||
// session.sessions().removeUserSessions(realm);
|
||||
// UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
// UserModel user2 = session.users().getUserByUsername("user2", realm);
|
||||
//
|
||||
// UserManager um = new UserManager(session);
|
||||
// um.removeUser(realm, user1);
|
||||
// um.removeUser(realm, user2);
|
||||
// kc.stopSession(session, true);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Test
|
||||
// public void testOfflineSessionsCrud() {
|
||||
// // Create some online sessions in infinispan
|
||||
// int started = Time.currentTime();
|
||||
// UserSessionModel[] origSessions = createSessions();
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// Map<String, String> offlineSessions = new HashMap<>();
|
||||
//
|
||||
// // Persist 3 created userSessions and clientSessions as offline
|
||||
// ClientModel testApp = realm.getClientByClientId("test-app");
|
||||
// List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, testApp);
|
||||
// for (UserSessionModel userSession : userSessions) {
|
||||
// offlineSessions.putAll(createOfflineSessionIncludeClientSessions(userSession));
|
||||
// }
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert all previously saved offline sessions found
|
||||
// for (Map.Entry<String, String> entry : offlineSessions.entrySet()) {
|
||||
// Assert.assertTrue(sessionManager.findOfflineClientSession(realm, entry.getKey()) != null);
|
||||
//
|
||||
// UserSessionModel offlineSession = session.sessions().getUserSession(realm, entry.getValue());
|
||||
// boolean found = false;
|
||||
// for (ClientSessionModel clientSession : offlineSession.getClientSessions()) {
|
||||
// if (clientSession.getId().equals(entry.getKey())) {
|
||||
// found = true;
|
||||
// }
|
||||
// }
|
||||
// Assert.assertTrue(found);
|
||||
// }
|
||||
//
|
||||
// // Find clients with offline token
|
||||
// UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
// Set<ClientModel> clients = sessionManager.findClientsWithOfflineToken(realm, user1);
|
||||
// Assert.assertEquals(clients.size(), 2);
|
||||
// for (ClientModel client : clients) {
|
||||
// Assert.assertTrue(client.getClientId().equals("test-app") || client.getClientId().equals("third-party"));
|
||||
// }
|
||||
//
|
||||
// UserModel user2 = session.users().getUserByUsername("user2", realm);
|
||||
// clients = sessionManager.findClientsWithOfflineToken(realm, user2);
|
||||
// Assert.assertEquals(clients.size(), 1);
|
||||
// Assert.assertTrue(clients.iterator().next().getClientId().equals("test-app"));
|
||||
//
|
||||
// // Test count
|
||||
// testApp = realm.getClientByClientId("test-app");
|
||||
// ClientModel thirdparty = realm.getClientByClientId("third-party");
|
||||
// Assert.assertEquals(3, session.sessions().getOfflineSessionsCount(realm, testApp));
|
||||
// Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, thirdparty));
|
||||
//
|
||||
// // Revoke "test-app" for user1
|
||||
// sessionManager.revokeOfflineToken(user1, testApp);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert userSession revoked
|
||||
// testApp = realm.getClientByClientId("test-app");
|
||||
// thirdparty = realm.getClientByClientId("third-party");
|
||||
// Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, testApp));
|
||||
// Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, thirdparty));
|
||||
//
|
||||
// List<UserSessionModel> testAppSessions = session.sessions().getOfflineUserSessions(realm, testApp, 0, 10);
|
||||
// List<UserSessionModel> thirdpartySessions = session.sessions().getOfflineUserSessions(realm, thirdparty, 0, 10);
|
||||
// Assert.assertEquals(1, testAppSessions.size());
|
||||
// Assert.assertEquals("127.0.0.3", testAppSessions.get(0).getIpAddress());
|
||||
// Assert.assertEquals("user2", testAppSessions.get(0).getUser().getUsername());
|
||||
// Assert.assertEquals(1, thirdpartySessions.size());
|
||||
// Assert.assertEquals("127.0.0.1", thirdpartySessions.get(0).getIpAddress());
|
||||
// Assert.assertEquals("user1", thirdpartySessions.get(0).getUser().getUsername());
|
||||
//
|
||||
// user1 = session.users().getUserByUsername("user1", realm);
|
||||
// user2 = session.users().getUserByUsername("user2", realm);
|
||||
// clients = sessionManager.findClientsWithOfflineToken(realm, user1);
|
||||
// Assert.assertEquals(1, clients.size());
|
||||
// Assert.assertEquals("third-party", clients.iterator().next().getClientId());
|
||||
// clients = sessionManager.findClientsWithOfflineToken(realm, user2);
|
||||
// Assert.assertEquals(1, clients.size());
|
||||
// Assert.assertEquals("test-app", clients.iterator().next().getClientId());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testOnRealmRemoved() {
|
||||
// RealmModel fooRealm = session.realms().createRealm("foo", "foo");
|
||||
// fooRealm.addClient("foo-app");
|
||||
// session.users().addUser(fooRealm, "user3");
|
||||
//
|
||||
// UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
|
||||
// ClientSessionModel clientSession = createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Persist offline session
|
||||
// fooRealm = session.realms().getRealm("foo");
|
||||
// userSession = session.sessions().getUserSession(fooRealm, userSession.getId());
|
||||
// clientSession = session.sessions().getClientSession(fooRealm, clientSession.getId());
|
||||
// sessionManager.createOrUpdateOfflineSession(userSession.getClientSessions().get(0), userSession);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// ClientSessionModel offlineClientSession = sessionManager.findOfflineClientSession(fooRealm, clientSession.getId());
|
||||
// Assert.assertEquals("foo-app", offlineClientSession.getClient().getClientId());
|
||||
// Assert.assertEquals("user3", offlineClientSession.getUserSession().getUser().getUsername());
|
||||
// Assert.assertEquals(offlineClientSession.getId(), offlineClientSession.getUserSession().getClientSessions().get(0).getId());
|
||||
//
|
||||
// // Remove realm
|
||||
// RealmManager realmMgr = new RealmManager(session);
|
||||
// realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// fooRealm = session.realms().createRealm("foo", "foo");
|
||||
// fooRealm.addClient("foo-app");
|
||||
// session.users().addUser(fooRealm, "user3");
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert nothing loaded
|
||||
// fooRealm = session.realms().getRealm("foo");
|
||||
// Assert.assertNull(sessionManager.findOfflineClientSession(fooRealm, clientSession.getId()));
|
||||
// Assert.assertEquals(0, session.sessions().getOfflineSessionsCount(fooRealm, fooRealm.getClientByClientId("foo-app")));
|
||||
//
|
||||
// // Cleanup
|
||||
// realmMgr = new RealmManager(session);
|
||||
// realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testOnClientRemoved() {
|
||||
// int started = Time.currentTime();
|
||||
//
|
||||
// RealmModel fooRealm = session.realms().createRealm("foo", "foo");
|
||||
// fooRealm.addClient("foo-app");
|
||||
// fooRealm.addClient("bar-app");
|
||||
// session.users().addUser(fooRealm, "user3");
|
||||
//
|
||||
// UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
|
||||
// createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
// createClientSession(fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Create offline session
|
||||
// fooRealm = session.realms().getRealm("foo");
|
||||
// userSession = session.sessions().getUserSession(fooRealm, userSession.getId());
|
||||
// createOfflineSessionIncludeClientSessions(userSession);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// RealmManager realmMgr = new RealmManager(session);
|
||||
// ClientManager clientMgr = new ClientManager(realmMgr);
|
||||
// fooRealm = realmMgr.getRealm("foo");
|
||||
//
|
||||
// // Assert session was persisted with both clientSessions
|
||||
// UserSessionModel offlineSession = session.sessions().getOfflineUserSession(fooRealm, userSession.getId());
|
||||
// UserSessionProviderTest.assertSession(offlineSession, session.users().getUserByUsername("user3", fooRealm), "127.0.0.1", started, started, "foo-app", "bar-app");
|
||||
//
|
||||
// // Remove foo-app client
|
||||
// ClientModel client = fooRealm.getClientByClientId("foo-app");
|
||||
// clientMgr.removeClient(fooRealm, client);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// realmMgr = new RealmManager(session);
|
||||
// clientMgr = new ClientManager(realmMgr);
|
||||
// fooRealm = realmMgr.getRealm("foo");
|
||||
//
|
||||
// // Assert just one bar-app clientSession persisted now
|
||||
// offlineSession = session.sessions().getOfflineUserSession(fooRealm, userSession.getId());
|
||||
// Assert.assertEquals(1, offlineSession.getClientSessions().size());
|
||||
// Assert.assertEquals("bar-app", offlineSession.getClientSessions().get(0).getClient().getClientId());
|
||||
//
|
||||
// // Remove bar-app client
|
||||
// client = fooRealm.getClientByClientId("bar-app");
|
||||
// clientMgr.removeClient(fooRealm, client);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert nothing loaded - userSession was removed as well because it was last userSession
|
||||
// offlineSession = session.sessions().getOfflineUserSession(fooRealm, userSession.getId());
|
||||
// Assert.assertEquals(0, offlineSession.getClientSessions().size());
|
||||
//
|
||||
// // Cleanup
|
||||
// realmMgr = new RealmManager(session);
|
||||
// realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testOnUserRemoved() {
|
||||
// int started = Time.currentTime();
|
||||
//
|
||||
// RealmModel fooRealm = session.realms().createRealm("foo", "foo");
|
||||
// fooRealm.addClient("foo-app");
|
||||
// session.users().addUser(fooRealm, "user3");
|
||||
//
|
||||
// UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
|
||||
// ClientSessionModel clientSession = createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Create offline session
|
||||
// fooRealm = session.realms().getRealm("foo");
|
||||
// userSession = session.sessions().getUserSession(fooRealm, userSession.getId());
|
||||
// createOfflineSessionIncludeClientSessions(userSession);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// RealmManager realmMgr = new RealmManager(session);
|
||||
// fooRealm = realmMgr.getRealm("foo");
|
||||
// UserModel user3 = session.users().getUserByUsername("user3", fooRealm);
|
||||
//
|
||||
// // Assert session was persisted with both clientSessions
|
||||
// UserSessionModel offlineSession = session.sessions().getOfflineUserSession(fooRealm, userSession.getId());
|
||||
// UserSessionProviderTest.assertSession(offlineSession, user3, "127.0.0.1", started, started, "foo-app");
|
||||
//
|
||||
// // Remove user3
|
||||
// new UserManager(session).removeUser(fooRealm, user3);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert userSession removed as well
|
||||
// Assert.assertNull(session.sessions().getOfflineUserSession(fooRealm, userSession.getId()));
|
||||
// Assert.assertNull(session.sessions().getOfflineClientSession(fooRealm, clientSession.getId()));
|
||||
//
|
||||
// // Cleanup
|
||||
// realmMgr = new RealmManager(session);
|
||||
// realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testExpired() {
|
||||
// // Create some online sessions in infinispan
|
||||
// int started = Time.currentTime();
|
||||
// UserSessionModel[] origSessions = createSessions();
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// Map<String, String> offlineSessions = new HashMap<>();
|
||||
//
|
||||
// // Persist 3 created userSessions and clientSessions as offline
|
||||
// ClientModel testApp = realm.getClientByClientId("test-app");
|
||||
// List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, testApp);
|
||||
// for (UserSessionModel userSession : userSessions) {
|
||||
// offlineSessions.putAll(createOfflineSessionIncludeClientSessions(userSession));
|
||||
// }
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // Assert all previously saved offline sessions found
|
||||
// for (Map.Entry<String, String> entry : offlineSessions.entrySet()) {
|
||||
// Assert.assertTrue(sessionManager.findOfflineClientSession(realm, entry.getKey()) != null);
|
||||
// }
|
||||
//
|
||||
// UserSessionModel session0 = session.sessions().getOfflineUserSession(realm, origSessions[0].getId());
|
||||
// Assert.assertNotNull(session0);
|
||||
// List<String> clientSessions = new LinkedList<>();
|
||||
// for (ClientSessionModel clientSession : session0.getClientSessions()) {
|
||||
// clientSessions.add(clientSession.getId());
|
||||
// Assert.assertNotNull(session.sessions().getOfflineClientSession(realm, clientSession.getId()));
|
||||
// }
|
||||
//
|
||||
// UserSessionModel session1 = session.sessions().getOfflineUserSession(realm, origSessions[1].getId());
|
||||
// Assert.assertEquals(1, session1.getClientSessions().size());
|
||||
// ClientSessionModel cls1 = session1.getClientSessions().get(0);
|
||||
//
|
||||
// // sessions are in persister too
|
||||
// Assert.assertEquals(3, persister.getUserSessionsCount(true));
|
||||
//
|
||||
// // Set lastSessionRefresh to session[0] to 0
|
||||
// session0.setLastSessionRefresh(0);
|
||||
//
|
||||
// // Set timestamp to cls1 to 0
|
||||
// cls1.setTimestamp(0);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// session.sessions().removeExpired(realm);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// // assert session0 not found now
|
||||
// Assert.assertNull(session.sessions().getOfflineUserSession(realm, origSessions[0].getId()));
|
||||
// for (String clientSession : clientSessions) {
|
||||
// Assert.assertNull(session.sessions().getOfflineClientSession(realm, origSessions[0].getId()));
|
||||
// offlineSessions.remove(clientSession);
|
||||
// }
|
||||
//
|
||||
// // Assert cls1 not found too
|
||||
// for (Map.Entry<String, String> entry : offlineSessions.entrySet()) {
|
||||
// String userSessionId = entry.getValue();
|
||||
// if (userSessionId.equals(session1.getId())) {
|
||||
// Assert.assertFalse(sessionManager.findOfflineClientSession(realm, entry.getKey()) != null);
|
||||
// } else {
|
||||
// Assert.assertTrue(sessionManager.findOfflineClientSession(realm, entry.getKey()) != null);
|
||||
// }
|
||||
// }
|
||||
// Assert.assertEquals(1, persister.getUserSessionsCount(true));
|
||||
//
|
||||
// // Expire everything and assert nothing found
|
||||
// Time.setOffset(3000000);
|
||||
// try {
|
||||
// session.sessions().removeExpired(realm);
|
||||
//
|
||||
// resetSession();
|
||||
//
|
||||
// for (Map.Entry<String, String> entry : offlineSessions.entrySet()) {
|
||||
// Assert.assertTrue(sessionManager.findOfflineClientSession(realm, entry.getKey()) == null);
|
||||
// }
|
||||
// Assert.assertEquals(0, persister.getUserSessionsCount(true));
|
||||
//
|
||||
// } finally {
|
||||
// Time.setOffset(0);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private Map<String, String> createOfflineSessionIncludeClientSessions(UserSessionModel userSession) {
|
||||
// Map<String, String> offlineSessions = new HashMap<>();
|
||||
//
|
||||
// for (ClientSessionModel clientSession : userSession.getClientSessions()) {
|
||||
// sessionManager.createOrUpdateOfflineSession(clientSession, userSession);
|
||||
// offlineSessions.put(clientSession.getId(), userSession.getId());
|
||||
// }
|
||||
// return offlineSessions;
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// private void resetSession() {
|
||||
// kc.stopSession(session, true);
|
||||
// session = kc.startSession();
|
||||
// realm = session.realms().getRealm("test");
|
||||
// sessionManager = new UserSessionManager(session);
|
||||
// persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
// }
|
||||
//
|
||||
// private ClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
|
||||
// ClientSessionModel clientSession = session.sessions().createClientSession(client.getRealm(), client);
|
||||
// if (userSession != null) clientSession.setUserSession(userSession);
|
||||
// clientSession.setRedirectUri(redirect);
|
||||
// if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
|
||||
// if (roles != null) clientSession.setRoles(roles);
|
||||
// if (protocolMappers != null) clientSession.setProtocolMappers(protocolMappers);
|
||||
// return clientSession;
|
||||
// }
|
||||
//
|
||||
// private UserSessionModel[] createSessions() {
|
||||
// UserSessionModel[] sessions = new UserSessionModel[3];
|
||||
// sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
|
||||
//
|
||||
// Set<String> roles = new HashSet<String>();
|
||||
// roles.add("one");
|
||||
// roles.add("two");
|
||||
//
|
||||
// Set<String> protocolMappers = new HashSet<String>();
|
||||
// protocolMappers.add("mapper-one");
|
||||
// protocolMappers.add("mapper-two");
|
||||
//
|
||||
// createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
|
||||
// createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
|
||||
// createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
|
||||
// createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
//
|
||||
// return sessions;
|
||||
// }
|
||||
@ClassRule
|
||||
public static KeycloakRule kc = new KeycloakRule();
|
||||
|
||||
@Rule
|
||||
public LoggingRule loggingRule = new LoggingRule(this);
|
||||
|
||||
private KeycloakSession session;
|
||||
private RealmModel realm;
|
||||
private UserSessionManager sessionManager;
|
||||
private UserSessionPersisterProvider persister;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
session = kc.startSession();
|
||||
realm = session.realms().getRealm("test");
|
||||
session.users().addUser(realm, "user1").setEmail("user1@localhost");
|
||||
session.users().addUser(realm, "user2").setEmail("user2@localhost");
|
||||
sessionManager = new UserSessionManager(session);
|
||||
persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
resetSession();
|
||||
session.sessions().removeUserSessions(realm);
|
||||
UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
UserModel user2 = session.users().getUserByUsername("user2", realm);
|
||||
|
||||
UserManager um = new UserManager(session);
|
||||
um.removeUser(realm, user1);
|
||||
um.removeUser(realm, user2);
|
||||
kc.stopSession(session, true);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testOfflineSessionsCrud() {
|
||||
// Create some online sessions in infinispan
|
||||
int started = Time.currentTime();
|
||||
UserSessionModel[] origSessions = createSessions();
|
||||
|
||||
resetSession();
|
||||
|
||||
// Key is userSession ID, values are client UUIDS
|
||||
Map<String, Set<String>> offlineSessions = new HashMap<>();
|
||||
|
||||
// Persist 3 created userSessions and clientSessions as offline
|
||||
ClientModel testApp = realm.getClientByClientId("test-app");
|
||||
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, testApp);
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
offlineSessions.put(userSession.getId(), createOfflineSessionIncludeClientSessions(userSession));
|
||||
}
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert all previously saved offline sessions found
|
||||
for (Map.Entry<String, Set<String>> entry : offlineSessions.entrySet()) {
|
||||
UserSessionModel offlineSession = sessionManager.findOfflineUserSession(realm, entry.getKey());
|
||||
Assert.assertNotNull(offlineSession);
|
||||
Assert.assertEquals(offlineSession.getAuthenticatedClientSessions().keySet(), entry.getValue());
|
||||
}
|
||||
|
||||
// Find clients with offline token
|
||||
UserModel user1 = session.users().getUserByUsername("user1", realm);
|
||||
Set<ClientModel> clients = sessionManager.findClientsWithOfflineToken(realm, user1);
|
||||
Assert.assertEquals(clients.size(), 2);
|
||||
for (ClientModel client : clients) {
|
||||
Assert.assertTrue(client.getClientId().equals("test-app") || client.getClientId().equals("third-party"));
|
||||
}
|
||||
|
||||
UserModel user2 = session.users().getUserByUsername("user2", realm);
|
||||
clients = sessionManager.findClientsWithOfflineToken(realm, user2);
|
||||
Assert.assertEquals(clients.size(), 1);
|
||||
Assert.assertTrue(clients.iterator().next().getClientId().equals("test-app"));
|
||||
|
||||
// Test count
|
||||
testApp = realm.getClientByClientId("test-app");
|
||||
ClientModel thirdparty = realm.getClientByClientId("third-party");
|
||||
Assert.assertEquals(3, session.sessions().getOfflineSessionsCount(realm, testApp));
|
||||
Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, thirdparty));
|
||||
|
||||
// Revoke "test-app" for user1
|
||||
sessionManager.revokeOfflineToken(user1, testApp);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert userSession revoked
|
||||
testApp = realm.getClientByClientId("test-app");
|
||||
thirdparty = realm.getClientByClientId("third-party");
|
||||
Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, testApp));
|
||||
Assert.assertEquals(1, session.sessions().getOfflineSessionsCount(realm, thirdparty));
|
||||
|
||||
List<UserSessionModel> testAppSessions = session.sessions().getOfflineUserSessions(realm, testApp, 0, 10);
|
||||
List<UserSessionModel> thirdpartySessions = session.sessions().getOfflineUserSessions(realm, thirdparty, 0, 10);
|
||||
Assert.assertEquals(1, testAppSessions.size());
|
||||
Assert.assertEquals("127.0.0.3", testAppSessions.get(0).getIpAddress());
|
||||
Assert.assertEquals("user2", testAppSessions.get(0).getUser().getUsername());
|
||||
Assert.assertEquals(1, thirdpartySessions.size());
|
||||
Assert.assertEquals("127.0.0.1", thirdpartySessions.get(0).getIpAddress());
|
||||
Assert.assertEquals("user1", thirdpartySessions.get(0).getUser().getUsername());
|
||||
|
||||
user1 = session.users().getUserByUsername("user1", realm);
|
||||
user2 = session.users().getUserByUsername("user2", realm);
|
||||
clients = sessionManager.findClientsWithOfflineToken(realm, user1);
|
||||
Assert.assertEquals(1, clients.size());
|
||||
Assert.assertEquals("third-party", clients.iterator().next().getClientId());
|
||||
clients = sessionManager.findClientsWithOfflineToken(realm, user2);
|
||||
Assert.assertEquals(1, clients.size());
|
||||
Assert.assertEquals("test-app", clients.iterator().next().getClientId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnRealmRemoved() {
|
||||
RealmModel fooRealm = session.realms().createRealm("foo", "foo");
|
||||
fooRealm.addClient("foo-app");
|
||||
session.users().addUser(fooRealm, "user3");
|
||||
|
||||
UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
|
||||
AuthenticatedClientSessionModel clientSession = createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
resetSession();
|
||||
|
||||
// Persist offline session
|
||||
fooRealm = session.realms().getRealm("foo");
|
||||
userSession = session.sessions().getUserSession(fooRealm, userSession.getId());
|
||||
createOfflineSessionIncludeClientSessions(userSession);
|
||||
|
||||
resetSession();
|
||||
|
||||
UserSessionModel offlineUserSession = sessionManager.findOfflineUserSession(fooRealm, userSession.getId());
|
||||
Assert.assertEquals(offlineUserSession.getAuthenticatedClientSessions().size(), 1);
|
||||
AuthenticatedClientSessionModel offlineClientSession = offlineUserSession.getAuthenticatedClientSessions().values().iterator().next();
|
||||
Assert.assertEquals("foo-app", offlineClientSession.getClient().getClientId());
|
||||
Assert.assertEquals("user3", offlineClientSession.getUserSession().getUser().getUsername());
|
||||
|
||||
// Remove realm
|
||||
RealmManager realmMgr = new RealmManager(session);
|
||||
realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
|
||||
resetSession();
|
||||
|
||||
fooRealm = session.realms().createRealm("foo", "foo");
|
||||
fooRealm.addClient("foo-app");
|
||||
session.users().addUser(fooRealm, "user3");
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert nothing loaded
|
||||
fooRealm = session.realms().getRealm("foo");
|
||||
Assert.assertNull(sessionManager.findOfflineUserSession(fooRealm, userSession.getId()));
|
||||
Assert.assertEquals(0, session.sessions().getOfflineSessionsCount(fooRealm, fooRealm.getClientByClientId("foo-app")));
|
||||
|
||||
// Cleanup
|
||||
realmMgr = new RealmManager(session);
|
||||
realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClientRemoved() {
|
||||
int started = Time.currentTime();
|
||||
|
||||
RealmModel fooRealm = session.realms().createRealm("foo", "foo");
|
||||
fooRealm.addClient("foo-app");
|
||||
fooRealm.addClient("bar-app");
|
||||
session.users().addUser(fooRealm, "user3");
|
||||
|
||||
UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
|
||||
createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
createClientSession(fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
resetSession();
|
||||
|
||||
// Create offline session
|
||||
fooRealm = session.realms().getRealm("foo");
|
||||
userSession = session.sessions().getUserSession(fooRealm, userSession.getId());
|
||||
createOfflineSessionIncludeClientSessions(userSession);
|
||||
|
||||
resetSession();
|
||||
|
||||
RealmManager realmMgr = new RealmManager(session);
|
||||
ClientManager clientMgr = new ClientManager(realmMgr);
|
||||
fooRealm = realmMgr.getRealm("foo");
|
||||
|
||||
// Assert session was persisted with both clientSessions
|
||||
UserSessionModel offlineSession = session.sessions().getOfflineUserSession(fooRealm, userSession.getId());
|
||||
UserSessionProviderTest.assertSession(offlineSession, session.users().getUserByUsername("user3", fooRealm), "127.0.0.1", started, started, "foo-app", "bar-app");
|
||||
|
||||
// Remove foo-app client
|
||||
ClientModel client = fooRealm.getClientByClientId("foo-app");
|
||||
clientMgr.removeClient(fooRealm, client);
|
||||
|
||||
resetSession();
|
||||
|
||||
realmMgr = new RealmManager(session);
|
||||
clientMgr = new ClientManager(realmMgr);
|
||||
fooRealm = realmMgr.getRealm("foo");
|
||||
|
||||
// Assert just one bar-app clientSession persisted now
|
||||
offlineSession = session.sessions().getOfflineUserSession(fooRealm, userSession.getId());
|
||||
Assert.assertEquals(1, offlineSession.getAuthenticatedClientSessions().size());
|
||||
Assert.assertEquals("bar-app", offlineSession.getAuthenticatedClientSessions().values().iterator().next().getClient().getClientId());
|
||||
|
||||
// Remove bar-app client
|
||||
client = fooRealm.getClientByClientId("bar-app");
|
||||
clientMgr.removeClient(fooRealm, client);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert nothing loaded - userSession was removed as well because it was last userSession
|
||||
realmMgr = new RealmManager(session);
|
||||
fooRealm = realmMgr.getRealm("foo");
|
||||
offlineSession = session.sessions().getOfflineUserSession(fooRealm, userSession.getId());
|
||||
Assert.assertEquals(0, offlineSession.getAuthenticatedClientSessions().size());
|
||||
|
||||
// Cleanup
|
||||
realmMgr = new RealmManager(session);
|
||||
realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUserRemoved() {
|
||||
int started = Time.currentTime();
|
||||
|
||||
RealmModel fooRealm = session.realms().createRealm("foo", "foo");
|
||||
fooRealm.addClient("foo-app");
|
||||
session.users().addUser(fooRealm, "user3");
|
||||
|
||||
UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
|
||||
AuthenticatedClientSessionModel clientSession = createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
resetSession();
|
||||
|
||||
// Create offline session
|
||||
fooRealm = session.realms().getRealm("foo");
|
||||
userSession = session.sessions().getUserSession(fooRealm, userSession.getId());
|
||||
createOfflineSessionIncludeClientSessions(userSession);
|
||||
|
||||
resetSession();
|
||||
|
||||
RealmManager realmMgr = new RealmManager(session);
|
||||
fooRealm = realmMgr.getRealm("foo");
|
||||
UserModel user3 = session.users().getUserByUsername("user3", fooRealm);
|
||||
|
||||
// Assert session was persisted with both clientSessions
|
||||
UserSessionModel offlineSession = session.sessions().getOfflineUserSession(fooRealm, userSession.getId());
|
||||
UserSessionProviderTest.assertSession(offlineSession, user3, "127.0.0.1", started, started, "foo-app");
|
||||
|
||||
// Remove user3
|
||||
new UserManager(session).removeUser(fooRealm, user3);
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert userSession removed as well
|
||||
Assert.assertNull(session.sessions().getOfflineUserSession(fooRealm, userSession.getId()));
|
||||
|
||||
// Cleanup
|
||||
realmMgr = new RealmManager(session);
|
||||
realmMgr.removeRealm(realmMgr.getRealm("foo"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpired() {
|
||||
// Create some online sessions in infinispan
|
||||
int started = Time.currentTime();
|
||||
UserSessionModel[] origSessions = createSessions();
|
||||
|
||||
resetSession();
|
||||
|
||||
// Key is userSessionId, value is set of client UUIDS
|
||||
Map<String, Set<String>> offlineSessions = new HashMap<>();
|
||||
|
||||
// Persist 3 created userSessions and clientSessions as offline
|
||||
ClientModel testApp = realm.getClientByClientId("test-app");
|
||||
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, testApp);
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
offlineSessions.put(userSession.getId(), createOfflineSessionIncludeClientSessions(userSession));
|
||||
}
|
||||
|
||||
resetSession();
|
||||
|
||||
// Assert all previously saved offline sessions found
|
||||
for (Map.Entry<String, Set<String>> entry : offlineSessions.entrySet()) {
|
||||
UserSessionModel foundSession = sessionManager.findOfflineUserSession(realm, entry.getKey());
|
||||
Assert.assertEquals(foundSession.getAuthenticatedClientSessions().keySet(), entry.getValue());
|
||||
}
|
||||
|
||||
UserSessionModel session0 = session.sessions().getOfflineUserSession(realm, origSessions[0].getId());
|
||||
Assert.assertNotNull(session0);
|
||||
|
||||
// sessions are in persister too
|
||||
Assert.assertEquals(3, persister.getUserSessionsCount(true));
|
||||
|
||||
// Set lastSessionRefresh to session[0] to 0
|
||||
session0.setLastSessionRefresh(0);
|
||||
|
||||
resetSession();
|
||||
|
||||
session.sessions().removeExpired(realm);
|
||||
|
||||
resetSession();
|
||||
|
||||
// assert session0 not found now
|
||||
Assert.assertNull(session.sessions().getOfflineUserSession(realm, origSessions[0].getId()));
|
||||
|
||||
Assert.assertEquals(2, persister.getUserSessionsCount(true));
|
||||
|
||||
// Expire everything and assert nothing found
|
||||
Time.setOffset(3000000);
|
||||
try {
|
||||
session.sessions().removeExpired(realm);
|
||||
|
||||
resetSession();
|
||||
|
||||
for (String userSessionId : offlineSessions.keySet()) {
|
||||
Assert.assertNull(sessionManager.findOfflineUserSession(realm, userSessionId));
|
||||
}
|
||||
Assert.assertEquals(0, persister.getUserSessionsCount(true));
|
||||
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> createOfflineSessionIncludeClientSessions(UserSessionModel userSession) {
|
||||
Set<String> offlineSessions = new HashSet<>();
|
||||
|
||||
for (AuthenticatedClientSessionModel clientSession : userSession.getAuthenticatedClientSessions().values()) {
|
||||
sessionManager.createOrUpdateOfflineSession(clientSession, userSession);
|
||||
offlineSessions.add(clientSession.getClient().getId());
|
||||
}
|
||||
return offlineSessions;
|
||||
}
|
||||
|
||||
|
||||
private void resetSession() {
|
||||
kc.stopSession(session, true);
|
||||
session = kc.startSession();
|
||||
realm = session.realms().getRealm("test");
|
||||
sessionManager = new UserSessionManager(session);
|
||||
persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
}
|
||||
|
||||
private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
|
||||
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(client.getRealm(), client, userSession);
|
||||
if (userSession != null) clientSession.setUserSession(userSession);
|
||||
clientSession.setRedirectUri(redirect);
|
||||
if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
|
||||
if (roles != null) clientSession.setRoles(roles);
|
||||
if (protocolMappers != null) clientSession.setProtocolMappers(protocolMappers);
|
||||
return clientSession;
|
||||
}
|
||||
|
||||
private UserSessionModel[] createSessions() {
|
||||
UserSessionModel[] sessions = new UserSessionModel[3];
|
||||
sessions[0] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
|
||||
|
||||
Set<String> roles = new HashSet<String>();
|
||||
roles.add("one");
|
||||
roles.add("two");
|
||||
|
||||
Set<String> protocolMappers = new HashSet<String>();
|
||||
protocolMappers.add("mapper-one");
|
||||
protocolMappers.add("mapper-two");
|
||||
|
||||
createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
|
||||
createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
sessions[1] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
|
||||
createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
sessions[2] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
|
||||
createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
return sessions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.junit.Test;
|
|||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserLoginFailureModel;
|
||||
|
@ -37,8 +36,8 @@ import org.keycloak.models.UserManager;
|
|||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -106,22 +105,36 @@ public class UserSessionProviderTest {
|
|||
assertEquals(1000, session.sessions().getUserSession(realm, sessions[0].getId()).getLastSessionRefresh());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestartSession() {
|
||||
int started = Time.currentTime();
|
||||
UserSessionModel[] sessions = createSessions();
|
||||
|
||||
Time.setOffset(100);
|
||||
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, sessions[0].getId());
|
||||
assertSession(userSession, session.users().getUserByUsername("user1", realm), "127.0.0.1", started, started, "test-app", "third-party");
|
||||
|
||||
userSession.restartSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.6", "form", true, null, null);
|
||||
|
||||
resetSession();
|
||||
|
||||
userSession = session.sessions().getUserSession(realm, sessions[0].getId());
|
||||
assertSession(userSession, session.users().getUserByUsername("user2", realm), "127.0.0.6", started + 100, started + 100);
|
||||
|
||||
Time.setOffset(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateClientSession() {
|
||||
UserSessionModel[] sessions = createSessions();
|
||||
|
||||
List<ClientSessionModel> clientSessions = session.sessions().getUserSession(realm, sessions[0].getId()).getClientSessions();
|
||||
Map<String, AuthenticatedClientSessionModel> clientSessions = session.sessions().getUserSession(realm, sessions[0].getId()).getAuthenticatedClientSessions();
|
||||
assertEquals(2, clientSessions.size());
|
||||
|
||||
String client1 = realm.getClientByClientId("test-app").getId();
|
||||
String clientUUID = realm.getClientByClientId("test-app").getId();
|
||||
|
||||
ClientSessionModel session1;
|
||||
|
||||
if (clientSessions.get(0).getClient().getId().equals(client1)) {
|
||||
session1 = clientSessions.get(0);
|
||||
} else {
|
||||
session1 = clientSessions.get(1);
|
||||
}
|
||||
AuthenticatedClientSessionModel session1 = clientSessions.get(clientUUID);
|
||||
|
||||
assertEquals(null, session1.getAction());
|
||||
assertEquals(realm.getClientByClientId("test-app").getClientId(), session1.getClient().getClientId());
|
||||
|
@ -140,21 +153,22 @@ public class UserSessionProviderTest {
|
|||
public void testUpdateClientSession() {
|
||||
UserSessionModel[] sessions = createSessions();
|
||||
|
||||
String id = sessions[0].getClientSessions().get(0).getId();
|
||||
String userSessionId = sessions[0].getId();
|
||||
String clientUUID = realm.getClientByClientId("test-app").getId();
|
||||
|
||||
ClientSessionModel clientSession = session.sessions().getClientSession(realm, id);
|
||||
AuthenticatedClientSessionModel clientSession = sessions[0].getAuthenticatedClientSessions().get(clientUUID);
|
||||
|
||||
int time = clientSession.getTimestamp();
|
||||
assertEquals(null, clientSession.getAction());
|
||||
|
||||
clientSession.setAction(ClientSessionModel.Action.CODE_TO_TOKEN.name());
|
||||
clientSession.setAction(AuthenticatedClientSessionModel.Action.CODE_TO_TOKEN.name());
|
||||
clientSession.setTimestamp(time + 10);
|
||||
|
||||
kc.stopSession(session, true);
|
||||
session = kc.startSession();
|
||||
|
||||
ClientSessionModel updated = session.sessions().getClientSession(realm, id);
|
||||
assertEquals(ClientSessionModel.Action.CODE_TO_TOKEN.name(), updated.getAction());
|
||||
AuthenticatedClientSessionModel updated = session.sessions().getUserSession(realm, userSessionId).getAuthenticatedClientSessions().get(clientUUID);
|
||||
assertEquals(AuthenticatedClientSessionModel.Action.CODE_TO_TOKEN.name(), updated.getAction());
|
||||
assertEquals(time + 10, updated.getTimestamp());
|
||||
}
|
||||
|
||||
|
@ -170,17 +184,12 @@ public class UserSessionProviderTest {
|
|||
public void testRemoveUserSessionsByUser() {
|
||||
UserSessionModel[] sessions = createSessions();
|
||||
|
||||
List<String> clientSessionsRemoved = new LinkedList<String>();
|
||||
List<String> clientSessionsKept = new LinkedList<String>();
|
||||
Map<String, Integer> clientSessionsKept = new HashMap<>();
|
||||
for (UserSessionModel s : sessions) {
|
||||
s = session.sessions().getUserSession(realm, s.getId());
|
||||
|
||||
for (ClientSessionModel c : s.getClientSessions()) {
|
||||
if (c.getUserSession().getUser().getUsername().equals("user1")) {
|
||||
clientSessionsRemoved.add(c.getId());
|
||||
} else {
|
||||
clientSessionsKept.add(c.getId());
|
||||
}
|
||||
if (!s.getUser().getUsername().equals("user1")) {
|
||||
clientSessionsKept.put(s.getId(), s.getAuthenticatedClientSessions().keySet().size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,13 +197,12 @@ public class UserSessionProviderTest {
|
|||
resetSession();
|
||||
|
||||
assertTrue(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user1", realm)).isEmpty());
|
||||
assertFalse(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm)).isEmpty());
|
||||
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm));
|
||||
assertFalse(userSessions.isEmpty());
|
||||
|
||||
for (String c : clientSessionsRemoved) {
|
||||
assertNull(session.sessions().getClientSession(realm, c));
|
||||
}
|
||||
for (String c : clientSessionsKept) {
|
||||
assertNotNull(session.sessions().getClientSession(realm, c));
|
||||
Assert.assertEquals(userSessions.size(), clientSessionsKept.size());
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
Assert.assertEquals((int) clientSessionsKept.get(userSession.getId()), userSession.getAuthenticatedClientSessions().size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,76 +210,47 @@ public class UserSessionProviderTest {
|
|||
public void testRemoveUserSession() {
|
||||
UserSessionModel userSession = createSessions()[0];
|
||||
|
||||
List<String> clientSessionsRemoved = new LinkedList<String>();
|
||||
for (ClientSessionModel c : userSession.getClientSessions()) {
|
||||
clientSessionsRemoved.add(c.getId());
|
||||
}
|
||||
|
||||
session.sessions().removeUserSession(realm, userSession);
|
||||
resetSession();
|
||||
|
||||
assertNull(session.sessions().getUserSession(realm, userSession.getId()));
|
||||
for (String c : clientSessionsRemoved) {
|
||||
assertNull(session.sessions().getClientSession(realm, c));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveUserSessionsByRealm() {
|
||||
UserSessionModel[] sessions = createSessions();
|
||||
|
||||
List<ClientSessionModel> clientSessions = new LinkedList<ClientSessionModel>();
|
||||
for (UserSessionModel s : sessions) {
|
||||
clientSessions.addAll(s.getClientSessions());
|
||||
}
|
||||
|
||||
session.sessions().removeUserSessions(realm);
|
||||
resetSession();
|
||||
|
||||
assertTrue(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user1", realm)).isEmpty());
|
||||
assertTrue(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm)).isEmpty());
|
||||
|
||||
for (ClientSessionModel c : clientSessions) {
|
||||
assertNull(session.sessions().getClientSession(realm, c.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClientRemoved() {
|
||||
UserSessionModel[] sessions = createSessions();
|
||||
|
||||
List<String> clientSessionsRemoved = new LinkedList<String>();
|
||||
List<String> clientSessionsKept = new LinkedList<String>();
|
||||
String thirdPartyClientUUID = realm.getClientByClientId("third-party").getId();
|
||||
|
||||
Map<String, Set<String>> clientSessionsKept = new HashMap<>();
|
||||
for (UserSessionModel s : sessions) {
|
||||
Set<String> clientUUIDS = new HashSet<>(s.getAuthenticatedClientSessions().keySet());
|
||||
clientUUIDS.remove(thirdPartyClientUUID); // This client will be later removed, hence his clientSessions too
|
||||
clientSessionsKept.put(s.getId(), clientUUIDS);
|
||||
}
|
||||
|
||||
realm.removeClient(thirdPartyClientUUID);
|
||||
resetSession();
|
||||
|
||||
for (UserSessionModel s : sessions) {
|
||||
s = session.sessions().getUserSession(realm, s.getId());
|
||||
for (ClientSessionModel c : s.getClientSessions()) {
|
||||
if (c.getClient().getClientId().equals("third-party")) {
|
||||
clientSessionsRemoved.add(c.getId());
|
||||
} else {
|
||||
clientSessionsKept.add(c.getId());
|
||||
}
|
||||
}
|
||||
Set<String> clientUUIDS = s.getAuthenticatedClientSessions().keySet();
|
||||
assertEquals(clientUUIDS, clientSessionsKept.get(s.getId()));
|
||||
}
|
||||
|
||||
session.sessions().onClientRemoved(realm, realm.getClientByClientId("third-party"));
|
||||
resetSession();
|
||||
|
||||
for (String c : clientSessionsRemoved) {
|
||||
assertNull(session.sessions().getClientSession(realm, c));
|
||||
}
|
||||
for (String c : clientSessionsKept) {
|
||||
assertNotNull(session.sessions().getClientSession(realm, c));
|
||||
}
|
||||
|
||||
session.sessions().onClientRemoved(realm, realm.getClientByClientId("test-app"));
|
||||
resetSession();
|
||||
|
||||
for (String c : clientSessionsRemoved) {
|
||||
assertNull(session.sessions().getClientSession(realm, c));
|
||||
}
|
||||
for (String c : clientSessionsKept) {
|
||||
assertNull(session.sessions().getClientSession(realm, c));
|
||||
}
|
||||
// Revert client
|
||||
realm.addClient("third-party");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -281,11 +260,12 @@ public class UserSessionProviderTest {
|
|||
|
||||
try {
|
||||
Set<String> expired = new HashSet<String>();
|
||||
Set<String> expiredClientSessions = new HashSet<String>();
|
||||
|
||||
Time.setOffset(-(realm.getSsoSessionMaxLifespan() + 1));
|
||||
expired.add(session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null).getId());
|
||||
expiredClientSessions.add(session.sessions().createClientSession(realm, client).getId());
|
||||
UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
|
||||
expired.add(userSession.getId());
|
||||
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
|
||||
Assert.assertEquals(userSession, clientSession.getUserSession());
|
||||
|
||||
Time.setOffset(0);
|
||||
UserSessionModel s = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.1", "form", true, null, null);
|
||||
|
@ -293,15 +273,12 @@ public class UserSessionProviderTest {
|
|||
s.setLastSessionRefresh(0);
|
||||
expired.add(s.getId());
|
||||
|
||||
ClientSessionModel clSession = session.sessions().createClientSession(realm, client);
|
||||
clSession.setUserSession(s);
|
||||
expiredClientSessions.add(clSession.getId());
|
||||
|
||||
Set<String> valid = new HashSet<String>();
|
||||
Set<String> validClientSessions = new HashSet<String>();
|
||||
|
||||
valid.add(session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null).getId());
|
||||
validClientSessions.add(session.sessions().createClientSession(realm, client).getId());
|
||||
userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
|
||||
valid.add(userSession.getId());
|
||||
validClientSessions.add(session.sessions().createClientSession(realm, client, userSession).getId());
|
||||
|
||||
resetSession();
|
||||
|
||||
|
@ -311,91 +288,18 @@ public class UserSessionProviderTest {
|
|||
for (String e : expired) {
|
||||
assertNull(session.sessions().getUserSession(realm, e));
|
||||
}
|
||||
for (String e : expiredClientSessions) {
|
||||
assertNull(session.sessions().getClientSession(realm, e));
|
||||
}
|
||||
|
||||
for (String v : valid) {
|
||||
assertNotNull(session.sessions().getUserSession(realm, v));
|
||||
}
|
||||
for (String e : validClientSessions) {
|
||||
assertNotNull(session.sessions().getClientSession(realm, e));
|
||||
UserSessionModel userSessionLoaded = session.sessions().getUserSession(realm, v);
|
||||
assertNotNull(userSessionLoaded);
|
||||
Assert.assertEquals(1, userSessionLoaded.getAuthenticatedClientSessions().size());
|
||||
Assert.assertNotNull(userSessionLoaded.getAuthenticatedClientSessions().get(client.getId()));
|
||||
}
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpireDetachedClientSessions() {
|
||||
try {
|
||||
realm.setAccessCodeLifespan(10);
|
||||
realm.setAccessCodeLifespanUserAction(10);
|
||||
realm.setAccessCodeLifespanLogin(30);
|
||||
|
||||
// Login lifespan is largest
|
||||
String clientSessionId = session.sessions().createClientSession(realm, realm.getClientByClientId("test-app")).getId();
|
||||
resetSession();
|
||||
|
||||
Time.setOffset(25);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNotNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
Time.setOffset(35);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
// User action is largest
|
||||
realm.setAccessCodeLifespanUserAction(40);
|
||||
|
||||
Time.setOffset(0);
|
||||
clientSessionId = session.sessions().createClientSession(realm, realm.getClientByClientId("test-app")).getId();
|
||||
resetSession();
|
||||
|
||||
Time.setOffset(35);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNotNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
Time.setOffset(45);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
// Access code is largest
|
||||
realm.setAccessCodeLifespan(50);
|
||||
|
||||
Time.setOffset(0);
|
||||
clientSessionId = session.sessions().createClientSession(realm, realm.getClientByClientId("test-app")).getId();
|
||||
resetSession();
|
||||
|
||||
Time.setOffset(45);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNotNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
Time.setOffset(55);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNull(session.sessions().getClientSession(clientSessionId));
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
|
||||
realm.setAccessCodeLifespan(60);
|
||||
realm.setAccessCodeLifespanUserAction(300);
|
||||
realm.setAccessCodeLifespanLogin(1800);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// KEYCLOAK-2508
|
||||
@Test
|
||||
public void testRemovingExpiredSession() {
|
||||
|
@ -429,12 +333,13 @@ public class UserSessionProviderTest {
|
|||
for (int i = 0; i < 25; i++) {
|
||||
Time.setOffset(i);
|
||||
UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0." + i, "form", false, null, null);
|
||||
ClientSessionModel clientSession = session.sessions().createClientSession(realm, realm.getClientByClientId("test-app"));
|
||||
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, realm.getClientByClientId("test-app"), userSession);
|
||||
clientSession.setUserSession(userSession);
|
||||
clientSession.setRedirectUri("http://redirect");
|
||||
clientSession.setRoles(new HashSet<String>());
|
||||
clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, "state");
|
||||
clientSession.setTimestamp(userSession.getStarted());
|
||||
userSession.setLastSessionRefresh(userSession.getStarted());
|
||||
}
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
|
@ -451,19 +356,21 @@ public class UserSessionProviderTest {
|
|||
|
||||
@Test
|
||||
public void testCreateAndGetInSameTransaction() {
|
||||
ClientModel client = realm.getClientByClientId("test-app");
|
||||
UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
|
||||
ClientSessionModel clientSession = createClientSession(realm.getClientByClientId("test-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
AuthenticatedClientSessionModel clientSession = createClientSession(client, userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
|
||||
|
||||
Assert.assertNotNull(session.sessions().getUserSession(realm, userSession.getId()));
|
||||
Assert.assertNotNull(session.sessions().getClientSession(realm, clientSession.getId()));
|
||||
UserSessionModel userSessionLoaded = session.sessions().getUserSession(realm, userSession.getId());
|
||||
AuthenticatedClientSessionModel clientSessionLoaded = userSessionLoaded.getAuthenticatedClientSessions().get(client.getId());
|
||||
Assert.assertNotNull(userSessionLoaded);
|
||||
Assert.assertNotNull(clientSessionLoaded);
|
||||
|
||||
Assert.assertEquals(userSession.getId(), clientSession.getUserSession().getId());
|
||||
Assert.assertEquals(1, userSession.getClientSessions().size());
|
||||
Assert.assertEquals(clientSession.getId(), userSession.getClientSessions().get(0).getId());
|
||||
Assert.assertEquals(userSession.getId(), clientSessionLoaded.getUserSession().getId());
|
||||
Assert.assertEquals(1, userSessionLoaded.getAuthenticatedClientSessions().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientLoginSessions() {
|
||||
public void testAuthenticatedClientSessions() {
|
||||
UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
|
||||
|
||||
ClientModel client1 = realm.getClientByClientId("test-app");
|
||||
|
@ -486,8 +393,8 @@ public class UserSessionProviderTest {
|
|||
userSession = session.sessions().getUserSession(realm, userSession.getId());
|
||||
Map<String, AuthenticatedClientSessionModel> clientSessions = userSession.getAuthenticatedClientSessions();
|
||||
Assert.assertEquals(2, clientSessions.size());
|
||||
testClientLoginSession(clientSessions.get(client1.getId()), "test-app", userSession.getId(), "foo1", 100);
|
||||
testClientLoginSession(clientSessions.get(client2.getId()), "third-party", userSession.getId(), "foo2", 200);
|
||||
testAuthenticatedClientSession(clientSessions.get(client1.getId()), "test-app", userSession.getId(), "foo1", 100);
|
||||
testAuthenticatedClientSession(clientSessions.get(client2.getId()), "third-party", userSession.getId(), "foo2", 200);
|
||||
|
||||
// Update session1
|
||||
clientSessions.get(client1.getId()).setAction("foo1-updated");
|
||||
|
@ -498,7 +405,7 @@ public class UserSessionProviderTest {
|
|||
// Ensure updated
|
||||
userSession = session.sessions().getUserSession(realm, userSession.getId());
|
||||
clientSessions = userSession.getAuthenticatedClientSessions();
|
||||
testClientLoginSession(clientSessions.get(client1.getId()), "test-app", userSession.getId(), "foo1-updated", 100);
|
||||
testAuthenticatedClientSession(clientSessions.get(client1.getId()), "test-app", userSession.getId(), "foo1-updated", 100);
|
||||
|
||||
// Rewrite session2
|
||||
clientSession2 = session.sessions().createClientSession(realm, client2, userSession);
|
||||
|
@ -512,8 +419,8 @@ public class UserSessionProviderTest {
|
|||
userSession = session.sessions().getUserSession(realm, userSession.getId());
|
||||
clientSessions = userSession.getAuthenticatedClientSessions();
|
||||
Assert.assertEquals(2, clientSessions.size());
|
||||
testClientLoginSession(clientSessions.get(client1.getId()), "test-app", userSession.getId(), "foo1-updated", 100);
|
||||
testClientLoginSession(clientSessions.get(client2.getId()), "third-party", userSession.getId(), "foo2-rewrited", 300);
|
||||
testAuthenticatedClientSession(clientSessions.get(client1.getId()), "test-app", userSession.getId(), "foo1-updated", 100);
|
||||
testAuthenticatedClientSession(clientSessions.get(client2.getId()), "third-party", userSession.getId(), "foo2-rewrited", 300);
|
||||
|
||||
// remove session
|
||||
clientSession1 = userSession.getAuthenticatedClientSessions().get(client1.getId());
|
||||
|
@ -549,11 +456,11 @@ public class UserSessionProviderTest {
|
|||
}
|
||||
|
||||
|
||||
private void testClientLoginSession(AuthenticatedClientSessionModel clientLoginSession, String expectedClientId, String expectedUserSessionId, String expectedAction, int expectedTimestamp) {
|
||||
Assert.assertEquals(expectedClientId, clientLoginSession.getClient().getClientId());
|
||||
Assert.assertEquals(expectedUserSessionId, clientLoginSession.getUserSession().getId());
|
||||
Assert.assertEquals(expectedAction, clientLoginSession.getAction());
|
||||
Assert.assertEquals(expectedTimestamp, clientLoginSession.getTimestamp());
|
||||
private void testAuthenticatedClientSession(AuthenticatedClientSessionModel clientSession, String expectedClientId, String expectedUserSessionId, String expectedAction, int expectedTimestamp) {
|
||||
Assert.assertEquals(expectedClientId, clientSession.getClient().getClientId());
|
||||
Assert.assertEquals(expectedUserSessionId, clientSession.getUserSession().getId());
|
||||
Assert.assertEquals(expectedAction, clientSession.getAction());
|
||||
Assert.assertEquals(expectedTimestamp, clientSession.getTimestamp());
|
||||
}
|
||||
|
||||
private void assertPaginatedSession(RealmModel realm, ClientModel client, int start, int max, int expectedSize) {
|
||||
|
@ -642,9 +549,8 @@ public class UserSessionProviderTest {
|
|||
assertNotNull(session.sessions().getUserLoginFailure(realm, "user2"));
|
||||
}
|
||||
|
||||
private ClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
|
||||
ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
|
||||
if (userSession != null) clientSession.setUserSession(userSession);
|
||||
private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
|
||||
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
|
||||
clientSession.setRedirectUri(redirect);
|
||||
if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
|
||||
if (roles != null) clientSession.setRoles(roles);
|
||||
|
@ -710,9 +616,14 @@ public class UserSessionProviderTest {
|
|||
assertTrue(session.getStarted() >= started - 1 && session.getStarted() <= started + 1);
|
||||
assertTrue(session.getLastSessionRefresh() >= lastRefresh - 1 && session.getLastSessionRefresh() <= lastRefresh + 1);
|
||||
|
||||
String[] actualClients = new String[session.getClientSessions().size()];
|
||||
for (int i = 0; i < actualClients.length; i++) {
|
||||
actualClients[i] = session.getClientSessions().get(i).getClient().getClientId();
|
||||
String[] actualClients = new String[session.getAuthenticatedClientSessions().size()];
|
||||
int i = 0;
|
||||
for (Map.Entry<String, AuthenticatedClientSessionModel> entry : session.getAuthenticatedClientSessions().entrySet()) {
|
||||
String clientUUID = entry.getKey();
|
||||
AuthenticatedClientSessionModel clientSession = entry.getValue();
|
||||
Assert.assertEquals(clientUUID, clientSession.getClient().getId());
|
||||
actualClients[i] = clientSession.getClient().getClientId();
|
||||
i++;
|
||||
}
|
||||
|
||||
Arrays.sort(clients);
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.pages;
|
||||
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class LoginExpiredPage extends AbstractPage {
|
||||
|
||||
@FindBy(id = "loginRestartLink")
|
||||
private WebElement loginRestartLink;
|
||||
|
||||
@FindBy(id = "loginContinueLink")
|
||||
private WebElement loginContinueLink;
|
||||
|
||||
|
||||
public void clickLoginRestartLink() {
|
||||
loginRestartLink.click();
|
||||
}
|
||||
|
||||
public void clickLoginContinueLink() {
|
||||
loginContinueLink.click();
|
||||
}
|
||||
|
||||
|
||||
public boolean isCurrent() {
|
||||
return driver.getTitle().equals("Page has expired");
|
||||
}
|
||||
|
||||
public void open() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -47,9 +47,9 @@ public abstract class AbstractOfflineCacheCommand extends AbstractCommand {
|
|||
}
|
||||
|
||||
protected String toString(UserSessionEntity userSession) {
|
||||
int clientSessionsSize = userSession.getClientSessions()==null ? 0 : userSession.getClientSessions().size();
|
||||
int clientSessionsSize = userSession.getAuthenticatedClientSessions()==null ? 0 : userSession.getAuthenticatedClientSessions().size();
|
||||
return "ID: " + userSession.getId() + ", realm: " + userSession.getRealm() + ", lastAccessTime: " + Time.toDate(userSession.getLastSessionRefresh()) +
|
||||
", clientSessions: " + clientSessionsSize;
|
||||
", authenticatedClientSessions: " + clientSessionsSize;
|
||||
}
|
||||
|
||||
protected abstract void doRunCacheCommand(KeycloakSession session, Cache<String, SessionEntity> cache);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue