KEYCLOAK-4626 AuthenticationSessions: start

This commit is contained in:
mposolda 2017-03-06 11:39:47 +01:00
parent f392e79ad7
commit 83b29c5080
114 changed files with 4441 additions and 3633 deletions

View file

@ -157,12 +157,12 @@ public class ClientSessionAdapter implements ClientSessionModel {
}
@Override
public String getAuthMethod() {
public String getProtocol() {
return entity.getAuthMethod();
}
@Override
public void setAuthMethod(String authMethod) {
public void setProtocol(String authMethod) {
entity.setAuthMethod(authMethod);
update();
}

View file

@ -0,0 +1,166 @@
/*
* Copyright 2017 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.keycloak.models.KeycloakTransaction;
import java.util.HashMap;
import java.util.Map;
import org.infinispan.Cache;
import org.jboss.logging.Logger;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class InfinispanKeycloakTransaction implements KeycloakTransaction {
private final static Logger log = Logger.getLogger(InfinispanKeycloakTransaction.class);
public enum CacheOperation {
ADD, REMOVE, REPLACE
}
private boolean active;
private boolean rollback;
private final Map<Object, CacheTask> tasks = new HashMap<>();
@Override
public void begin() {
active = true;
}
@Override
public void commit() {
if (rollback) {
throw new RuntimeException("Rollback only!");
}
tasks.values().forEach(CacheTask::execute);
}
@Override
public void rollback() {
tasks.clear();
}
@Override
public void setRollbackOnly() {
rollback = true;
}
@Override
public boolean getRollbackOnly() {
return rollback;
}
@Override
public boolean isActive() {
return active;
}
public <K, V> void put(Cache<K, V> cache, K key, V value) {
log.tracev("Adding cache operation: {0} on {1}", CacheOperation.ADD, key);
Object taskKey = getTaskKey(cache, key);
if (tasks.containsKey(taskKey)) {
throw new IllegalStateException("Can't add session: task in progress for session");
} else {
tasks.put(taskKey, new CacheTask(cache, CacheOperation.ADD, key, value));
}
}
public <K, V> void replace(Cache<K, V> cache, K key, V value) {
log.tracev("Adding cache operation: {0} on {1}", CacheOperation.REPLACE, key);
Object taskKey = getTaskKey(cache, key);
CacheTask current = tasks.get(taskKey);
if (current != null) {
switch (current.operation) {
case ADD:
case REPLACE:
current.value = value;
return;
case REMOVE:
return;
}
} else {
tasks.put(taskKey, new CacheTask(cache, CacheOperation.REPLACE, key, value));
}
}
public <K, V> void remove(Cache<K, V> cache, K key) {
log.tracev("Adding cache operation: {0} on {1}", CacheOperation.REMOVE, key);
Object taskKey = getTaskKey(cache, key);
tasks.put(taskKey, new CacheTask(cache, CacheOperation.REMOVE, key, null));
}
// This is for possibility to lookup for session by id, which was created in this transaction
public <K, V> V get(Cache<K, V> cache, K key) {
Object taskKey = getTaskKey(cache, key);
CacheTask<K, V> current = tasks.get(taskKey);
if (current != null) {
switch (current.operation) {
case ADD:
case REPLACE:
return current.value;
}
}
return null;
}
private static <K, V> Object getTaskKey(Cache<K, V> cache, K key) {
if (key instanceof String) {
return new StringBuilder(cache.getName())
.append("::")
.append(key).toString();
} else {
return key;
}
}
public static class CacheTask<K, V> {
private final Cache<K, V> cache;
private final CacheOperation operation;
private final K key;
private V value;
public CacheTask(Cache<K, V> cache, CacheOperation operation, K key, V value) {
this.cache = cache;
this.operation = operation;
this.key = key;
this.value = value;
}
public void execute() {
log.tracev("Executing cache operation: {0} on {1}", operation, key);
switch (operation) {
case ADD:
cache.put(key, value);
break;
case REMOVE:
cache.remove(key);
break;
case REPLACE:
cache.replace(key, value);
break;
}
}
}
}

View file

@ -23,6 +23,7 @@ import org.infinispan.context.Flag;
import org.jboss.logging.Logger;
import org.keycloak.common.util.Time;
import org.keycloak.models.ClientInitialAccessModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
@ -89,6 +90,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
return offline ? offlineSessionCache : sessionCache;
}
/*
@Override
public ClientSessionModel createClientSession(RealmModel realm, ClientModel client) {
String id = KeycloakModelUtils.generateId();
@ -104,6 +106,12 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
ClientSessionAdapter wrap = wrap(realm, entity, false);
return wrap;
}*/
// TODO:mposolda
@Override
public ClientLoginSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession) {
return null;
}
@Override
@ -608,6 +616,8 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
}
// TODO:mposolda
/*
@Override
public ClientSessionModel createOfflineClientSession(ClientSessionModel clientSession) {
ClientSessionAdapter offlineClientSession = importClientSession(clientSession, true);
@ -616,6 +626,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
offlineClientSession.setTimestamp(Time.currentTime());
return offlineClientSession;
}*/
@Override
public ClientLoginSessionModel createOfflineClientSession(ClientLoginSessionModel clientSession) {
return null;
}
@Override
@ -624,27 +639,17 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
@Override
public List<ClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user) {
public List<UserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user) {
Iterator<Map.Entry<String, SessionEntity>> itr = offlineSessionCache.entrySet().stream().filter(UserSessionPredicate.create(realm.getId()).user(user.getId())).iterator();
List<ClientSessionModel> clientSessions = new LinkedList<>();
List<UserSessionModel> userSessions = new LinkedList<>();
while(itr.hasNext()) {
UserSessionEntity entity = (UserSessionEntity) itr.next().getValue();
Set<String> currClientSessions = entity.getClientSessions();
if (currClientSessions == null) {
continue;
}
for (String clientSessionId : currClientSessions) {
ClientSessionEntity cls = (ClientSessionEntity) offlineSessionCache.get(clientSessionId);
if (cls != null) {
clientSessions.add(wrap(realm, cls, true));
}
}
UserSessionModel userSession = wrap(realm, entity, true);
userSessions.add(userSession);
}
return clientSessions;
return userSessions;
}
@Override
@ -695,7 +700,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
entity.setAction(clientSession.getAction());
entity.setAuthenticatorStatus(clientSession.getExecutionStatus());
entity.setAuthMethod(clientSession.getAuthMethod());
entity.setAuthMethod(clientSession.getProtocol());
if (clientSession.getAuthenticatedUser() != null) {
entity.setAuthUserId(clientSession.getAuthenticatedUser().getId());
}
@ -757,145 +762,4 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
return list;
}
class InfinispanKeycloakTransaction implements KeycloakTransaction {
private boolean active;
private boolean rollback;
private Map<Object, CacheTask> tasks = new HashMap<>();
@Override
public void begin() {
active = true;
}
@Override
public void commit() {
if (rollback) {
throw new RuntimeException("Rollback only!");
}
for (CacheTask task : tasks.values()) {
task.execute();
}
}
@Override
public void rollback() {
tasks.clear();
}
@Override
public void setRollbackOnly() {
rollback = true;
}
@Override
public boolean getRollbackOnly() {
return rollback;
}
@Override
public boolean isActive() {
return active;
}
public void put(Cache cache, Object key, Object value) {
log.tracev("Adding cache operation: {0} on {1}", CacheOperation.ADD, key);
Object taskKey = getTaskKey(cache, key);
if (tasks.containsKey(taskKey)) {
throw new IllegalStateException("Can't add session: task in progress for session");
} else {
tasks.put(taskKey, new CacheTask(cache, CacheOperation.ADD, key, value));
}
}
public void replace(Cache cache, Object key, Object value) {
log.tracev("Adding cache operation: {0} on {1}", CacheOperation.REPLACE, key);
Object taskKey = getTaskKey(cache, key);
CacheTask current = tasks.get(taskKey);
if (current != null) {
switch (current.operation) {
case ADD:
case REPLACE:
current.value = value;
return;
case REMOVE:
return;
}
} else {
tasks.put(taskKey, new CacheTask(cache, CacheOperation.REPLACE, key, value));
}
}
public void remove(Cache cache, Object key) {
log.tracev("Adding cache operation: {0} on {1}", CacheOperation.REMOVE, key);
Object taskKey = getTaskKey(cache, key);
tasks.put(taskKey, new CacheTask(cache, CacheOperation.REMOVE, key, null));
}
// This is for possibility to lookup for session by id, which was created in this transaction
public Object get(Cache cache, Object key) {
Object taskKey = getTaskKey(cache, key);
CacheTask current = tasks.get(taskKey);
if (current != null) {
switch (current.operation) {
case ADD:
case REPLACE:
return current.value; }
}
return null;
}
private Object getTaskKey(Cache cache, Object key) {
if (key instanceof String) {
return new StringBuilder(cache.getName())
.append("::")
.append(key.toString()).toString();
} else {
// loginFailure cache
return key;
}
}
public class CacheTask {
private Cache cache;
private CacheOperation operation;
private Object key;
private Object value;
public CacheTask(Cache cache, CacheOperation operation, Object key, Object value) {
this.cache = cache;
this.operation = operation;
this.key = key;
this.value = value;
}
public void execute() {
log.tracev("Executing cache operation: {0} on {1}", operation, key);
switch (operation) {
case ADD:
cache.put(key, value);
break;
case REMOVE:
cache.remove(key);
break;
case REPLACE:
cache.replace(key, value);
break;
}
}
}
}
public enum CacheOperation {
ADD, REMOVE, REPLACE
}
}

View file

@ -18,6 +18,7 @@
package org.keycloak.models.sessions.infinispan;
import org.infinispan.Cache;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@ -60,6 +61,12 @@ public class UserSessionAdapter implements UserSessionModel {
this.offline = offline;
}
// TODO;mposolda
@Override
public Map<String, ClientLoginSessionModel> getClientLoginSessions() {
return null;
}
public String getId() {
return entity.getId();
}

View file

@ -18,6 +18,7 @@
package org.keycloak.models.sessions.infinispan.entities;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.sessions.LoginSessionModel;
import java.util.HashMap;
import java.util.HashSet;

View file

@ -0,0 +1,69 @@
/*
* 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.sessions.infinispan;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.sessions.LoginSessionModel;
import org.keycloak.sessions.LoginSessionProvider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class InfinispanLoginSessionProvider implements LoginSessionProvider {
@Override
public LoginSessionModel createLoginSession(RealmModel realm, ClientModel client, boolean browser) {
return null;
}
@Override
public LoginSessionModel getCurrentLoginSession(RealmModel realm) {
return null;
}
@Override
public LoginSessionModel getLoginSession(RealmModel realm, String loginSessionId) {
return null;
}
@Override
public void removeLoginSession(RealmModel realm, LoginSessionModel loginSession) {
}
@Override
public void removeExpired(RealmModel realm) {
}
@Override
public void onRealmRemoved(RealmModel realm) {
}
@Override
public void onClientRemoved(RealmModel realm, ClientModel client) {
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,270 @@
/*
* 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.sessions.infinispan;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.infinispan.Cache;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.sessions.LoginSessionModel;
/**
* NOTE: Calling setter doesn't automatically enlist for update
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LoginSessionAdapter implements LoginSessionModel {
private KeycloakSession session;
private InfinispanLoginSessionProvider provider;
private Cache<String, SessionEntity> cache;
private RealmModel realm;
private LoginSessionEntity entity;
public LoginSessionAdapter(KeycloakSession session, InfinispanLoginSessionProvider provider, Cache<String, SessionEntity> cache, RealmModel realm,
LoginSessionEntity entity) {
this.session = session;
this.provider = provider;
this.cache = cache;
this.realm = realm;
this.entity = entity;
}
@Override
public String getId() {
return entity.getId();
}
@Override
public RealmModel getRealm() {
return realm;
}
@Override
public ClientModel getClient() {
return realm.getClientById(entity.getClientUuid());
}
// @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());
// }
// }
@Override
public String getRedirectUri() {
return entity.getRedirectUri();
}
@Override
public void setRedirectUri(String uri) {
entity.setRedirectUri(uri);
}
@Override
public int getTimestamp() {
return entity.getTimestamp();
}
@Override
public void setTimestamp(int timestamp) {
entity.setTimestamp(timestamp);
}
@Override
public String getAction() {
return entity.getAction();
}
@Override
public void setAction(String action) {
entity.setAction(action);
}
@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);
}
@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);
}
@Override
public String getProtocol() {
return entity.getProtocol();
}
@Override
public void setProtocol(String protocol) {
entity.setProtocol(protocol);
}
@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);
}
@Override
public void removeNote(String name) {
if (entity.getNotes() != null) {
entity.getNotes().remove(name);
}
}
@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);
}
@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>());
}
@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);
}
@Override
public void removeRequiredAction(String action) {
entity.getRequiredActions().remove(action);
}
@Override
public void addRequiredAction(UserModel.RequiredAction action) {
addRequiredAction(action.name());
}
@Override
public void removeRequiredAction(UserModel.RequiredAction action) {
removeRequiredAction(action.name());
}
@Override
public Map<String, LoginSessionModel.ExecutionStatus> getExecutionStatus() {
return entity.getExecutionStatus();
}
@Override
public void setExecutionStatus(String authenticator, LoginSessionModel.ExecutionStatus status) {
entity.getExecutionStatus().put(authenticator, status);
}
@Override
public void clearExecutionStatus() {
entity.getExecutionStatus().clear();
}
@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());
}
}

View file

@ -0,0 +1,142 @@
/*
* 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.sessions.infinispan;
import java.util.Map;
import java.util.Set;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.sessions.LoginSessionModel;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LoginSessionEntity extends SessionEntity {
private String clientUuid;
private String authUserId;
private String redirectUri;
private int timestamp;
private String action;
private Set<String> roles;
private Set<String> protocolMappers;
private Map<String, LoginSessionModel.ExecutionStatus> executionStatus;
private String protocol;
private Map<String, String> notes;
private Set<String> requiredActions;
private Map<String, String> userSessionNotes;
public String getClientUuid() {
return clientUuid;
}
public void setClientUuid(String clientUuid) {
this.clientUuid = clientUuid;
}
public String getAuthUserId() {
return authUserId;
}
public void setAuthUserId(String authUserId) {
this.authUserId = authUserId;
}
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, LoginSessionModel.ExecutionStatus> getExecutionStatus() {
return executionStatus;
}
public void setExecutionStatus(Map<String, LoginSessionModel.ExecutionStatus> executionStatus) {
this.executionStatus = executionStatus;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public Map<String, String> getNotes() {
return notes;
}
public void setNotes(Map<String, String> notes) {
this.notes = notes;
}
public Set<String> getRequiredActions() {
return requiredActions;
}
public void setRequiredActions(Set<String> requiredActions) {
this.requiredActions = requiredActions;
}
public Map<String, String> getUserSessionNotes() {
return userSessionNotes;
}
public void setUserSessionNotes(Map<String, String> userSessionNotes) {
this.userSessionNotes = userSessionNotes;
}
}

View file

@ -17,6 +17,7 @@
package org.keycloak.models.jpa.session;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
@ -68,7 +69,7 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
}
@Override
public void createClientSession(ClientSessionModel clientSession, boolean offline) {
public void createClientSession(UserSessionModel userSession, ClientLoginSessionModel clientSession, boolean offline) {
PersistentClientSessionAdapter adapter = new PersistentClientSessionAdapter(clientSession);
PersistentClientSessionModel model = adapter.getUpdatedModel();
@ -217,6 +218,8 @@ 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);
@ -242,6 +245,7 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
}
}
}
*/
return result;
}

View file

@ -22,6 +22,7 @@ import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.sessions.LoginSessionModel;
import java.net.URI;
@ -62,7 +63,7 @@ public interface AuthenticationFlowContext extends AbstractAuthenticationFlowCon
*
* @return
*/
ClientSessionModel getClientSession();
LoginSessionModel getLoginSession();
/**
* Create a Freemarker form builder that presets the user, action URI, and a generated access code

View file

@ -26,6 +26,7 @@ import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.sessions.LoginSessionModel;
import javax.ws.rs.core.UriInfo;
@ -79,11 +80,11 @@ public interface FormContext {
RealmModel getRealm();
/**
* ClientSessionModel attached to this flow
* LoginSessionModel attached to this flow
*
* @return
*/
ClientSessionModel getClientSession();
LoginSessionModel getLoginSession();
/**
* Information about the IP address from the connecting HTTP client.

View file

@ -26,6 +26,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.sessions.LoginSessionModel;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
@ -90,8 +91,7 @@ public interface RequiredActionContext {
*/
UserModel getUser();
RealmModel getRealm();
ClientSessionModel getClientSession();
UserSessionModel getUserSession();
LoginSessionModel getLoginSession();
ClientConnection getConnection();
UriInfo getUriInfo();
KeycloakSession getSession();

View file

@ -19,6 +19,7 @@ package org.keycloak.broker.provider;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.Constants;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.sessions.LoginSessionModel;
import java.util.ArrayList;
import java.util.HashMap;
@ -46,7 +47,7 @@ public class BrokeredIdentityContext {
private IdentityProviderModel idpConfig;
private IdentityProvider idp;
private Map<String, Object> contextData = new HashMap<>();
private ClientSessionModel clientSession;
private LoginSessionModel loginSession;
public BrokeredIdentityContext(String id) {
if (id == null) {
@ -190,12 +191,12 @@ public class BrokeredIdentityContext {
this.lastName = lastName;
}
public ClientSessionModel getClientSession() {
return clientSession;
public LoginSessionModel getLoginSession() {
return loginSession;
}
public void setClientSession(ClientSessionModel clientSession) {
this.clientSession = clientSession;
public void setLoginSession(LoginSessionModel loginSession) {
this.loginSession = loginSession;
}
public void setName(String name) {

View file

@ -17,12 +17,12 @@
package org.keycloak.forms.login;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.provider.Provider;
import org.keycloak.sessions.LoginSessionModel;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
@ -70,13 +70,13 @@ public interface LoginFormsProvider extends Provider {
public Response createErrorPage();
public Response createOAuthGrant(ClientSessionModel clientSessionModel);
public Response createOAuthGrant();
public Response createCode();
public LoginFormsProvider setClientSessionCode(String accessCode);
public LoginFormsProvider setClientSession(ClientSessionModel clientSession);
public LoginFormsProvider setLoginSession(LoginSessionModel loginSession);
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String,RoleModel> resourceRolesRequested, List<ProtocolMapperModel> protocolMappers);
public LoginFormsProvider setAccessRequest(String message);

View file

@ -18,6 +18,7 @@
package org.keycloak.models.session;
import org.keycloak.Config;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
@ -70,7 +71,7 @@ public class DisabledUserSessionPersisterProvider implements UserSessionPersiste
}
@Override
public void createClientSession(ClientSessionModel clientSession, boolean offline) {
public void createClientSession(UserSessionModel userSession, ClientLoginSessionModel clientSession, boolean offline) {
}

View file

@ -18,6 +18,7 @@
package org.keycloak.models.session;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ModelException;
@ -36,7 +37,7 @@ import java.util.Set;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class PersistentClientSessionAdapter implements ClientSessionModel {
public class PersistentClientSessionAdapter implements ClientLoginSessionModel {
private final PersistentClientSessionModel model;
private final RealmModel realm;
@ -45,22 +46,20 @@ public class PersistentClientSessionAdapter implements ClientSessionModel {
private PersistentClientSessionData data;
public PersistentClientSessionAdapter(ClientSessionModel clientSession) {
public PersistentClientSessionAdapter(ClientLoginSessionModel clientSession) {
data = new PersistentClientSessionData();
data.setAction(clientSession.getAction());
data.setAuthMethod(clientSession.getAuthMethod());
data.setExecutionStatus(clientSession.getExecutionStatus());
data.setAuthMethod(clientSession.getProtocol());
data.setNotes(clientSession.getNotes());
data.setProtocolMappers(clientSession.getProtocolMappers());
data.setRedirectUri(clientSession.getRedirectUri());
data.setRoles(clientSession.getRoles());
data.setUserSessionNotes(clientSession.getUserSessionNotes());
model = new PersistentClientSessionModel();
model.setClientId(clientSession.getClient().getId());
model.setClientSessionId(clientSession.getId());
if (clientSession.getAuthenticatedUser() != null) {
model.setUserId(clientSession.getAuthenticatedUser().getId());
if (clientSession.getUserSession() != null) {
model.setUserId(clientSession.getUserSession().getUser().getId());
}
model.setUserSessionId(clientSession.getUserSession().getId());
model.setTimestamp(clientSession.getTimestamp());
@ -178,37 +177,12 @@ public class PersistentClientSessionAdapter implements ClientSessionModel {
}
@Override
public Map<String, ExecutionStatus> getExecutionStatus() {
return getData().getExecutionStatus();
}
@Override
public void setExecutionStatus(String authenticator, ExecutionStatus status) {
getData().getExecutionStatus().put(authenticator, status);
}
@Override
public void clearExecutionStatus() {
getData().getExecutionStatus().clear();
}
@Override
public UserModel getAuthenticatedUser() {
return userSession.getUser();
}
@Override
public void setAuthenticatedUser(UserModel user) {
throw new IllegalStateException("Not supported setAuthenticatedUser");
}
@Override
public String getAuthMethod() {
public String getProtocol() {
return getData().getAuthMethod();
}
@Override
public void setAuthMethod(String method) {
public void setProtocol(String method) {
getData().setAuthMethod(method);
}
@ -242,52 +216,6 @@ public class PersistentClientSessionAdapter implements ClientSessionModel {
return entity.getNotes();
}
@Override
public Set<String> getRequiredActions() {
return getData().getRequiredActions();
}
@Override
public void addRequiredAction(String action) {
getData().getRequiredActions().add(action);
}
@Override
public void removeRequiredAction(String action) {
getData().getRequiredActions().remove(action);
}
@Override
public void addRequiredAction(UserModel.RequiredAction action) {
addRequiredAction(action.name());
}
@Override
public void removeRequiredAction(UserModel.RequiredAction action) {
removeRequiredAction(action.name());
}
@Override
public void setUserSessionNote(String name, String value) {
PersistentClientSessionData entity = getData();
if (entity.getUserSessionNotes() == null) {
entity.setUserSessionNotes(new HashMap<String, String>());
}
entity.getUserSessionNotes().put(name, value);
}
@Override
public Map<String, String> getUserSessionNotes() {
PersistentClientSessionData entity = getData();
if (entity.getUserSessionNotes() == null || entity.getUserSessionNotes().isEmpty()) return Collections.emptyMap();
return entity.getUserSessionNotes();
}
@Override
public void clearUserSessionNotes() {
PersistentClientSessionData entity = getData();
entity.setUserSessionNotes(new HashMap<String, String>());
}
@Override
public boolean equals(Object o) {
@ -320,18 +248,9 @@ public class PersistentClientSessionAdapter implements ClientSessionModel {
@JsonProperty("notes")
private Map<String, String> notes;
@JsonProperty("userSessionNotes")
private Map<String, String> userSessionNotes;
@JsonProperty("executionStatus")
private Map<String, ClientSessionModel.ExecutionStatus> executionStatus = new HashMap<>();
@JsonProperty("action")
private String action;
@JsonProperty("requiredActions")
private Set<String> requiredActions = new HashSet<>();
public String getAuthMethod() {
return authMethod;
}
@ -372,22 +291,6 @@ public class PersistentClientSessionAdapter implements ClientSessionModel {
this.notes = notes;
}
public Map<String, String> getUserSessionNotes() {
return userSessionNotes;
}
public void setUserSessionNotes(Map<String, String> userSessionNotes) {
this.userSessionNotes = userSessionNotes;
}
public Map<String, ClientSessionModel.ExecutionStatus> getExecutionStatus() {
return executionStatus;
}
public void setExecutionStatus(Map<String, ClientSessionModel.ExecutionStatus> executionStatus) {
this.executionStatus = executionStatus;
}
public String getAction() {
return action;
}
@ -396,12 +299,5 @@ public class PersistentClientSessionAdapter implements ClientSessionModel {
this.action = action;
}
public Set<String> getRequiredActions() {
return requiredActions;
}
public void setRequiredActions(Set<String> requiredActions) {
this.requiredActions = requiredActions;
}
}
}

View file

@ -18,6 +18,7 @@
package org.keycloak.models.session;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
@ -159,6 +160,12 @@ public class PersistentUserSessionAdapter implements UserSessionModel {
return clientSessions;
}
// TODO:mposolda
@Override
public Map<String, ClientLoginSessionModel> getClientLoginSessions() {
return null;
}
@Override
public String getNote(String name) {
return getData().getNotes()==null ? null : getData().getNotes().get(name);

View file

@ -17,8 +17,8 @@
package org.keycloak.models.session;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
@ -35,7 +35,7 @@ public interface UserSessionPersisterProvider extends Provider {
void createUserSession(UserSessionModel userSession, boolean offline);
// Assuming that corresponding userSession is already persisted
void createClientSession(ClientSessionModel clientSession, boolean offline);
void createClientSession(UserSessionModel userSession, ClientLoginSessionModel clientSession, boolean offline);
void updateUserSession(UserSessionModel userSession, boolean offline);

View file

@ -45,6 +45,7 @@ import org.keycloak.events.admin.AuthDetails;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientTemplateModel;
@ -485,7 +486,7 @@ public class ModelToRepresentation {
rep.setUsername(session.getUser().getUsername());
rep.setUserId(session.getUser().getId());
rep.setIpAddress(session.getIpAddress());
for (ClientSessionModel clientSession : session.getClientSessions()) {
for (ClientLoginSessionModel clientSession : session.getClientLoginSessions().values()) {
ClientModel client = clientSession.getClient();
rep.getClients().put(client.getId(), client.getClientId());
}

View file

@ -18,12 +18,14 @@
package org.keycloak.protocol;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.provider.Provider;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.sessions.LoginSessionModel;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
@ -66,19 +68,19 @@ public interface LoginProtocol extends Provider {
LoginProtocol setEventBuilder(EventBuilder event);
Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode);
Response authenticated(UserSessionModel userSession, ClientSessionCode<ClientLoginSessionModel> accessCode);
Response sendError(ClientSessionModel clientSession, Error error);
Response sendError(LoginSessionModel loginSession, Error error);
void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession);
Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession);
void backchannelLogout(UserSessionModel userSession, ClientLoginSessionModel clientSession);
Response frontchannelLogout(UserSessionModel userSession, ClientLoginSessionModel clientSession);
Response finishLogout(UserSessionModel userSession);
/**
* @param userSession
* @param clientSession
* @param loginSession
* @return true if SSO cookie authentication can't be used. User will need to "actively" reauthenticate
*/
boolean requireReauthentication(UserSessionModel userSession, ClientSessionModel clientSession);
boolean requireReauthentication(UserSessionModel userSession, LoginSessionModel loginSession);
}

View file

@ -21,7 +21,6 @@ import org.jboss.logging.Logger;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.Time;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
@ -29,6 +28,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.OAuth2Constants;
import org.keycloak.sessions.CommonClientSessionModel;
import java.security.MessageDigest;
import java.util.HashSet;
@ -38,7 +38,7 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ClientSessionCode {
public class ClientSessionCode<CLIENT_SESSION extends CommonClientSessionModel> {
private static final String ACTIVE_CODE = "active_code";
@ -48,7 +48,7 @@ public class ClientSessionCode {
private KeycloakSession session;
private final RealmModel realm;
private final ClientSessionModel clientSession;
private final CLIENT_SESSION commonLoginSession;
public enum ActionType {
CLIENT,
@ -56,45 +56,45 @@ public class ClientSessionCode {
USER
}
public ClientSessionCode(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession) {
public ClientSessionCode(KeycloakSession session, RealmModel realm, CLIENT_SESSION commonLoginSession) {
this.session = session;
this.realm = realm;
this.clientSession = clientSession;
this.commonLoginSession = commonLoginSession;
}
public static class ParseResult {
ClientSessionCode code;
boolean clientSessionNotFound;
public static class ParseResult<CLIENT_SESSION extends CommonClientSessionModel> {
ClientSessionCode<CLIENT_SESSION> code;
boolean loginSessionNotFound;
boolean illegalHash;
ClientSessionModel clientSession;
CLIENT_SESSION clientSession;
public ClientSessionCode getCode() {
public ClientSessionCode<CLIENT_SESSION> getCode() {
return code;
}
public boolean isClientSessionNotFound() {
return clientSessionNotFound;
public boolean isLoginSessionNotFound() {
return loginSessionNotFound;
}
public boolean isIllegalHash() {
return illegalHash;
}
public ClientSessionModel getClientSession() {
public CLIENT_SESSION getClientSession() {
return clientSession;
}
}
public static ParseResult parseResult(String code, KeycloakSession session, RealmModel realm) {
ParseResult result = new ParseResult();
public static <CLIENT_SESSION extends CommonClientSessionModel> ParseResult<CLIENT_SESSION> parseResult(String code, KeycloakSession session, RealmModel realm, Class<CLIENT_SESSION> sessionClass) {
ParseResult<CLIENT_SESSION> result = new ParseResult<>();
if (code == null) {
result.illegalHash = true;
return result;
}
try {
result.clientSession = getClientSession(code, session, realm);
result.clientSession = getClientSession(code, session, realm, sessionClass);
if (result.clientSession == null) {
result.clientSessionNotFound = true;
result.loginSessionNotFound = true;
return result;
}
@ -103,7 +103,7 @@ public class ClientSessionCode {
return result;
}
result.code = new ClientSessionCode(session, realm, result.clientSession);
result.code = new ClientSessionCode<CLIENT_SESSION>(session, realm, result.clientSession);
return result;
} catch (RuntimeException e) {
result.illegalHash = true;
@ -111,9 +111,9 @@ public class ClientSessionCode {
}
}
public static ClientSessionCode parse(String code, KeycloakSession session, RealmModel realm) {
public static <CLIENT_SESSION extends CommonClientSessionModel> ClientSessionCode<CLIENT_SESSION> parse(String code, KeycloakSession session, RealmModel realm, Class<CLIENT_SESSION> sessionClass) {
try {
ClientSessionModel clientSession = getClientSession(code, session, realm);
CLIENT_SESSION clientSession = getClientSession(code, session, realm, sessionClass);
if (clientSession == null) {
return null;
}
@ -122,24 +122,18 @@ public class ClientSessionCode {
return null;
}
return new ClientSessionCode(session, realm, clientSession);
return new ClientSessionCode<>(session, realm, clientSession);
} catch (RuntimeException e) {
return null;
}
}
public static ClientSessionModel getClientSession(String code, KeycloakSession session, RealmModel realm) {
try {
String[] parts = code.split("\\.");
String id = parts[1];
return session.sessions().getClientSession(realm, id);
} catch (ArrayIndexOutOfBoundsException e) {
return null;
}
public static <CLIENT_SESSION extends CommonClientSessionModel> CLIENT_SESSION getClientSession(String code, KeycloakSession session, RealmModel realm, Class<CLIENT_SESSION> sessionClass) {
return CodeGenerateUtil.parseSession(code, session, realm, sessionClass);
}
public ClientSessionModel getClientSession() {
return clientSession;
public CLIENT_SESSION getClientSession() {
return commonLoginSession;
}
public boolean isValid(String requestedAction, ActionType actionType) {
@ -148,7 +142,7 @@ public class ClientSessionCode {
}
public boolean isActionActive(ActionType actionType) {
int timestamp = clientSession.getTimestamp();
int timestamp = commonLoginSession.getTimestamp();
int lifespan;
switch (actionType) {
@ -169,7 +163,7 @@ public class ClientSessionCode {
}
public boolean isValidAction(String requestedAction) {
String action = clientSession.getAction();
String action = commonLoginSession.getAction();
if (action == null) {
return false;
}
@ -182,7 +176,7 @@ public class ClientSessionCode {
public Set<RoleModel> getRequestedRoles() {
Set<RoleModel> requestedRoles = new HashSet<>();
for (String roleId : clientSession.getRoles()) {
for (String roleId : commonLoginSession.getRoles()) {
RoleModel role = realm.getRoleById(roleId);
if (role != null) {
requestedRoles.add(role);
@ -192,9 +186,11 @@ public class ClientSessionCode {
}
public Set<ProtocolMapperModel> getRequestedProtocolMappers() {
return getRequestedProtocolMappers(commonLoginSession.getProtocolMappers(), commonLoginSession.getClient());
}
public static Set<ProtocolMapperModel> getRequestedProtocolMappers(Set<String> protocolMappers, ClientModel client) {
Set<ProtocolMapperModel> requestedProtocolMappers = new HashSet<>();
Set<String> protocolMappers = clientSession.getProtocolMappers();
ClientModel client = clientSession.getClient();
ClientTemplateModel template = client.getClientTemplate();
if (protocolMappers != null) {
for (String protocolMapperId : protocolMappers) {
@ -211,33 +207,33 @@ public class ClientSessionCode {
}
public void setAction(String action) {
clientSession.setAction(action);
clientSession.setTimestamp(Time.currentTime());
commonLoginSession.setAction(action);
commonLoginSession.setTimestamp(Time.currentTime());
}
public String getCode() {
String nextCode = (String) session.getAttribute(NEXT_CODE + "." + clientSession.getId());
String nextCode = (String) session.getAttribute(NEXT_CODE + "." + commonLoginSession.getId());
if (nextCode == null) {
nextCode = generateCode(clientSession);
session.setAttribute(NEXT_CODE + "." + clientSession.getId(), nextCode);
nextCode = generateCode(commonLoginSession);
session.setAttribute(NEXT_CODE + "." + commonLoginSession.getId(), nextCode);
} else {
logger.debug("Code already generated for session, using code from session attributes");
}
return nextCode;
}
private static String generateCode(ClientSessionModel clientSession) {
private static String generateCode(CommonClientSessionModel loginSession) {
try {
String actionId = Base64Url.encode(KeycloakModelUtils.generateSecret());
StringBuilder sb = new StringBuilder();
sb.append(actionId);
sb.append('.');
sb.append(clientSession.getId());
sb.append(loginSession.getId());
// https://tools.ietf.org/html/rfc7636#section-4
String codeChallenge = clientSession.getNote(OAuth2Constants.CODE_CHALLENGE);
String codeChallengeMethod = clientSession.getNote(OAuth2Constants.CODE_CHALLENGE_METHOD);
String codeChallenge = loginSession.getNote(OAuth2Constants.CODE_CHALLENGE);
String codeChallengeMethod = loginSession.getNote(OAuth2Constants.CODE_CHALLENGE_METHOD);
if (codeChallenge != null) {
logger.debugf("PKCE received codeChallenge = %s", codeChallenge);
if (codeChallengeMethod == null) {
@ -248,9 +244,9 @@ public class ClientSessionCode {
}
}
String code = sb.toString();
String code = CodeGenerateUtil.generateCode(loginSession, actionId);
clientSession.setNote(ACTIVE_CODE, code);
loginSession.setNote(ACTIVE_CODE, code);
return code;
} catch (Exception e) {
@ -258,15 +254,15 @@ public class ClientSessionCode {
}
}
private static boolean verifyCode(String code, ClientSessionModel clientSession) {
private static boolean verifyCode(String code, CommonClientSessionModel loginSession) {
try {
String activeCode = clientSession.getNote(ACTIVE_CODE);
String activeCode = loginSession.getNote(ACTIVE_CODE);
if (activeCode == null) {
logger.debug("Active code not found in client session");
return false;
}
clientSession.removeNote(ACTIVE_CODE);
loginSession.removeNote(ACTIVE_CODE);
return MessageDigest.isEqual(code.getBytes(), activeCode.getBytes());
} catch (Exception e) {

View file

@ -0,0 +1,99 @@
/*
* 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.managers;
import org.keycloak.models.ClientLoginSessionModel;
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.sessions.CommonClientSessionModel;
import org.keycloak.sessions.LoginSessionModel;
/**
* TODO: More object oriented and rather add parsing/generating logic into the session implementations itself
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
class CodeGenerateUtil {
static <CS extends CommonClientSessionModel> CS parseSession(String code, KeycloakSession session, RealmModel realm, Class<CS> expectedClazz) {
CommonClientSessionModel result = null;
if (expectedClazz.equals(ClientSessionModel.class)) {
try {
String[] parts = code.split("\\.");
String id = parts[2];
result = session.sessions().getClientSession(realm, id);
} catch (ArrayIndexOutOfBoundsException e) {
return null;
}
} else if (expectedClazz.equals(LoginSessionModel.class)) {
result = session.loginSessions().getCurrentLoginSession(realm);
} else if (expectedClazz.equals(ClientLoginSessionModel.class)) {
try {
String[] parts = code.split("\\.");
String userSessionId = parts[1];
String clientUUID = parts[2];
UserSessionModel userSession = session.sessions().getUserSession(realm, userSessionId);
if (userSession == null) {
return null;
}
result = userSession.getClientLoginSessions().get(clientUUID);
} catch (ArrayIndexOutOfBoundsException e) {
return null;
}
} else {
throw new IllegalArgumentException("Not known impl: " + expectedClazz.getName());
}
return expectedClazz.cast(result);
}
static String generateCode(CommonClientSessionModel clientSession, String actionId) {
if (clientSession instanceof ClientSessionModel) {
StringBuilder sb = new StringBuilder();
sb.append("cls.");
sb.append(actionId);
sb.append('.');
sb.append(clientSession.getId());
return sb.toString();
} else if (clientSession instanceof LoginSessionModel) {
// Should be sufficient. LoginSession itself is in the cookie
return actionId;
} else if (clientSession instanceof ClientLoginSessionModel) {
String userSessionId = ((ClientLoginSessionModel) clientSession).getUserSession().getId();
String clientUUID = clientSession.getClient().getId();
StringBuilder sb = new StringBuilder();
sb.append("uss.");
sb.append(actionId);
sb.append('.');
sb.append(userSessionId);
sb.append('.');
sb.append(clientUUID);
return sb.toString();
} else {
throw new IllegalArgumentException("Not known impl: " + clientSession.getClass().getName());
}
}
}

View file

@ -0,0 +1,26 @@
/*
* 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.sessions;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface LoginSessionProviderFactory extends ProviderFactory<LoginSessionProvider> {
}

View file

@ -0,0 +1,49 @@
/*
* 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.sessions;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LoginSessionSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "loginSessions";
}
@Override
public Class<? extends Provider> getProviderClass() {
return LoginSessionProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return LoginSessionProviderFactory.class;
}
}

View file

@ -32,6 +32,7 @@ org.keycloak.timer.TimerSpi
org.keycloak.scripting.ScriptingSpi
org.keycloak.services.managers.BruteForceProtectorSpi
org.keycloak.services.resource.RealmResourceSPI
org.keycloak.sessions.LoginSessionSpi
org.keycloak.protocol.ClientInstallationSpi
org.keycloak.protocol.LoginProtocolSpi
org.keycloak.protocol.ProtocolMapperSpi

View file

@ -0,0 +1,30 @@
/*
* 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;
import org.keycloak.sessions.CommonClientSessionModel;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface ClientLoginSessionModel extends CommonClientSessionModel {
void setUserSession(UserSessionModel userSession);
UserSessionModel getUserSession();
}

View file

@ -20,14 +20,12 @@ package org.keycloak.models;
import java.util.Map;
import java.util.Set;
import org.keycloak.sessions.CommonClientSessionModel;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface ClientSessionModel {
public String getId();
public RealmModel getRealm();
public ClientModel getClient();
public interface ClientSessionModel extends CommonClientSessionModel {
public UserSessionModel getUserSession();
public void setUserSession(UserSessionModel userSession);
@ -35,41 +33,12 @@ public interface ClientSessionModel {
public String getRedirectUri();
public void setRedirectUri(String uri);
public int getTimestamp();
public void setTimestamp(int timestamp);
public String getAction();
public void setAction(String action);
public Set<String> getRoles();
public void setRoles(Set<String> roles);
public Set<String> getProtocolMappers();
public void setProtocolMappers(Set<String> protocolMappers);
public Map<String, ExecutionStatus> getExecutionStatus();
public void setExecutionStatus(String authenticator, ExecutionStatus status);
public void clearExecutionStatus();
public UserModel getAuthenticatedUser();
public void setAuthenticatedUser(UserModel user);
/**
* Authentication request type, i.e. OAUTH, SAML 2.0, SAML 1.1, etc.
*
* @return
*/
public String getAuthMethod();
public void setAuthMethod(String method);
public String getNote(String name);
public void setNote(String name, String value);
public void removeNote(String name);
public Map<String, String> getNotes();
/**
* Required actions that are attached to this client session.
*
@ -103,28 +72,5 @@ public interface ClientSessionModel {
public void clearUserSessionNotes();
public static enum Action {
OAUTH_GRANT,
CODE_TO_TOKEN,
VERIFY_EMAIL,
UPDATE_PROFILE,
CONFIGURE_TOTP,
UPDATE_PASSWORD,
RECOVER_PASSWORD, // deprecated
AUTHENTICATE,
SOCIAL_CALLBACK,
LOGGED_OUT,
RESET_CREDENTIALS,
EXECUTE_ACTIONS,
REQUIRED_ACTIONS
}
public enum ExecutionStatus {
FAILED,
SUCCESS,
SETUP_REQUIRED,
ATTEMPTED,
SKIPPED,
CHALLENGED
}
}

View file

@ -20,6 +20,7 @@ package org.keycloak.models;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.cache.UserCache;
import org.keycloak.provider.Provider;
import org.keycloak.sessions.LoginSessionProvider;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import java.util.Set;
@ -102,6 +103,9 @@ public interface KeycloakSession {
UserSessionProvider sessions();
LoginSessionProvider loginSessions();
void close();

View file

@ -53,6 +53,9 @@ public interface UserSessionModel {
void setLastSessionRefresh(int seconds);
Map<String, ClientLoginSessionModel> getClientLoginSessions();
// TODO: Remove
List<ClientSessionModel> getClientSessions();
public String getNote(String name);
@ -64,7 +67,7 @@ public interface UserSessionModel {
void setState(State state);
public static enum State {
LOGGING_IN,
LOGGING_IN, // TODO: Maybe state "LOGGING_IN" is useless now once userSession is attached after requiredActions
LOGGED_IN,
LOGGING_OUT,
LOGGED_OUT

View file

@ -27,7 +27,7 @@ import java.util.List;
*/
public interface UserSessionProvider extends Provider {
ClientSessionModel createClientSession(RealmModel realm, ClientModel client);
ClientLoginSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession);
ClientSessionModel getClientSession(RealmModel realm, String id);
ClientSessionModel getClientSession(String id);
@ -40,6 +40,8 @@ public interface UserSessionProvider extends Provider {
UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId);
long getActiveUserSessions(RealmModel realm, ClientModel client);
// This will remove attached ClientLoginSessionModels too
void removeUserSession(RealmModel realm, UserSessionModel session);
void removeUserSessions(RealmModel realm, UserModel user);
@ -62,9 +64,9 @@ public interface UserSessionProvider extends Provider {
// Removes the attached clientSessions as well
void removeOfflineUserSession(RealmModel realm, UserSessionModel userSession);
ClientSessionModel createOfflineClientSession(ClientSessionModel clientSession);
ClientLoginSessionModel createOfflineClientSession(ClientLoginSessionModel clientSession);
ClientSessionModel getOfflineClientSession(RealmModel realm, String clientSessionId);
List<ClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user);
List<UserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user);
// Don't remove userSession even if it's last userSession
void removeOfflineClientSession(RealmModel realm, String clientSessionId);

View file

@ -0,0 +1,86 @@
/*
* 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.sessions;
import java.util.Map;
import java.util.Set;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
/**
* Predecesor of LoginSessionModel, ClientLoginSessionModel and ClientSessionModel (then action tickets). Maybe we will remove it later...
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface CommonClientSessionModel {
public String getRedirectUri();
public void setRedirectUri(String uri);
public String getId();
public RealmModel getRealm();
public ClientModel getClient();
public int getTimestamp();
public void setTimestamp(int timestamp);
public String getAction();
public void setAction(String action);
public String getProtocol();
public void setProtocol(String method);
// TODO: Not needed here...?
public Set<String> getRoles();
public void setRoles(Set<String> roles);
// TODO: Not needed here...?
public Set<String> getProtocolMappers();
public void setProtocolMappers(Set<String> protocolMappers);
public String getNote(String name);
public void setNote(String name, String value);
public void removeNote(String name);
public Map<String, String> getNotes();
public static enum Action {
OAUTH_GRANT,
CODE_TO_TOKEN,
VERIFY_EMAIL,
UPDATE_PROFILE,
CONFIGURE_TOTP,
UPDATE_PASSWORD,
RECOVER_PASSWORD, // deprecated
AUTHENTICATE,
SOCIAL_CALLBACK,
LOGGED_OUT,
RESET_CREDENTIALS,
EXECUTE_ACTIONS,
REQUIRED_ACTIONS
}
public enum ExecutionStatus {
FAILED,
SUCCESS,
SETUP_REQUIRED,
ATTEMPTED,
SKIPPED,
CHALLENGED
}
}

View file

@ -0,0 +1,76 @@
/*
* 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.sessions;
import java.util.Map;
import java.util.Set;
import org.keycloak.models.UserModel;
/**
* Using class for now to avoid many updates among implementations
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface LoginSessionModel extends CommonClientSessionModel {
//
// public UserSessionModel getUserSession();
// public void setUserSession(UserSessionModel userSession);
public Map<String, ExecutionStatus> getExecutionStatus();
public void setExecutionStatus(String authenticator, ExecutionStatus status);
public void clearExecutionStatus();
public UserModel getAuthenticatedUser();
public void setAuthenticatedUser(UserModel user);
/**
* Required actions that are attached to this client session.
*
* @return
*/
Set<String> getRequiredActions();
void addRequiredAction(String action);
void removeRequiredAction(String action);
void addRequiredAction(UserModel.RequiredAction action);
void removeRequiredAction(UserModel.RequiredAction action);
/**
* These are notes you want applied to the UserSessionModel when the client session is attached to it.
*
* @param name
* @param value
*/
public void setUserSessionNote(String name, String value);
/**
* These are notes you want applied to the UserSessionModel when the client session is attached to it.
*
* @return
*/
public Map<String, String> getUserSessionNotes();
public void clearUserSessionNotes();
}

View file

@ -0,0 +1,43 @@
/*
* 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.sessions;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.Provider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface LoginSessionProvider extends Provider {
LoginSessionModel createLoginSession(RealmModel realm, ClientModel client, boolean browser);
LoginSessionModel getCurrentLoginSession(RealmModel realm);
LoginSessionModel getLoginSession(RealmModel realm, String loginSessionId);
void removeLoginSession(RealmModel realm, LoginSessionModel loginSession);
void removeExpired(RealmModel realm);
void onRealmRemoved(RealmModel realm);
void onClientRemoved(RealmModel realm, ClientModel client);
}

View file

@ -31,6 +31,7 @@ import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
@ -50,6 +51,7 @@ 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.sessions.LoginSessionModel;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
@ -67,7 +69,7 @@ public class AuthenticationProcessor {
protected static final Logger logger = Logger.getLogger(AuthenticationProcessor.class);
protected RealmModel realm;
protected UserSessionModel userSession;
protected ClientSessionModel clientSession;
protected LoginSessionModel loginSession;
protected ClientConnection connection;
protected UriInfo uriInfo;
protected KeycloakSession session;
@ -87,7 +89,6 @@ public class AuthenticationProcessor {
* This could be an success message forwarded from another authenticator
*/
protected FormMessage forwardedSuccessMessage;
protected boolean userSessionCreated;
// Used for client authentication
protected ClientModel client;
@ -128,8 +129,8 @@ public class AuthenticationProcessor {
return clientAuthAttributes;
}
public ClientSessionModel getClientSession() {
return clientSession;
public LoginSessionModel getLoginSession() {
return loginSession;
}
public ClientConnection getConnection() {
@ -148,17 +149,13 @@ public class AuthenticationProcessor {
return userSession;
}
public boolean isUserSessionCreated() {
return userSessionCreated;
}
public AuthenticationProcessor setRealm(RealmModel realm) {
this.realm = realm;
return this;
}
public AuthenticationProcessor setClientSession(ClientSessionModel clientSession) {
this.clientSession = clientSession;
public AuthenticationProcessor setLoginSession(LoginSessionModel loginSession) {
this.loginSession = loginSession;
return this;
}
@ -213,8 +210,8 @@ public class AuthenticationProcessor {
}
public String generateCode() {
ClientSessionCode accessCode = new ClientSessionCode(session, getRealm(), getClientSession());
clientSession.setTimestamp(Time.currentTime());
ClientSessionCode accessCode = new ClientSessionCode(session, getRealm(), getLoginSession());
loginSession.setTimestamp(Time.currentTime());
return accessCode.getCode();
}
@ -232,15 +229,15 @@ public class AuthenticationProcessor {
}
public void setAutheticatedUser(UserModel user) {
UserModel previousUser = clientSession.getAuthenticatedUser();
UserModel previousUser = getLoginSession().getAuthenticatedUser();
if (previousUser != null && !user.getId().equals(previousUser.getId()))
throw new AuthenticationFlowException(AuthenticationFlowError.USER_CONFLICT);
validateUser(user);
getClientSession().setAuthenticatedUser(user);
getLoginSession().setAuthenticatedUser(user);
}
public void clearAuthenticatedUser() {
getClientSession().setAuthenticatedUser(null);
getLoginSession().setAuthenticatedUser(null);
}
public class Result implements AuthenticationFlowContext, ClientAuthenticationFlowContext {
@ -363,7 +360,7 @@ public class AuthenticationProcessor {
@Override
public UserModel getUser() {
return getClientSession().getAuthenticatedUser();
return getLoginSession().getAuthenticatedUser();
}
@Override
@ -397,8 +394,8 @@ public class AuthenticationProcessor {
}
@Override
public ClientSessionModel getClientSession() {
return AuthenticationProcessor.this.getClientSession();
public LoginSessionModel getLoginSession() {
return AuthenticationProcessor.this.getLoginSession();
}
@Override
@ -490,12 +487,12 @@ public class AuthenticationProcessor {
@Override
public void cancelLogin() {
getEvent().error(Errors.REJECTED_BY_USER);
LoginProtocol protocol = getSession().getProvider(LoginProtocol.class, getClientSession().getAuthMethod());
LoginProtocol protocol = getSession().getProvider(LoginProtocol.class, getLoginSession().getProtocol());
protocol.setRealm(getRealm())
.setHttpHeaders(getHttpRequest().getHttpHeaders())
.setUriInfo(getUriInfo())
.setEventBuilder(event);
Response response = protocol.sendError(getClientSession(), Error.CANCELLED_BY_USER);
Response response = protocol.sendError(getLoginSession(), Error.CANCELLED_BY_USER);
forceChallenge(response);
}
@ -539,7 +536,7 @@ public class AuthenticationProcessor {
public void logFailure() {
if (realm.isBruteForceProtected()) {
String username = clientSession.getNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
String username = loginSession.getNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
// todo need to handle non form failures
if (username == null) {
@ -569,7 +566,7 @@ public class AuthenticationProcessor {
}
public boolean isSuccessful(AuthenticationExecutionModel model) {
ClientSessionModel.ExecutionStatus status = clientSession.getExecutionStatus().get(model.getId());
ClientSessionModel.ExecutionStatus status = loginSession.getExecutionStatus().get(model.getId());
if (status == null) return false;
return status == ClientSessionModel.ExecutionStatus.SUCCESS;
}
@ -602,10 +599,10 @@ public class AuthenticationProcessor {
} else if (e.getError() == AuthenticationFlowError.FORK_FLOW) {
ForkFlowException reset = (ForkFlowException)e;
ClientSessionModel clone = clone(session, clientSession);
LoginSessionModel clone = clone(session, loginSession);
clone.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clone)
processor.setLoginSession(clone)
.setFlowPath(LoginActionsService.AUTHENTICATE_PATH)
.setFlowId(realm.getBrowserFlow().getId())
.setForwardedErrorMessage(reset.getErrorMessage())
@ -707,12 +704,12 @@ public class AuthenticationProcessor {
}
public static Response redirectToRequiredActions(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession, UriInfo uriInfo) {
public static Response redirectToRequiredActions(KeycloakSession session, RealmModel realm, LoginSessionModel loginSession, UriInfo uriInfo) {
// redirect to non-action url so browser refresh button works without reposting past data
ClientSessionCode accessCode = new ClientSessionCode(session, realm, clientSession);
ClientSessionCode<LoginSessionModel> accessCode = new ClientSessionCode<>(session, realm, loginSession);
accessCode.setAction(ClientSessionModel.Action.REQUIRED_ACTIONS.name());
clientSession.setTimestamp(Time.currentTime());
loginSession.setTimestamp(Time.currentTime());
URI redirect = LoginActionsService.loginActionsBaseUrl(uriInfo)
.path(LoginActionsService.REQUIRED_ACTION)
@ -721,22 +718,23 @@ public class AuthenticationProcessor {
}
public static void resetFlow(ClientSessionModel clientSession) {
public static void resetFlow(LoginSessionModel loginSession) {
logger.debug("RESET FLOW");
clientSession.setTimestamp(Time.currentTime());
clientSession.setAuthenticatedUser(null);
clientSession.clearExecutionStatus();
clientSession.clearUserSessionNotes();
clientSession.removeNote(CURRENT_AUTHENTICATION_EXECUTION);
loginSession.setTimestamp(Time.currentTime());
loginSession.setAuthenticatedUser(null);
loginSession.clearExecutionStatus();
loginSession.clearUserSessionNotes();
loginSession.removeNote(CURRENT_AUTHENTICATION_EXECUTION);
}
public static ClientSessionModel clone(KeycloakSession session, ClientSessionModel clientSession) {
ClientSessionModel clone = session.sessions().createClientSession(clientSession.getRealm(), clientSession.getClient());
for (Map.Entry<String, String> entry : clientSession.getNotes().entrySet()) {
public static LoginSessionModel clone(KeycloakSession session, LoginSessionModel loginSession) {
// TODO: Doublecheck false...
LoginSessionModel clone = session.loginSessions().createLoginSession(loginSession.getRealm(), loginSession.getClient(), false);
for (Map.Entry<String, String> entry : loginSession.getNotes().entrySet()) {
clone.setNote(entry.getKey(), entry.getValue());
}
clone.setRedirectUri(clientSession.getRedirectUri());
clone.setAuthMethod(clientSession.getAuthMethod());
clone.setRedirectUri(loginSession.getRedirectUri());
clone.setProtocol(loginSession.getProtocol());
clone.setTimestamp(Time.currentTime());
clone.removeNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
return clone;
@ -747,26 +745,26 @@ public class AuthenticationProcessor {
public Response authenticationAction(String execution) {
logger.debug("authenticationAction");
checkClientSession();
String current = clientSession.getNote(CURRENT_AUTHENTICATION_EXECUTION);
String current = loginSession.getNote(CURRENT_AUTHENTICATION_EXECUTION);
if (!execution.equals(current)) {
logger.debug("Current execution does not equal executed execution. Might be a page refresh");
//logFailure();
//resetFlow(clientSession);
return authenticate();
}
UserModel authUser = clientSession.getAuthenticatedUser();
UserModel authUser = loginSession.getAuthenticatedUser();
validateUser(authUser);
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
logger.debug("Cannot find execution, reseting flow");
logFailure();
resetFlow(clientSession);
resetFlow(loginSession);
return authenticate();
}
event.client(clientSession.getClient().getClientId())
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
.detail(Details.AUTH_METHOD, clientSession.getAuthMethod());
String authType = clientSession.getNote(Details.AUTH_TYPE);
event.client(loginSession.getClient().getClientId())
.detail(Details.REDIRECT_URI, loginSession.getRedirectUri())
.detail(Details.AUTH_METHOD, loginSession.getProtocol());
String authType = loginSession.getNote(Details.AUTH_TYPE);
if (authType != null) {
event.detail(Details.AUTH_TYPE, authType);
}
@ -774,14 +772,14 @@ public class AuthenticationProcessor {
AuthenticationFlow authenticationFlow = createFlowExecution(this.flowId, model);
Response challenge = authenticationFlow.processAction(execution);
if (challenge != null) return challenge;
if (clientSession.getAuthenticatedUser() == null) {
if (loginSession.getAuthenticatedUser() == null) {
throw new AuthenticationFlowException(AuthenticationFlowError.UNKNOWN_USER);
}
return authenticationComplete();
}
public void checkClientSession() {
ClientSessionCode code = new ClientSessionCode(session, realm, clientSession);
ClientSessionCode code = new ClientSessionCode(session, realm, loginSession);
String action = ClientSessionModel.Action.AUTHENTICATE.name();
if (!code.isValidAction(action)) {
throw new AuthenticationFlowException(AuthenticationFlowError.INVALID_CLIENT_SESSION);
@ -789,25 +787,25 @@ public class AuthenticationProcessor {
if (!code.isActionActive(ClientSessionCode.ActionType.LOGIN)) {
throw new AuthenticationFlowException(AuthenticationFlowError.EXPIRED_CODE);
}
clientSession.setTimestamp(Time.currentTime());
loginSession.setTimestamp(Time.currentTime());
}
public Response authenticateOnly() throws AuthenticationFlowException {
logger.debug("AUTHENTICATE ONLY");
checkClientSession();
event.client(clientSession.getClient().getClientId())
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
.detail(Details.AUTH_METHOD, clientSession.getAuthMethod());
String authType = clientSession.getNote(Details.AUTH_TYPE);
event.client(loginSession.getClient().getClientId())
.detail(Details.REDIRECT_URI, loginSession.getRedirectUri())
.detail(Details.AUTH_METHOD, loginSession.getProtocol());
String authType = loginSession.getNote(Details.AUTH_TYPE);
if (authType != null) {
event.detail(Details.AUTH_TYPE, authType);
}
UserModel authUser = clientSession.getAuthenticatedUser();
UserModel authUser = loginSession.getAuthenticatedUser();
validateUser(authUser);
AuthenticationFlow authenticationFlow = createFlowExecution(this.flowId, null);
Response challenge = authenticationFlow.processFlow();
if (challenge != null) return challenge;
if (clientSession.getAuthenticatedUser() == null) {
if (loginSession.getAuthenticatedUser() == null) {
throw new AuthenticationFlowException(AuthenticationFlowError.UNKNOWN_USER);
}
return challenge;
@ -835,34 +833,44 @@ public class AuthenticationProcessor {
}
}
public void attachSession() {
String username = clientSession.getAuthenticatedUser().getUsername();
String attemptedUsername = clientSession.getNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
// May create userSession too
public ClientLoginSessionModel attachSession() {
return attachSession(loginSession, userSession, session, realm, connection, event);
}
// May create new userSession too (if userSession argument is null)
public static ClientLoginSessionModel attachSession(LoginSessionModel loginSession, UserSessionModel userSession, KeycloakSession session, RealmModel realm, ClientConnection connection, EventBuilder event) {
String username = loginSession.getAuthenticatedUser().getUsername();
String attemptedUsername = loginSession.getNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
if (attemptedUsername != null) username = attemptedUsername;
String rememberMe = clientSession.getNote(Details.REMEMBER_ME);
String rememberMe = loginSession.getNote(Details.REMEMBER_ME);
boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("true");
if (userSession == null) { // if no authenticator attached a usersession
userSession = session.sessions().createUserSession(realm, clientSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), clientSession.getAuthMethod(), remember, null, null);
userSession = session.sessions().createUserSession(realm, loginSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), loginSession.getProtocol(), remember, null, null);
userSession.setState(UserSessionModel.State.LOGGING_IN);
userSessionCreated = true;
}
if (remember) {
event.detail(Details.REMEMBER_ME, "true");
}
TokenManager.attachClientSession(userSession, clientSession);
ClientLoginSessionModel clientSession = TokenManager.attachLoginSession(session, userSession, loginSession);
event.user(userSession.getUser())
.detail(Details.USERNAME, username)
.session(userSession);
return clientSession;
}
public void evaluateRequiredActionTriggers() {
AuthenticationManager.evaluateRequiredActionTriggers(session, userSession, clientSession, connection, request, uriInfo, event, realm, clientSession.getAuthenticatedUser());
AuthenticationManager.evaluateRequiredActionTriggers(session, loginSession, connection, request, uriInfo, event, realm, loginSession.getAuthenticatedUser());
}
public Response finishAuthentication(LoginProtocol protocol) {
event.success();
RealmModel realm = clientSession.getRealm();
return AuthenticationManager.redirectAfterSuccessfulFlow(session, realm, userSession, clientSession, request, uriInfo, connection, event, protocol);
RealmModel realm = loginSession.getRealm();
ClientLoginSessionModel clientSession = attachSession();
return AuthenticationManager.redirectAfterSuccessfulFlow(session, realm, userSession,clientSession, request, uriInfo, connection, event, protocol);
}
@ -877,19 +885,21 @@ public class AuthenticationProcessor {
}
protected Response authenticationComplete() {
attachSession();
// attachSession(); // Session will be attached after requiredActions + consents are finished.
if (isActionRequired()) {
return redirectToRequiredActions(session, realm, clientSession, uriInfo);
// TODO:mposolda Changed this to avoid additional redirect. Doublecheck consequences...
//return redirectToRequiredActions(session, realm, loginSession, uriInfo);
return AuthenticationManager.nextActionAfterAuthentication(session, loginSession, connection, request, uriInfo, event);
} else {
event.detail(Details.CODE_ID, clientSession.getId()); // todo This should be set elsewhere. find out why tests fail. Don't know where this is supposed to be set
event.detail(Details.CODE_ID, loginSession.getId()); // todo This should be set elsewhere. find out why tests fail. Don't know where this is supposed to be set
// the user has successfully logged in and we can clear his/her previous login failure attempts.
logSuccess();
return AuthenticationManager.finishedRequiredActions(session, userSession, clientSession, connection, request, uriInfo, event);
return AuthenticationManager.finishedRequiredActions(session, loginSession, connection, request, uriInfo, event);
}
}
public boolean isActionRequired() {
return AuthenticationManager.isActionRequired(session, userSession, clientSession, connection, request, uriInfo, event);
return AuthenticationManager.isActionRequired(session, loginSession, connection, request, uriInfo, event);
}
public AuthenticationProcessor.Result createAuthenticatorContext(AuthenticationExecutionModel model, Authenticator authenticator, List<AuthenticationExecutionModel> executions) {

View file

@ -51,7 +51,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
protected boolean isProcessed(AuthenticationExecutionModel model) {
if (model.isDisabled()) return true;
ClientSessionModel.ExecutionStatus status = processor.getClientSession().getExecutionStatus().get(model.getId());
ClientSessionModel.ExecutionStatus status = processor.getLoginSession().getExecutionStatus().get(model.getId());
if (status == null) return false;
return status == ClientSessionModel.ExecutionStatus.SUCCESS || status == ClientSessionModel.ExecutionStatus.SKIPPED
|| status == ClientSessionModel.ExecutionStatus.ATTEMPTED
@ -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.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
processor.getLoginSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
if (model.isAlternative()) alternativeSuccessful = true;
return processFlow();
} else {
@ -92,7 +92,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
authenticator.action(result);
Response response = processResult(result);
if (response == null) {
processor.getClientSession().removeNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
processor.getLoginSession().removeNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
if (result.status == FlowStatus.SUCCESS) {
// we do this so that flow can redirect to a non-action URL
processor.setActionSuccessful();
@ -119,7 +119,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
}
if (model.isAlternative() && alternativeSuccessful) {
logger.debug("Skip alternative execution");
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
processor.getLoginSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
continue;
}
if (model.isAuthenticatorFlow()) {
@ -127,7 +127,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
Response flowChallenge = authenticationFlow.processFlow();
if (flowChallenge == null) {
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
processor.getLoginSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
if (model.isAlternative()) alternativeSuccessful = true;
continue;
} else {
@ -135,13 +135,13 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
alternativeChallenge = flowChallenge;
challengedAlternativeExecution = model;
} else if (model.isRequired()) {
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
processor.getLoginSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return flowChallenge;
} else if (model.isOptional()) {
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
processor.getLoginSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
continue;
} else {
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
processor.getLoginSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
continue;
}
return flowChallenge;
@ -154,11 +154,11 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
}
Authenticator authenticator = factory.create(processor.getSession());
logger.debugv("authenticator: {0}", factory.getId());
UserModel authUser = processor.getClientSession().getAuthenticatedUser();
UserModel authUser = processor.getLoginSession().getAuthenticatedUser();
if (authenticator.requiresUser() && authUser == null) {
if (alternativeChallenge != null) {
processor.getClientSession().setExecutionStatus(challengedAlternativeExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
processor.getLoginSession().setExecutionStatus(challengedAlternativeExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return alternativeChallenge;
}
throw new AuthenticationFlowException("authenticator: " + factory.getId(), AuthenticationFlowError.UNKNOWN_USER);
@ -170,14 +170,14 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
if (model.isRequired()) {
if (factory.isUserSetupAllowed()) {
logger.debugv("authenticator SETUP_REQUIRED: {0}", factory.getId());
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser());
processor.getLoginSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getLoginSession().getAuthenticatedUser());
continue;
} else {
throw new AuthenticationFlowException(AuthenticationFlowError.CREDENTIAL_SETUP_REQUIRED);
}
} else if (model.isOptional()) {
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
processor.getLoginSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
continue;
}
}
@ -202,56 +202,56 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
switch (status) {
case SUCCESS:
logger.debugv("authenticator SUCCESS: {0}", execution.getAuthenticator());
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
processor.getLoginSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
if (execution.isAlternative()) alternativeSuccessful = true;
return null;
case FAILED:
logger.debugv("authenticator FAILED: {0}", execution.getAuthenticator());
processor.logFailure();
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
processor.getLoginSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
if (result.getChallenge() != null) {
return sendChallenge(result, execution);
}
throw new AuthenticationFlowException(result.getError());
case FORK:
logger.debugv("reset browser login from authenticator: {0}", execution.getAuthenticator());
processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
processor.getLoginSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
throw new ForkFlowException(result.getSuccessMessage(), result.getErrorMessage());
case FORCE_CHALLENGE:
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
processor.getLoginSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return sendChallenge(result, execution);
case CHALLENGE:
logger.debugv("authenticator CHALLENGE: {0}", execution.getAuthenticator());
if (execution.isRequired()) {
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
processor.getLoginSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return sendChallenge(result, execution);
}
UserModel authenticatedUser = processor.getClientSession().getAuthenticatedUser();
UserModel authenticatedUser = processor.getLoginSession().getAuthenticatedUser();
if (execution.isOptional() && authenticatedUser != null && result.getAuthenticator().configuredFor(processor.getSession(), processor.getRealm(), authenticatedUser)) {
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
processor.getLoginSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return sendChallenge(result, execution);
}
if (execution.isAlternative()) {
alternativeChallenge = result.getChallenge();
challengedAlternativeExecution = execution;
} else {
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
processor.getLoginSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
}
return null;
case FAILURE_CHALLENGE:
logger.debugv("authenticator FAILURE_CHALLENGE: {0}", execution.getAuthenticator());
processor.logFailure();
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
processor.getLoginSession().setExecutionStatus(execution.getId(), ClientSessionModel.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.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
processor.getLoginSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
return null;
case FLOW_RESET:
AuthenticationProcessor.resetFlow(processor.getClientSession());
AuthenticationProcessor.resetFlow(processor.getLoginSession());
return processor.authenticate();
default:
logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
@ -261,7 +261,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
}
public Response sendChallenge(AuthenticationProcessor.Result result, AuthenticationExecutionModel execution) {
processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
processor.getLoginSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
return result.getChallenge();
}

View file

@ -30,6 +30,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.sessions.LoginSessionModel;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
@ -93,7 +94,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
@Override
public UserModel getUser() {
return getClientSession().getAuthenticatedUser();
return getLoginSession().getAuthenticatedUser();
}
@Override
@ -107,8 +108,8 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
}
@Override
public ClientSessionModel getClientSession() {
return processor.getClientSession();
public LoginSessionModel getLoginSession() {
return processor.getLoginSession();
}
@Override
@ -178,7 +179,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
FormActionFactory factory = (FormActionFactory)processor.getSession().getKeycloakSessionFactory().getProviderFactory(FormAction.class, formActionExecution.getAuthenticator());
FormAction action = factory.create(processor.getSession());
UserModel authUser = processor.getClientSession().getAuthenticatedUser();
UserModel authUser = processor.getLoginSession().getAuthenticatedUser();
if (action.requiresUser() && authUser == null) {
throw new AuthenticationFlowException("form action: " + formExecution.getAuthenticator() + " requires user", AuthenticationFlowError.UNKNOWN_USER);
}
@ -235,14 +236,14 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
}
// set status and required actions only if form is fully successful
for (Map.Entry<String, ClientSessionModel.ExecutionStatus> entry : executionStatus.entrySet()) {
processor.getClientSession().setExecutionStatus(entry.getKey(), entry.getValue());
processor.getLoginSession().setExecutionStatus(entry.getKey(), entry.getValue());
}
for (FormAction action : requiredActions) {
action.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser());
action.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getLoginSession().getAuthenticatedUser());
}
processor.getClientSession().setExecutionStatus(actionExecution, ClientSessionModel.ExecutionStatus.SUCCESS);
processor.getClientSession().removeNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
processor.getLoginSession().setExecutionStatus(actionExecution, ClientSessionModel.ExecutionStatus.SUCCESS);
processor.getLoginSession().removeNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
processor.setActionSuccessful();
return null;
}
@ -262,7 +263,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
public Response renderForm(MultivaluedMap<String, String> formData, List<FormMessage> errors) {
String executionId = formExecution.getId();
processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, executionId);
processor.getLoginSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, executionId);
String code = processor.generateCode();
URI actionUrl = getActionUrl(executionId, code);
LoginFormsProvider form = processor.getSession().getProvider(LoginFormsProvider.class)

View file

@ -23,13 +23,12 @@ import org.keycloak.common.ClientConnection;
import org.keycloak.common.util.Time;
import org.keycloak.events.EventBuilder;
import org.keycloak.forms.login.LoginFormsProvider;
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.services.managers.ClientSessionCode;
import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.sessions.LoginSessionModel;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
@ -40,8 +39,7 @@ import java.net.URI;
* @version $Revision: 1 $
*/
public class RequiredActionContextResult implements RequiredActionContext {
protected UserSessionModel userSession;
protected ClientSessionModel clientSession;
protected LoginSessionModel loginSession;
protected RealmModel realm;
protected EventBuilder eventBuilder;
protected KeycloakSession session;
@ -51,12 +49,11 @@ public class RequiredActionContextResult implements RequiredActionContext {
protected UserModel user;
protected RequiredActionFactory factory;
public RequiredActionContextResult(UserSessionModel userSession, ClientSessionModel clientSession,
public RequiredActionContextResult(LoginSessionModel loginSession,
RealmModel realm, EventBuilder eventBuilder, KeycloakSession session,
HttpRequest httpRequest,
UserModel user, RequiredActionFactory factory) {
this.userSession = userSession;
this.clientSession = clientSession;
this.loginSession = loginSession;
this.realm = realm;
this.eventBuilder = eventBuilder;
this.session = session;
@ -81,13 +78,8 @@ public class RequiredActionContextResult implements RequiredActionContext {
}
@Override
public ClientSessionModel getClientSession() {
return clientSession;
}
@Override
public UserSessionModel getUserSession() {
return userSession;
public LoginSessionModel getLoginSession() {
return loginSession;
}
@Override
@ -148,8 +140,8 @@ public class RequiredActionContextResult implements RequiredActionContext {
@Override
public String generateCode() {
ClientSessionCode accessCode = new ClientSessionCode(session, getRealm(), getClientSession());
clientSession.setTimestamp(Time.currentTime());
ClientSessionCode<LoginSessionModel> accessCode = new ClientSessionCode<>(session, getRealm(), getLoginSession());
loginSession.setTimestamp(Time.currentTime());
return accessCode.getCode();
}

View file

@ -25,11 +25,11 @@ import org.keycloak.authentication.authenticators.broker.util.ExistingUserInfo;
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.events.Errors;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.messages.Messages;
import org.keycloak.sessions.LoginSessionModel;
import javax.ws.rs.core.Response;
@ -59,13 +59,13 @@ public abstract class AbstractIdpAuthenticator implements Authenticator {
@Override
public void authenticate(AuthenticationFlowContext context) {
ClientSessionModel clientSession = context.getClientSession();
LoginSessionModel loginSession = context.getLoginSession();
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromClientSession(clientSession, BROKERED_CONTEXT_NOTE);
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromLoginSession(loginSession, BROKERED_CONTEXT_NOTE);
if (serializedCtx == null) {
throw new AuthenticationFlowException("Not found serialized context in clientSession", AuthenticationFlowError.IDENTITY_PROVIDER_ERROR);
}
BrokeredIdentityContext brokerContext = serializedCtx.deserialize(context.getSession(), clientSession);
BrokeredIdentityContext brokerContext = serializedCtx.deserialize(context.getSession(), loginSession);
if (!brokerContext.getIdpConfig().isEnabled()) {
sendFailureChallenge(context, Errors.IDENTITY_PROVIDER_ERROR, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR, AuthenticationFlowError.IDENTITY_PROVIDER_ERROR);
@ -76,9 +76,9 @@ public abstract class AbstractIdpAuthenticator implements Authenticator {
@Override
public void action(AuthenticationFlowContext context) {
ClientSessionModel clientSession = context.getClientSession();
LoginSessionModel clientSession = context.getLoginSession();
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromClientSession(clientSession, BROKERED_CONTEXT_NOTE);
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromLoginSession(clientSession, BROKERED_CONTEXT_NOTE);
if (serializedCtx == null) {
throw new AuthenticationFlowException("Not found serialized context in clientSession", AuthenticationFlowError.IDENTITY_PROVIDER_ERROR);
}
@ -112,8 +112,8 @@ public abstract class AbstractIdpAuthenticator implements Authenticator {
}
public static UserModel getExistingUser(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession) {
String existingUserId = clientSession.getNote(EXISTING_USER_INFO);
public static UserModel getExistingUser(KeycloakSession session, RealmModel realm, LoginSessionModel loginSession) {
String existingUserId = loginSession.getNote(EXISTING_USER_INFO);
if (existingUserId == null) {
throw new AuthenticationFlowException("Unexpected state. There is no existing duplicated user identified in ClientSession",
AuthenticationFlowError.INTERNAL_ERROR);

View file

@ -24,12 +24,12 @@ import org.keycloak.authentication.authenticators.broker.util.ExistingUserInfo;
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.messages.Messages;
import org.keycloak.sessions.LoginSessionModel;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
@ -41,9 +41,9 @@ public class IdpConfirmLinkAuthenticator extends AbstractIdpAuthenticator {
@Override
protected void authenticateImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) {
ClientSessionModel clientSession = context.getClientSession();
LoginSessionModel loginSession = context.getLoginSession();
String existingUserInfo = clientSession.getNote(EXISTING_USER_INFO);
String existingUserInfo = loginSession.getNote(EXISTING_USER_INFO);
if (existingUserInfo == null) {
ServicesLogger.LOGGER.noDuplicationDetected();
context.attempted();
@ -65,8 +65,8 @@ public class IdpConfirmLinkAuthenticator extends AbstractIdpAuthenticator {
String action = formData.getFirst("submitAction");
if (action != null && action.equals("updateProfile")) {
context.getClientSession().setNote(ENFORCE_UPDATE_PROFILE, "true");
context.getClientSession().removeNote(EXISTING_USER_INFO);
context.getLoginSession().setNote(ENFORCE_UPDATE_PROFILE, "true");
context.getLoginSession().removeNote(EXISTING_USER_INFO);
context.resetFlow();
} else if (action != null && action.equals("linkAccount")) {
context.success();

View file

@ -53,7 +53,7 @@ public class IdpCreateUserIfUniqueAuthenticator extends AbstractIdpAuthenticator
KeycloakSession session = context.getSession();
RealmModel realm = context.getRealm();
if (context.getClientSession().getNote(EXISTING_USER_INFO) != null) {
if (context.getLoginSession().getNote(EXISTING_USER_INFO) != null) {
context.attempted();
return;
}
@ -61,7 +61,7 @@ public class IdpCreateUserIfUniqueAuthenticator extends AbstractIdpAuthenticator
String username = getUsername(context, serializedCtx, brokerContext);
if (username == null) {
ServicesLogger.LOGGER.resetFlow(realm.isRegistrationEmailAsUsername() ? "Email" : "Username");
context.getClientSession().setNote(ENFORCE_UPDATE_PROFILE, "true");
context.getLoginSession().setNote(ENFORCE_UPDATE_PROFILE, "true");
context.resetFlow();
return;
}
@ -91,14 +91,14 @@ public class IdpCreateUserIfUniqueAuthenticator extends AbstractIdpAuthenticator
userRegisteredSuccess(context, federatedUser, serializedCtx, brokerContext);
context.setUser(federatedUser);
context.getClientSession().setNote(BROKER_REGISTERED_NEW_USER, "true");
context.getLoginSession().setNote(BROKER_REGISTERED_NEW_USER, "true");
context.success();
} else {
logger.debugf("Duplication detected. There is already existing user with %s '%s' .",
duplication.getDuplicateAttributeName(), duplication.getDuplicateAttributeValue());
// Set duplicated user, so next authenticators can deal with it
context.getClientSession().setNote(EXISTING_USER_INFO, duplication.serialize());
context.getLoginSession().setNote(EXISTING_USER_INFO, duplication.serialize());
Response challengeResponse = context.form()
.setError(Messages.FEDERATED_IDENTITY_EXISTS, duplication.getDuplicateAttributeName(), duplication.getDuplicateAttributeValue())

View file

@ -53,7 +53,7 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator
@Override
protected void authenticateImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) {
KeycloakSession session = context.getSession();
/*KeycloakSession session = context.getSession();
RealmModel realm = context.getRealm();
ClientSessionModel clientSession = context.getClientSession();
@ -63,9 +63,6 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator
return;
}
// Create action cookie to detect if email verification happened in same browser
LoginActionsService.createActionCookie(context.getRealm(), context.getUriInfo(), context.getConnection(), context.getClientSession().getId());
VerifyEmail.setupKey(clientSession);
UserModel existingUser = getExistingUser(session, realm, clientSession);
@ -107,12 +104,12 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator
.setStatus(Response.Status.OK)
.setAttribute(LoginFormsProvider.IDENTITY_PROVIDER_BROKER_CONTEXT, brokerContext)
.createIdpLinkEmailPage();
context.forceChallenge(challenge);
context.forceChallenge(challenge);*/
}
@Override
protected void actionImpl(AuthenticationFlowContext context, SerializedBrokeredIdentityContext serializedCtx, BrokeredIdentityContext brokerContext) {
MultivaluedMap<String, String> queryParams = context.getSession().getContext().getUri().getQueryParameters();
/*MultivaluedMap<String, String> queryParams = context.getSession().getContext().getUri().getQueryParameters();
String key = queryParams.getFirst(Constants.KEY);
ClientSessionModel clientSession = context.getClientSession();
RealmModel realm = context.getRealm();
@ -149,7 +146,7 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator
.setError(Messages.MISSING_PARAMETER, Constants.KEY)
.createErrorPage();
context.failureChallenge(AuthenticationFlowError.IDENTITY_PROVIDER_ERROR, challengeResponse);
}
}*/
}
@Override

View file

@ -33,7 +33,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.resources.AttributeFormDataProcessor;
import org.keycloak.services.validation.Validation;
@ -74,7 +73,7 @@ public class IdpReviewProfileAuthenticator extends AbstractIdpAuthenticator {
}
protected boolean requiresUpdateProfilePage(AuthenticationFlowContext context, SerializedBrokeredIdentityContext userCtx, BrokeredIdentityContext brokerContext) {
String enforceUpdateProfile = context.getClientSession().getNote(ENFORCE_UPDATE_PROFILE);
String enforceUpdateProfile = context.getLoginSession().getNote(ENFORCE_UPDATE_PROFILE);
if (Boolean.parseBoolean(enforceUpdateProfile)) {
return true;
}
@ -123,12 +122,12 @@ public class IdpReviewProfileAuthenticator extends AbstractIdpAuthenticator {
}
userCtx.setEmail(email);
context.getClientSession().setNote(UPDATE_PROFILE_EMAIL_CHANGED, "true");
context.getLoginSession().setNote(UPDATE_PROFILE_EMAIL_CHANGED, "true");
}
AttributeFormDataProcessor.process(formData, realm, userCtx);
userCtx.saveToClientSession(context.getClientSession(), BROKERED_CONTEXT_NOTE);
userCtx.saveToLoginSession(context.getLoginSession(), BROKERED_CONTEXT_NOTE);
logger.debugf("Profile updated successfully after first authentication with identity provider '%s' for broker user '%s'.", brokerContext.getIdpConfig().getAlias(), userCtx.getUsername());

View file

@ -39,7 +39,7 @@ public class IdpUsernamePasswordForm extends UsernamePasswordForm {
@Override
protected Response challenge(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
UserModel existingUser = AbstractIdpAuthenticator.getExistingUser(context.getSession(), context.getRealm(), context.getClientSession());
UserModel existingUser = AbstractIdpAuthenticator.getExistingUser(context.getSession(), context.getRealm(), context.getLoginSession());
return setupForm(context, formData, existingUser)
.setStatus(Response.Status.OK)
@ -48,7 +48,7 @@ public class IdpUsernamePasswordForm extends UsernamePasswordForm {
@Override
protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
UserModel existingUser = AbstractIdpAuthenticator.getExistingUser(context.getSession(), context.getRealm(), context.getClientSession());
UserModel existingUser = AbstractIdpAuthenticator.getExistingUser(context.getSession(), context.getRealm(), context.getLoginSession());
context.setUser(existingUser);
// Restore formData for the case of error
@ -58,7 +58,7 @@ public class IdpUsernamePasswordForm extends UsernamePasswordForm {
}
protected LoginFormsProvider setupForm(AuthenticationFlowContext context, MultivaluedMap<String, String> formData, UserModel existingUser) {
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromClientSession(context.getClientSession(), AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE);
SerializedBrokeredIdentityContext serializedCtx = SerializedBrokeredIdentityContext.readFromLoginSession(context.getLoginSession(), AbstractIdpAuthenticator.BROKERED_CONTEXT_NOTE);
if (serializedCtx == null) {
throw new AuthenticationFlowException("Not found serialized context in clientSession", AuthenticationFlowError.IDENTITY_PROVIDER_ERROR);
}

View file

@ -31,6 +31,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.sessions.LoginSessionModel;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
@ -246,7 +247,7 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
}
}
public BrokeredIdentityContext deserialize(KeycloakSession session, ClientSessionModel clientSession) {
public BrokeredIdentityContext deserialize(KeycloakSession session, LoginSessionModel loginSession) {
BrokeredIdentityContext ctx = new BrokeredIdentityContext(getId());
ctx.setUsername(getBrokerUsername());
@ -258,7 +259,7 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
ctx.setBrokerUserId(getBrokerUserId());
ctx.setToken(getToken());
RealmModel realm = clientSession.getRealm();
RealmModel realm = loginSession.getRealm();
IdentityProviderModel idpConfig = realm.getIdentityProviderByAlias(getIdentityProviderId());
if (idpConfig == null) {
throw new ModelException("Can't find identity provider with ID " + getIdentityProviderId() + " in realm " + realm.getName());
@ -282,7 +283,7 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
}
}
ctx.setClientSession(clientSession);
ctx.setLoginSession(loginSession);
return ctx;
}
@ -299,7 +300,7 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
ctx.setToken(context.getToken());
ctx.setIdentityProviderId(context.getIdpConfig().getAlias());
ctx.emailAsUsername = context.getClientSession().getRealm().isRegistrationEmailAsUsername();
ctx.emailAsUsername = context.getLoginSession().getRealm().isRegistrationEmailAsUsername();
IdentityProviderDataMarshaller serializer = context.getIdp().getMarshaller();
@ -314,23 +315,23 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
}
// Save this context as note to clientSession
public void saveToClientSession(ClientSessionModel clientSession, String noteKey) {
public void saveToLoginSession(LoginSessionModel loginSession, String noteKey) {
try {
String asString = JsonSerialization.writeValueAsString(this);
clientSession.setNote(noteKey, asString);
loginSession.setNote(noteKey, asString);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
public static SerializedBrokeredIdentityContext readFromClientSession(ClientSessionModel clientSession, String noteKey) {
String asString = clientSession.getNote(noteKey);
public static SerializedBrokeredIdentityContext readFromLoginSession(LoginSessionModel loginSession, String noteKey) {
String asString = loginSession.getNote(noteKey);
if (asString == null) {
return null;
} else {
try {
SerializedBrokeredIdentityContext serializedCtx = JsonSerialization.readValue(asString, SerializedBrokeredIdentityContext.class);
serializedCtx.emailAsUsername = clientSession.getRealm().isRegistrationEmailAsUsername();
serializedCtx.emailAsUsername = loginSession.getRealm().isRegistrationEmailAsUsername();
return serializedCtx;
} catch (IOException ioe) {
throw new RuntimeException(ioe);

View file

@ -126,7 +126,7 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth
username = username.trim();
context.getEvent().detail(Details.USERNAME, username);
context.getClientSession().setNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, username);
context.getLoginSession().setNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, username);
UserModel user = null;
try {
@ -159,10 +159,10 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth
String rememberMe = inputData.getFirst("rememberMe");
boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("on");
if (remember) {
context.getClientSession().setNote(Details.REMEMBER_ME, "true");
context.getLoginSession().setNote(Details.REMEMBER_ME, "true");
context.getEvent().detail(Details.REMEMBER_ME, "true");
} else {
context.getClientSession().removeNote(Details.REMEMBER_ME);
context.getLoginSession().removeNote(Details.REMEMBER_ME);
}
context.setUser(user);
return true;

View file

@ -25,6 +25,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.protocol.LoginProtocol;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.sessions.LoginSessionModel;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -44,8 +45,8 @@ public class CookieAuthenticator implements Authenticator {
if (authResult == null) {
context.attempted();
} else {
ClientSessionModel clientSession = context.getClientSession();
LoginProtocol protocol = context.getSession().getProvider(LoginProtocol.class, clientSession.getAuthMethod());
LoginSessionModel clientSession = context.getLoginSession();
LoginProtocol protocol = context.getSession().getProvider(LoginProtocol.class, clientSession.getProtocol());
// Cookie re-authentication is skipped if re-authentication is required
if (protocol.requireReauthentication(authResult.getSession(), clientSession)) {

View file

@ -63,7 +63,7 @@ public class IdentityProviderAuthenticator implements Authenticator {
List<IdentityProviderModel> identityProviders = context.getRealm().getIdentityProviders();
for (IdentityProviderModel identityProvider : identityProviders) {
if (identityProvider.isEnabled() && providerId.equals(identityProvider.getAlias())) {
String accessCode = new ClientSessionCode(context.getSession(), context.getRealm(), context.getClientSession()).getCode();
String accessCode = new ClientSessionCode<>(context.getSession(), context.getRealm(), context.getLoginSession()).getCode();
Response response = Response.seeOther(
Urls.identityProviderAuthnRequest(context.getUriInfo().getBaseUri(), providerId, context.getRealm().getName(), accessCode))
.build();

View file

@ -160,7 +160,7 @@ public class ScriptBasedAuthenticator implements Authenticator {
bindings.put("user", context.getUser());
bindings.put("session", context.getSession());
bindings.put("httpRequest", context.getHttpRequest());
bindings.put("clientSession", context.getClientSession());
bindings.put("clientSession", context.getLoginSession());
bindings.put("LOG", LOGGER);
});
}

View file

@ -98,7 +98,7 @@ public class SpnegoAuthenticator extends AbstractUsernameFormAuthenticator imple
context.setUser(output.getAuthenticatedUser());
if (output.getState() != null && !output.getState().isEmpty()) {
for (Map.Entry<String, String> entry : output.getState().entrySet()) {
context.getClientSession().setUserSessionNote(entry.getKey(), entry.getValue());
context.getLoginSession().setUserSessionNote(entry.getKey(), entry.getValue());
}
}
context.success();

View file

@ -59,7 +59,7 @@ public class UsernamePasswordForm extends AbstractUsernameFormAuthenticator impl
@Override
public void authenticate(AuthenticationFlowContext context) {
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
String loginHint = context.getLoginSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
String rememberMeUsername = AuthenticationManager.getRememberMeUsername(context.getRealm(), context.getHttpRequest().getHttpHeaders());
@ -72,7 +72,7 @@ public class UsernamePasswordForm extends AbstractUsernameFormAuthenticator impl
}
}
Response challengeResponse = challenge(context, formData);
context.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, context.getExecution().getId());
context.getLoginSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, context.getExecution().getId());
context.challenge(challengeResponse);
}

View file

@ -55,7 +55,7 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
return;
}
context.getEvent().detail(Details.USERNAME, username);
context.getClientSession().setNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, username);
context.getLoginSession().setNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, username);
UserModel user = null;
try {

View file

@ -53,9 +53,9 @@ public class ResetCredentialChooseUser implements Authenticator, AuthenticatorFa
@Override
public void authenticate(AuthenticationFlowContext context) {
String existingUserId = context.getClientSession().getNote(AbstractIdpAuthenticator.EXISTING_USER_INFO);
String existingUserId = context.getLoginSession().getNote(AbstractIdpAuthenticator.EXISTING_USER_INFO);
if (existingUserId != null) {
UserModel existingUser = AbstractIdpAuthenticator.getExistingUser(context.getSession(), context.getRealm(), context.getClientSession());
UserModel existingUser = AbstractIdpAuthenticator.getExistingUser(context.getSession(), context.getRealm(), context.getLoginSession());
logger.debugf("Forget-password triggered when reauthenticating user after first broker login. Skipping reset-credential-choose-user screen and using user '%s' ", existingUser.getUsername());
context.setUser(existingUser);
@ -89,7 +89,7 @@ public class ResetCredentialChooseUser implements Authenticator, AuthenticatorFa
user = context.getSession().users().getUserByEmail(username, realm);
}
context.getClientSession().setNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, username);
context.getLoginSession().setNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, username);
// we don't want people guessing usernames, so if there is a problem, just continue, but don't set the user
// a null user will notify further executions, that this was a failure.

View file

@ -61,7 +61,7 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
@Override
public void authenticate(AuthenticationFlowContext context) {
LoginActionsService.createActionCookie(context.getRealm(), context.getUriInfo(), context.getConnection(), context.getClientSession().getId());
/*LoginActionsService.createActionCookie(context.getRealm(), context.getUriInfo(), context.getConnection(), context.getClientSession().getId());
UserModel user = context.getUser();
String username = context.getClientSession().getNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
@ -109,12 +109,12 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
.setError(Messages.EMAIL_SENT_ERROR)
.createErrorPage();
context.failure(AuthenticationFlowError.INTERNAL_ERROR, challenge);
}
}*/
}
@Override
public void action(AuthenticationFlowContext context) {
String secret = context.getClientSession().getNote(RESET_CREDENTIAL_SECRET);
/*String secret = context.getClientSession().getNote(RESET_CREDENTIAL_SECRET);
String key = context.getUriInfo().getQueryParameters().getFirst(Constants.KEY);
// Can only guess once! We remove the note so another guess can't happen
@ -129,7 +129,7 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
}
// We now know email is valid, so set it to valid.
context.getUser().setEmailVerified(true);
context.success();
context.success();*/
}
@Override

View file

@ -33,7 +33,7 @@ public class ResetOTP extends AbstractSetRequiredActionAuthenticator {
if (context.getExecution().isRequired() ||
(context.getExecution().isOptional() &&
configuredFor(context))) {
context.getClientSession().addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
context.getLoginSession().addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
}
context.success();
}

View file

@ -34,14 +34,14 @@ public class ResetPassword extends AbstractSetRequiredActionAuthenticator {
@Override
public void authenticate(AuthenticationFlowContext context) {
String actionCookie = LoginActionsService.getActionCookie(context.getSession().getContext().getRequestHeaders(), context.getRealm(), context.getUriInfo(), context.getConnection());
if (actionCookie == null || !actionCookie.equals(context.getClientSession().getId())) {
context.getClientSession().setNote(AuthenticationManager.END_AFTER_REQUIRED_ACTIONS, "true");
if (actionCookie == null || !actionCookie.equals(context.getLoginSession().getId())) {
context.getLoginSession().setNote(AuthenticationManager.END_AFTER_REQUIRED_ACTIONS, "true");
}
if (context.getExecution().isRequired() ||
(context.getExecution().isOptional() &&
configuredFor(context))) {
context.getClientSession().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
context.getLoginSession().addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
}
context.success();
}

View file

@ -134,16 +134,16 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
user.setEnabled(true);
user.setEmail(email);
context.getClientSession().setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, username);
context.getLoginSession().setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, username);
AttributeFormDataProcessor.process(formData, context.getRealm(), user);
context.setUser(user);
context.getEvent().user(user);
context.getEvent().success();
context.newEvent().event(EventType.LOGIN);
context.getEvent().client(context.getClientSession().getClient().getClientId())
.detail(Details.REDIRECT_URI, context.getClientSession().getRedirectUri())
.detail(Details.AUTH_METHOD, context.getClientSession().getAuthMethod());
String authType = context.getClientSession().getNote(Details.AUTH_TYPE);
context.getEvent().client(context.getLoginSession().getClient().getClientId())
.detail(Details.REDIRECT_URI, context.getLoginSession().getRedirectUri())
.detail(Details.AUTH_METHOD, context.getLoginSession().getProtocol());
String authType = context.getLoginSession().getNote(Details.AUTH_TYPE);
if (authType != null) {
context.getEvent().detail(Details.AUTH_TYPE, authType);
}

View file

@ -88,8 +88,8 @@ public class UpdatePassword implements RequiredActionProvider, RequiredActionFac
String passwordConfirm = formData.getFirst("password-confirm");
EventBuilder errorEvent = event.clone().event(EventType.UPDATE_PASSWORD_ERROR)
.client(context.getClientSession().getClient())
.user(context.getClientSession().getUserSession().getUser());
.client(context.getLoginSession().getClient())
.user(context.getLoginSession().getAuthenticatedUser());
if (Validation.isBlank(passwordNew)) {
Response challenge = context.form()

View file

@ -62,6 +62,8 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
return;
}
// TODO:mposolda
/*
context.getEvent().clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, context.getUser().getEmail()).success();
LoginActionsService.createActionCookie(context.getRealm(), context.getUriInfo(), context.getConnection(), context.getUserSession().getId());
@ -73,6 +75,7 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
.setUser(context.getUser());
Response challenge = loginFormsProvider.createResponse(UserModel.RequiredAction.VERIFY_EMAIL);
context.challenge(challenge);
*/
}
@Override

View file

@ -38,6 +38,7 @@ import javax.ws.rs.core.Response;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
import org.keycloak.authorization.admin.representation.PolicyEvaluationResponseBuilder;
@ -55,7 +56,6 @@ import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.authorization.util.Permissions;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
@ -67,6 +67,7 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.Urls;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.sessions.LoginSessionModel;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@ -192,19 +193,13 @@ public class PolicyEvaluationService {
private static class CloseableKeycloakIdentity extends KeycloakIdentity {
private UserSessionModel userSession;
private ClientSessionModel clientSession;
public CloseableKeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession, UserSessionModel userSession, ClientSessionModel clientSession) {
public CloseableKeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession, UserSessionModel userSession) {
super(accessToken, keycloakSession);
this.userSession = userSession;
this.clientSession = clientSession;
}
public void close() {
if (clientSession != null) {
keycloakSession.sessions().removeClientSession(realm, clientSession);
}
if (userSession != null) {
keycloakSession.sessions().removeUserSession(realm, userSession);
}
@ -220,7 +215,7 @@ public class PolicyEvaluationService {
String subject = representation.getUserId();
ClientSessionModel clientSession = null;
ClientLoginSessionModel clientSession = null;
UserSessionModel userSession = null;
if (subject != null) {
UserModel userModel = keycloakSession.users().getUserById(subject, realm);
@ -234,11 +229,11 @@ public class PolicyEvaluationService {
if (clientId != null) {
ClientModel clientModel = realm.getClientById(clientId);
clientSession = keycloakSession.sessions().createClientSession(realm, clientModel);
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
LoginSessionModel loginSession = keycloakSession.loginSessions().createLoginSession(realm, clientModel, false);
loginSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
userSession = keycloakSession.sessions().createUserSession(realm, userModel, userModel.getUsername(), "127.0.0.1", "passwd", false, null, null);
new TokenManager().attachClientSession(userSession, clientSession);
new TokenManager().attachLoginSession(keycloakSession, userSession, loginSession);
Set<RoleModel> requestedRoles = new HashSet<>();
for (String roleId : clientSession.getRoles()) {
@ -276,6 +271,6 @@ public class PolicyEvaluationService {
representation.getRoleIds().forEach(roleName -> realmAccess.addRole(roleName));
}
return new CloseableKeycloakIdentity(accessToken, keycloakSession, userSession, clientSession);
return new CloseableKeycloakIdentity(accessToken, keycloakSession, userSession);
}
}

View file

@ -87,14 +87,14 @@ public class HardcodedUserSessionAttributeMapper extends AbstractIdentityProvide
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(ATTRIBUTE);
String attributeValue = mapperModel.getConfig().get(ATTRIBUTE_VALUE);
context.getClientSession().setUserSessionNote(attribute, attributeValue);
context.getLoginSession().setUserSessionNote(attribute, attributeValue);
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(ATTRIBUTE);
String attributeValue = mapperModel.getConfig().get(ATTRIBUTE_VALUE);
context.getClientSession().setUserSessionNote(attribute, attributeValue);
context.getLoginSession().setUserSessionNote(attribute, attributeValue);
}
@Override

View file

@ -23,8 +23,6 @@ import org.keycloak.authentication.requiredactions.util.UpdateProfileContext;
import org.keycloak.authentication.requiredactions.util.UserUpdateProfileContext;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.common.util.ObjectUtil;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailTemplateProvider;
import org.keycloak.forms.login.LoginFormsPages;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.forms.login.freemarker.model.ClientBean;
@ -39,8 +37,6 @@ import org.keycloak.forms.login.freemarker.model.RequiredActionUrlFormatterMetho
import org.keycloak.forms.login.freemarker.model.TotpBean;
import org.keycloak.forms.login.freemarker.model.UrlBean;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.Constants;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
@ -50,6 +46,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.services.Urls;
import org.keycloak.services.messages.Messages;
import org.keycloak.sessions.LoginSessionModel;
import org.keycloak.theme.BrowserSecurityHeaderSetup;
import org.keycloak.theme.FreeMarkerException;
import org.keycloak.theme.FreeMarkerUtil;
@ -77,7 +74,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -106,7 +102,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
private UserModel user;
private ClientSessionModel clientSession;
private LoginSessionModel loginSession;
private final Map<String, Object> attributes = new HashMap<String, Object>();
public FreeMarkerLoginFormsProvider(KeycloakSession session, FreeMarkerUtil freeMarker) {
@ -145,10 +141,11 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
page = LoginFormsPages.LOGIN_UPDATE_PASSWORD;
break;
case VERIFY_EMAIL:
try {
// TODO:mposolda It should be also clientSession (actionTicket) involved here. Not just loginSession
/*try {
UriBuilder builder = Urls.loginActionEmailVerificationBuilder(uriInfo.getBaseUri());
builder.queryParam(OAuth2Constants.CODE, accessCode);
builder.queryParam(Constants.KEY, clientSession.getNote(Constants.VERIFY_EMAIL_KEY));
builder.queryParam(Constants.KEY, loginSession.getNote(Constants.VERIFY_EMAIL_KEY));
String link = builder.build(realm.getName()).toString();
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
@ -157,7 +154,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
} catch (EmailException e) {
logger.error("Failed to send verification email", e);
return setError(Messages.EMAIL_SENT_ERROR).createErrorPage();
}
}*/
actionMessage = Messages.VERIFY_EMAIL;
page = LoginFormsPages.LOGIN_VERIFY_EMAIL;
@ -298,7 +295,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
attributes.put("register", new RegisterBean(formData));
break;
case OAUTH_GRANT:
attributes.put("oauth", new OAuthGrantBean(accessCode, clientSession, client, realmRolesRequested, resourceRolesRequested, protocolMappersRequested, this.accessRequestMessage));
attributes.put("oauth", new OAuthGrantBean(accessCode, client, realmRolesRequested, resourceRolesRequested, protocolMappersRequested, this.accessRequestMessage));
attributes.put("advancedMsg", new AdvancedMessageFormatterMethod(locale, messagesBundle));
break;
case CODE:
@ -485,8 +482,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
}
@Override
public Response createOAuthGrant(ClientSessionModel clientSession) {
this.clientSession = clientSession;
public Response createOAuthGrant() {
return createResponse(LoginFormsPages.OAUTH_GRANT);
}
@ -593,8 +589,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
}
@Override
public LoginFormsProvider setClientSession(ClientSessionModel clientSession) {
this.clientSession = clientSession;
public LoginFormsProvider setLoginSession(LoginSessionModel loginSession) {
this.loginSession = loginSession;
return this;
}

View file

@ -18,7 +18,6 @@ package org.keycloak.forms.login.freemarker.model;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RoleModel;
@ -38,7 +37,7 @@ public class OAuthGrantBean {
private ClientModel client;
private List<String> claimsRequested;
public OAuthGrantBean(String code, ClientSessionModel clientSession, ClientModel client, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested,
public OAuthGrantBean(String code, ClientModel client, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested,
List<ProtocolMapperModel> protocolMappersRequested, String accessRequestMessage) {
this.code = code;
this.client = client;

View file

@ -30,6 +30,7 @@ import org.keycloak.protocol.LoginProtocol.Error;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.sessions.LoginSessionModel;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
@ -63,9 +64,9 @@ public abstract class AuthorizationEndpointBase {
this.event = event;
}
protected AuthenticationProcessor createProcessor(ClientSessionModel clientSession, String flowId, String flowPath) {
protected AuthenticationProcessor createProcessor(LoginSessionModel loginSession, String flowId, String flowPath) {
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)
processor.setLoginSession(loginSession)
.setFlowPath(flowPath)
.setFlowId(flowId)
.setBrowserFlow(true)
@ -81,42 +82,45 @@ public abstract class AuthorizationEndpointBase {
/**
* Common method to handle browser authentication request in protocols unified way.
*
* @param clientSession for current request
* @param loginSession for current request
* @param protocol handler for protocol used to initiate login
* @param isPassive set to true if login should be passive (without login screen shown)
* @param redirectToAuthentication if true redirect to flow url. If initial call to protocol is a POST, you probably want to do this. This is so we can disable the back button on browser
* @return response to be returned to the browser
*/
protected Response handleBrowserAuthenticationRequest(ClientSessionModel clientSession, LoginProtocol protocol, boolean isPassive, boolean redirectToAuthentication) {
protected Response handleBrowserAuthenticationRequest(LoginSessionModel loginSession, LoginProtocol protocol, boolean isPassive, boolean redirectToAuthentication) {
AuthenticationFlowModel flow = getAuthenticationFlow();
String flowId = flow.getId();
AuthenticationProcessor processor = createProcessor(clientSession, flowId, LoginActionsService.AUTHENTICATE_PATH);
event.detail(Details.CODE_ID, clientSession.getId());
AuthenticationProcessor processor = createProcessor(loginSession, flowId, LoginActionsService.AUTHENTICATE_PATH);
event.detail(Details.CODE_ID, loginSession.getId());
if (isPassive) {
// OIDC prompt == NONE or SAML 2 IsPassive flag
// This means that client is just checking if the user is already completely logged in.
// We cancel login if any authentication action or required action is required
try {
if (processor.authenticateOnly() == null) {
processor.attachSession();
// processor.attachSession();
} else {
Response response = protocol.sendError(clientSession, Error.PASSIVE_LOGIN_REQUIRED);
session.sessions().removeClientSession(realm, clientSession);
Response response = protocol.sendError(loginSession, Error.PASSIVE_LOGIN_REQUIRED);
session.loginSessions().removeLoginSession(realm, loginSession);
return response;
}
if (processor.isActionRequired()) {
Response response = protocol.sendError(clientSession, Error.PASSIVE_INTERACTION_REQUIRED);
session.sessions().removeClientSession(realm, clientSession);
Response response = protocol.sendError(loginSession, Error.PASSIVE_INTERACTION_REQUIRED);
session.loginSessions().removeLoginSession(realm, loginSession);
return response;
}
// Attach session once no requiredActions or other things are required
processor.attachSession();
} catch (Exception e) {
return processor.handleBrowserException(e);
}
return processor.finishAuthentication(protocol);
} else {
try {
RestartLoginCookie.setRestartCookie(session, realm, clientConnection, uriInfo, clientSession);
// TODO: Check if this is required...
RestartLoginCookie.setRestartCookie(session, realm, clientConnection, uriInfo, loginSession);
if (redirectToAuthentication) {
return processor.redirectToFlow();
}

View file

@ -31,6 +31,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.util.CookieHelper;
import org.keycloak.sessions.LoginSessionModel;
import javax.crypto.SecretKey;
import javax.ws.rs.core.Cookie;
@ -125,10 +126,10 @@ public class RestartLoginCookie {
public RestartLoginCookie() {
}
public RestartLoginCookie(ClientSessionModel clientSession) {
public RestartLoginCookie(LoginSessionModel clientSession) {
this.action = clientSession.getAction();
this.clientId = clientSession.getClient().getClientId();
this.authMethod = clientSession.getAuthMethod();
this.authMethod = clientSession.getProtocol();
this.redirectUri = clientSession.getRedirectUri();
this.clientSession = clientSession.getId();
for (Map.Entry<String, String> entry : clientSession.getNotes().entrySet()) {
@ -136,8 +137,8 @@ public class RestartLoginCookie {
}
}
public static void setRestartCookie(KeycloakSession session, RealmModel realm, ClientConnection connection, UriInfo uriInfo, ClientSessionModel clientSession) {
RestartLoginCookie restart = new RestartLoginCookie(clientSession);
public static void setRestartCookie(KeycloakSession session, RealmModel realm, ClientConnection connection, UriInfo uriInfo, LoginSessionModel loginSession) {
RestartLoginCookie restart = new RestartLoginCookie(loginSession);
String encoded = restart.encode(session, realm);
String path = AuthenticationManager.getRealmCookiePath(realm, uriInfo);
boolean secureOnly = realm.getSslRequired().isRequired(connection);
@ -150,6 +151,8 @@ public class RestartLoginCookie {
CookieHelper.addCookie(KC_RESTART, "", path, null, null, 0, secureOnly, true);
}
// TODO:mposolda
/*
public static ClientSessionModel restartSession(KeycloakSession session, RealmModel realm, String code) throws Exception {
Cookie cook = session.getContext().getRequestHeaders().getCookies().get(KC_RESTART);
if (cook == null) {
@ -183,5 +186,5 @@ public class RestartLoginCookie {
}
return clientSession;
}
}*/
}

View file

@ -23,8 +23,8 @@ import org.keycloak.common.util.Time;
import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.ClientLoginSessionModel;
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;
@ -38,6 +38,8 @@ import org.keycloak.services.ServicesLogger;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.sessions.CommonClientSessionModel;
import org.keycloak.sessions.LoginSessionModel;
import org.keycloak.util.TokenUtil;
import javax.ws.rs.core.HttpHeaders;
@ -128,7 +130,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
}
private void setupResponseTypeAndMode(ClientSessionModel clientSession) {
private void setupResponseTypeAndMode(CommonClientSessionModel clientSession) {
String responseType = clientSession.getNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
String responseMode = clientSession.getNote(OIDCLoginProtocol.RESPONSE_MODE_PARAM);
this.responseType = OIDCResponseType.parse(responseType);
@ -169,8 +171,8 @@ public class OIDCLoginProtocol implements LoginProtocol {
@Override
public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
ClientSessionModel clientSession = accessCode.getClientSession();
public Response authenticated(UserSessionModel userSession, ClientSessionCode<ClientLoginSessionModel> accessCode) {
ClientLoginSessionModel clientSession = accessCode.getClientSession();
setupResponseTypeAndMode(clientSession);
String redirect = clientSession.getRedirectUri();
@ -182,7 +184,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
// Standard or hybrid flow
if (responseType.hasResponseType(OIDCResponseType.CODE)) {
accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN.name());
accessCode.setAction(CommonClientSessionModel.Action.CODE_TO_TOKEN.name());
redirectUri.addParam(OAuth2Constants.CODE, accessCode.getCode());
}
@ -227,15 +229,15 @@ public class OIDCLoginProtocol implements LoginProtocol {
@Override
public Response sendError(ClientSessionModel clientSession, Error error) {
setupResponseTypeAndMode(clientSession);
public Response sendError(LoginSessionModel loginSession, Error error) {
setupResponseTypeAndMode(loginSession);
String redirect = clientSession.getRedirectUri();
String state = clientSession.getNote(OIDCLoginProtocol.STATE_PARAM);
String redirect = loginSession.getRedirectUri();
String state = loginSession.getNote(OIDCLoginProtocol.STATE_PARAM);
OIDCRedirectUriBuilder redirectUri = OIDCRedirectUriBuilder.fromUri(redirect, responseMode).addParam(OAuth2Constants.ERROR, translateError(error));
if (state != null)
redirectUri.addParam(OAuth2Constants.STATE, state);
session.sessions().removeClientSession(realm, clientSession);
session.loginSessions().removeLoginSession(realm, loginSession);
RestartLoginCookie.expireRestartCookie(realm, session.getContext().getConnection(), uriInfo);
return redirectUri.build();
}
@ -256,13 +258,13 @@ public class OIDCLoginProtocol implements LoginProtocol {
}
@Override
public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
public void backchannelLogout(UserSessionModel userSession, ClientLoginSessionModel clientSession) {
ClientModel client = clientSession.getClient();
new ResourceAdminManager(session).logoutClientSession(uriInfo.getRequestUri(), realm, client, clientSession);
}
@Override
public Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
public Response frontchannelLogout(UserSessionModel userSession, ClientLoginSessionModel clientSession) {
// todo oidc redirect support
throw new RuntimeException("NOT IMPLEMENTED");
}
@ -289,18 +291,18 @@ public class OIDCLoginProtocol implements LoginProtocol {
@Override
public boolean requireReauthentication(UserSessionModel userSession, ClientSessionModel clientSession) {
return isPromptLogin(clientSession) || isAuthTimeExpired(userSession, clientSession);
public boolean requireReauthentication(UserSessionModel userSession, LoginSessionModel loginSession) {
return isPromptLogin(loginSession) || isAuthTimeExpired(userSession, loginSession);
}
protected boolean isPromptLogin(ClientSessionModel clientSession) {
String prompt = clientSession.getNote(OIDCLoginProtocol.PROMPT_PARAM);
protected boolean isPromptLogin(LoginSessionModel loginSession) {
String prompt = loginSession.getNote(OIDCLoginProtocol.PROMPT_PARAM);
return TokenUtil.hasPrompt(prompt, OIDCLoginProtocol.PROMPT_VALUE_LOGIN);
}
protected boolean isAuthTimeExpired(UserSessionModel userSession, ClientSessionModel clientSession) {
protected boolean isAuthTimeExpired(UserSessionModel userSession, LoginSessionModel loginSession) {
String authTime = userSession.getNote(AuthenticationManager.AUTH_TIME);
String maxAge = clientSession.getNote(OIDCLoginProtocol.MAX_AGE_PARAM);
String maxAge = loginSession.getNote(OIDCLoginProtocol.MAX_AGE_PARAM);
if (maxAge == null) {
return false;
}
@ -310,7 +312,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
if (authTimeInt + maxAgeInt < Time.currentTime()) {
logger.debugf("Authentication time is expired, needs to reauthenticate. userSession=%s, clientId=%s, maxAge=%d, authTime=%d", userSession.getId(),
clientSession.getClient().getId(), maxAgeInt, authTimeInt);
loginSession.getClient().getId(), maxAgeInt, authTimeInt);
return true;
}

View file

@ -31,6 +31,7 @@ import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.jose.jws.crypto.HashProvider;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientTemplateModel;
@ -38,7 +39,6 @@ import org.keycloak.models.GroupModel;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelException;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
@ -60,6 +60,7 @@ import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.sessions.LoginSessionModel;
import org.keycloak.util.TokenUtil;
import org.keycloak.common.util.Time;
@ -107,10 +108,10 @@ public class TokenManager {
public static class TokenValidation {
public final UserModel user;
public final UserSessionModel userSession;
public final ClientSessionModel clientSession;
public final ClientLoginSessionModel clientSession;
public final AccessToken newToken;
public TokenValidation(UserModel user, UserSessionModel userSession, ClientSessionModel clientSession, AccessToken newToken) {
public TokenValidation(UserModel user, UserSessionModel userSession, ClientLoginSessionModel clientSession, AccessToken newToken) {
this.user = user;
this.userSession = userSession;
this.clientSession = clientSession;
@ -129,29 +130,18 @@ public class TokenManager {
}
UserSessionModel userSession = null;
ClientSessionModel clientSession = null;
if (TokenUtil.TOKEN_TYPE_OFFLINE.equals(oldToken.getType())) {
UserSessionManager sessionManager = new UserSessionManager(session);
clientSession = sessionManager.findOfflineClientSession(realm, oldToken.getClientSession());
if (clientSession != null) {
userSession = clientSession.getUserSession();
if (userSession == null) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Offline user session not found", "Offline user session not found");
}
String userSessionId = oldToken.getSessionState();
if (!userSessionId.equals(userSession.getId())) {
throw new ModelException("User session don't match. Offline client session " + clientSession.getId() + ", It's user session " + userSession.getId() +
" Wanted user session: " + userSessionId);
}
userSession = sessionManager.findOfflineUserSession(realm, oldToken.getSessionState());
if (userSession != null) {
// Revoke timeouted offline userSession
if (userSession.getLastSessionRefresh() < Time.currentTime() - realm.getOfflineSessionIdleTimeout()) {
sessionManager.revokeOfflineUserSession(userSession);
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Offline user session not active", "Offline user session session not active");
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Offline session not active", "Offline session not active");
}
}
} else {
// Find userSession regularly for online tokens
@ -160,20 +150,14 @@ public class TokenManager {
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
}
for (ClientSessionModel clientSessionModel : userSession.getClientSessions()) {
if (clientSessionModel.getId().equals(oldToken.getClientSession())) {
clientSession = clientSessionModel;
break;
}
}
}
if (clientSession == null) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Client session not active", "Client session not active");
if (userSession == null) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Offline user session not found", "Offline user session not found");
}
ClientModel client = clientSession.getClient();
ClientModel client = session.getContext().getClient();
ClientLoginSessionModel clientSession = userSession.getClientLoginSessions().get(client.getId());
if (!client.getClientId().equals(oldToken.getIssuedFor())) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Unmatching clients", "Unmatching clients");
@ -221,18 +205,30 @@ public class TokenManager {
}
}
<<<<<<< f392e79ad781014387c9fe5724815b24eab7a35f
userSession = session.sessions().getOfflineUserSession(realm, token.getSessionState());
if (AuthenticationManager.isOfflineSessionValid(realm, userSession)) {
ClientSessionModel clientSession = session.sessions().getOfflineClientSession(realm, token.getClientSession());
if (clientSession != null) {
return true;
}
=======
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
if (client == null || !client.isEnabled()) {
return false;
}
ClientLoginSessionModel clientSession = userSession.getClientLoginSessions().get(client.getId());
if (clientSession == null) {
return false;
>>>>>>> KEYCLOAK-4626 AuthenticationSessions: start
}
return false;
}
public RefreshResult refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel authorizedClient, String encodedRefreshToken, EventBuilder event, HttpHeaders headers) throws OAuthErrorException {
public RefreshResult refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel authorizedClient,
String encodedRefreshToken, EventBuilder event, HttpHeaders headers) throws OAuthErrorException {
RefreshToken refreshToken = verifyRefreshToken(session, realm, encodedRefreshToken);
event.user(refreshToken.getSubject()).session(refreshToken.getSessionState())
@ -349,7 +345,8 @@ public class TokenManager {
}
}
public AccessToken createClientAccessToken(KeycloakSession session, Set<RoleModel> requestedRoles, RealmModel realm, ClientModel client, UserModel user, UserSessionModel userSession, ClientSessionModel clientSession) {
public AccessToken createClientAccessToken(KeycloakSession session, Set<RoleModel> requestedRoles, RealmModel realm, ClientModel client, UserModel user, UserSessionModel userSession,
ClientLoginSessionModel clientSession) {
AccessToken token = initToken(realm, client, user, userSession, clientSession, session.getContext().getUri());
for (RoleModel role : requestedRoles) {
addComposites(token, role);
@ -358,17 +355,14 @@ public class TokenManager {
return token;
}
public static void attachClientSession(UserSessionModel session, ClientSessionModel clientSession) {
if (clientSession.getUserSession() != null) {
return;
}
public static ClientLoginSessionModel attachLoginSession(KeycloakSession session, UserSessionModel userSession, LoginSessionModel loginSession) {
UserModel user = userSession.getUser();
ClientModel client = loginSession.getClient();
ClientLoginSessionModel clientSession = session.sessions().createClientSession(userSession.getRealm(), client, userSession);
UserModel user = session.getUser();
clientSession.setUserSession(session);
Set<String> requestedRoles = new HashSet<String>();
// todo scope param protocol independent
String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
ClientModel client = clientSession.getClient();
String scopeParam = loginSession.getNote(OAuth2Constants.SCOPE);
for (RoleModel r : TokenManager.getAccess(scopeParam, true, client, user)) {
requestedRoles.add(r.getId());
}
@ -378,28 +372,41 @@ public class TokenManager {
ClientTemplateModel clientTemplate = client.getClientTemplate();
if (clientTemplate != null && client.useTemplateMappers()) {
for (ProtocolMapperModel protocolMapper : clientTemplate.getProtocolMappers()) {
if (protocolMapper.getProtocol().equals(clientSession.getAuthMethod())) {
if (protocolMapper.getProtocol().equals(loginSession.getProtocol())) {
requestedProtocolMappers.add(protocolMapper.getId());
}
}
}
for (ProtocolMapperModel protocolMapper : client.getProtocolMappers()) {
if (protocolMapper.getProtocol().equals(clientSession.getAuthMethod())) {
if (protocolMapper.getProtocol().equals(loginSession.getProtocol())) {
requestedProtocolMappers.add(protocolMapper.getId());
}
}
clientSession.setProtocolMappers(requestedProtocolMappers);
Map<String, String> transferredNotes = clientSession.getUserSessionNotes();
Map<String, String> transferredNotes = loginSession.getNotes();
for (Map.Entry<String, String> entry : transferredNotes.entrySet()) {
session.setNote(entry.getKey(), entry.getValue());
clientSession.setNote(entry.getKey(), entry.getValue());
}
Map<String, String> transferredUserSessionNotes = loginSession.getUserSessionNotes();
for (Map.Entry<String, String> entry : transferredUserSessionNotes.entrySet()) {
userSession.setNote(entry.getKey(), entry.getValue());
}
clientSession.setTimestamp(Time.currentTime());
userSession.getClientLoginSessions().put(client.getId(), clientSession);
// Remove login session now
session.loginSessions().removeLoginSession(userSession.getRealm(), loginSession);
return clientSession;
}
public static void dettachClientSession(UserSessionProvider sessions, RealmModel realm, ClientSessionModel clientSession) {
public static void dettachClientSession(UserSessionProvider sessions, RealmModel realm, ClientLoginSessionModel clientSession) {
UserSessionModel userSession = clientSession.getUserSession();
if (userSession == null) {
return;
@ -543,8 +550,8 @@ public class TokenManager {
}
public AccessToken transformAccessToken(KeycloakSession session, AccessToken token, RealmModel realm, ClientModel client, UserModel user,
UserSessionModel userSession, ClientSessionModel clientSession) {
Set<ProtocolMapperModel> mappings = new ClientSessionCode(session, realm, clientSession).getRequestedProtocolMappers();
UserSessionModel userSession, ClientLoginSessionModel clientSession) {
Set<ProtocolMapperModel> mappings = ClientSessionCode.getRequestedProtocolMappers(clientSession.getProtocolMappers(), client);
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (ProtocolMapperModel mapping : mappings) {
@ -558,8 +565,8 @@ public class TokenManager {
}
public AccessToken transformUserInfoAccessToken(KeycloakSession session, AccessToken token, RealmModel realm, ClientModel client, UserModel user,
UserSessionModel userSession, ClientSessionModel clientSession) {
Set<ProtocolMapperModel> mappings = new ClientSessionCode(session, realm, clientSession).getRequestedProtocolMappers();
UserSessionModel userSession, ClientLoginSessionModel clientSession) {
Set<ProtocolMapperModel> mappings = ClientSessionCode.getRequestedProtocolMappers(clientSession.getProtocolMappers(), client);
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (ProtocolMapperModel mapping : mappings) {
@ -573,8 +580,8 @@ public class TokenManager {
}
public void transformIDToken(KeycloakSession session, IDToken token, RealmModel realm, ClientModel client, UserModel user,
UserSessionModel userSession, ClientSessionModel clientSession) {
Set<ProtocolMapperModel> mappings = new ClientSessionCode(session, realm, clientSession).getRequestedProtocolMappers();
UserSessionModel userSession, ClientLoginSessionModel clientSession) {
Set<ProtocolMapperModel> mappings = ClientSessionCode.getRequestedProtocolMappers(clientSession.getProtocolMappers(), client);
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (ProtocolMapperModel mapping : mappings) {
@ -585,9 +592,9 @@ public class TokenManager {
}
}
protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel user, UserSessionModel session, ClientSessionModel clientSession, UriInfo uriInfo) {
protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel user, UserSessionModel session, ClientLoginSessionModel clientSession, UriInfo uriInfo) {
AccessToken token = new AccessToken();
if (clientSession != null) token.clientSession(clientSession.getId());
token.clientSession(clientSession.getId());
token.id(KeycloakModelUtils.generateId());
token.type(TokenUtil.TOKEN_TYPE_BEARER);
token.subject(user.getId());
@ -607,9 +614,9 @@ public class TokenManager {
token.setAuthTime(Integer.parseInt(authTime));
}
if (session != null) {
token.setSessionState(session.getId());
}
token.setSessionState(session.getId());
int tokenLifespan = getTokenLifespan(realm, clientSession);
if (tokenLifespan > 0) {
token.expiration(Time.currentTime() + tokenLifespan);
@ -621,7 +628,7 @@ public class TokenManager {
return token;
}
private int getTokenLifespan(RealmModel realm, ClientSessionModel clientSession) {
private int getTokenLifespan(RealmModel realm, ClientLoginSessionModel clientSession) {
boolean implicitFlow = false;
String responseType = clientSession.getNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
if (responseType != null) {
@ -663,7 +670,7 @@ public class TokenManager {
return new JWSBuilder().type(JWT).kid(activeRsaKey.getKid()).jsonContent(token).sign(jwsAlgorithm, activeRsaKey.getPrivateKey());
}
public AccessTokenResponseBuilder responseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public AccessTokenResponseBuilder responseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
return new AccessTokenResponseBuilder(realm, client, event, session, userSession, clientSession);
}
@ -673,7 +680,7 @@ public class TokenManager {
EventBuilder event;
KeycloakSession session;
UserSessionModel userSession;
ClientSessionModel clientSession;
ClientLoginSessionModel clientSession;
AccessToken accessToken;
RefreshToken refreshToken;
@ -682,7 +689,7 @@ public class TokenManager {
boolean generateAccessTokenHash = false;
String codeHash;
public AccessTokenResponseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public AccessTokenResponseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
this.realm = realm;
this.client = client;
this.event = event;

View file

@ -44,6 +44,7 @@ import org.keycloak.services.Urls;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.services.util.CacheControlUtil;
import org.keycloak.sessions.LoginSessionModel;
import org.keycloak.util.TokenUtil;
import javax.ws.rs.GET;
@ -63,12 +64,12 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
public static final String CODE_AUTH_TYPE = "code";
/**
* Prefix used to store additional HTTP GET params from original client request into {@link ClientSessionModel} note to be available later in Authenticators, RequiredActions etc. Prefix is used to
* Prefix used to store additional HTTP GET params from original client request into {@link LoginSessionModel} note to be available later in Authenticators, RequiredActions etc. Prefix is used to
* prevent collisions with internally used notes.
*
* @see ClientSessionModel#getNote(String)
* @see LoginSessionModel#getNote(String)
*/
public static final String CLIENT_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX = "client_request_param_";
public static final String LOGIN_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX = "client_request_param_";
// https://tools.ietf.org/html/rfc7636#section-4.2
private static final Pattern VALID_CODE_CHALLENGE_PATTERN = Pattern.compile("^[0-9a-zA-Z\\-\\.~_]+$");
@ -78,7 +79,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
}
private ClientModel client;
private ClientSessionModel clientSession;
private LoginSessionModel loginSession;
private Action action;
private OIDCResponseType parsedResponseType;
@ -125,7 +126,8 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
return errorResponse;
}
createClientSession();
createLoginSession();
// So back button doesn't work
CacheControlUtil.noBackButtonCacheControlHeader();
switch (action) {
@ -356,44 +358,44 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
}
}
private void createClientSession() {
clientSession = session.sessions().createClientSession(realm, client);
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
clientSession.setRedirectUri(redirectUri);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
clientSession.setNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, request.getResponseType());
clientSession.setNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, request.getRedirectUriParam());
clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
private void createLoginSession() {
loginSession = session.loginSessions().createLoginSession(realm, client, true);
loginSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
loginSession.setRedirectUri(redirectUri);
loginSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
loginSession.setNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, request.getResponseType());
loginSession.setNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, request.getRedirectUriParam());
loginSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
if (request.getState() != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, request.getState());
if (request.getNonce() != null) clientSession.setNote(OIDCLoginProtocol.NONCE_PARAM, request.getNonce());
if (request.getMaxAge() != null) clientSession.setNote(OIDCLoginProtocol.MAX_AGE_PARAM, String.valueOf(request.getMaxAge()));
if (request.getScope() != null) clientSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, request.getScope());
if (request.getLoginHint() != null) clientSession.setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, request.getLoginHint());
if (request.getPrompt() != null) clientSession.setNote(OIDCLoginProtocol.PROMPT_PARAM, request.getPrompt());
if (request.getIdpHint() != null) clientSession.setNote(AdapterConstants.KC_IDP_HINT, request.getIdpHint());
if (request.getResponseMode() != null) clientSession.setNote(OIDCLoginProtocol.RESPONSE_MODE_PARAM, request.getResponseMode());
if (request.getState() != null) loginSession.setNote(OIDCLoginProtocol.STATE_PARAM, request.getState());
if (request.getNonce() != null) loginSession.setNote(OIDCLoginProtocol.NONCE_PARAM, request.getNonce());
if (request.getMaxAge() != null) loginSession.setNote(OIDCLoginProtocol.MAX_AGE_PARAM, String.valueOf(request.getMaxAge()));
if (request.getScope() != null) loginSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, request.getScope());
if (request.getLoginHint() != null) loginSession.setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, request.getLoginHint());
if (request.getPrompt() != null) loginSession.setNote(OIDCLoginProtocol.PROMPT_PARAM, request.getPrompt());
if (request.getIdpHint() != null) loginSession.setNote(AdapterConstants.KC_IDP_HINT, request.getIdpHint());
if (request.getResponseMode() != null) loginSession.setNote(OIDCLoginProtocol.RESPONSE_MODE_PARAM, request.getResponseMode());
// https://tools.ietf.org/html/rfc7636#section-4
if (request.getCodeChallenge() != null) clientSession.setNote(OIDCLoginProtocol.CODE_CHALLENGE_PARAM, request.getCodeChallenge());
if (request.getCodeChallenge() != null) loginSession.setNote(OIDCLoginProtocol.CODE_CHALLENGE_PARAM, request.getCodeChallenge());
if (request.getCodeChallengeMethod() != null) {
clientSession.setNote(OIDCLoginProtocol.CODE_CHALLENGE_METHOD_PARAM, request.getCodeChallengeMethod());
loginSession.setNote(OIDCLoginProtocol.CODE_CHALLENGE_METHOD_PARAM, request.getCodeChallengeMethod());
} else {
clientSession.setNote(OIDCLoginProtocol.CODE_CHALLENGE_METHOD_PARAM, OIDCLoginProtocol.PKCE_METHOD_PLAIN);
loginSession.setNote(OIDCLoginProtocol.CODE_CHALLENGE_METHOD_PARAM, OIDCLoginProtocol.PKCE_METHOD_PLAIN);
}
if (request.getAdditionalReqParams() != null) {
for (String paramName : request.getAdditionalReqParams().keySet()) {
clientSession.setNote(CLIENT_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX + paramName, request.getAdditionalReqParams().get(paramName));
loginSession.setNote(LOGIN_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX + paramName, request.getAdditionalReqParams().get(paramName));
}
}
}
private Response buildAuthorizationCodeAuthorizationResponse() {
this.event.event(EventType.LOGIN);
clientSession.setNote(Details.AUTH_TYPE, CODE_AUTH_TYPE);
loginSession.setNote(Details.AUTH_TYPE, CODE_AUTH_TYPE);
return handleBrowserAuthenticationRequest(clientSession, new OIDCLoginProtocol(session, realm, uriInfo, headers, event), TokenUtil.hasPrompt(request.getPrompt(), OIDCLoginProtocol.PROMPT_VALUE_NONE), false);
return handleBrowserAuthenticationRequest(loginSession, new OIDCLoginProtocol(session, realm, uriInfo, headers, event), TokenUtil.hasPrompt(request.getPrompt(), OIDCLoginProtocol.PROMPT_VALUE_NONE), false);
}
private Response buildRegister() {
@ -402,7 +404,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
AuthenticationFlowModel flow = realm.getRegistrationFlow();
String flowId = flow.getId();
AuthenticationProcessor processor = createProcessor(clientSession, flowId, LoginActionsService.REGISTRATION_PATH);
AuthenticationProcessor processor = createProcessor(loginSession, flowId, LoginActionsService.REGISTRATION_PATH);
return processor.authenticate();
}
@ -413,7 +415,7 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
AuthenticationFlowModel flow = realm.getResetCredentialsFlow();
String flowId = flow.getId();
AuthenticationProcessor processor = createProcessor(clientSession, flowId, LoginActionsService.RESET_CREDENTIALS_PATH);
AuthenticationProcessor processor = createProcessor(loginSession, flowId, LoginActionsService.RESET_CREDENTIALS_PATH);
return processor.authenticate();
}

View file

@ -32,13 +32,12 @@ import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientLoginSessionModel;
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.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
@ -52,6 +51,7 @@ import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.sessions.LoginSessionModel;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
@ -62,7 +62,6 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -208,29 +207,37 @@ public class TokenEndpoint {
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Missing parameter: " + OAuth2Constants.CODE, Response.Status.BAD_REQUEST);
}
ClientSessionCode.ParseResult parseResult = ClientSessionCode.parseResult(code, session, realm);
if (parseResult.isClientSessionNotFound() || parseResult.isIllegalHash()) {
ClientSessionCode.ParseResult<ClientLoginSessionModel> parseResult = ClientSessionCode.parseResult(code, session, realm, ClientLoginSessionModel.class);
if (parseResult.isLoginSessionNotFound() || parseResult.isIllegalHash()) {
String[] parts = code.split("\\.");
if (parts.length == 2) {
event.detail(Details.CODE_ID, parts[1]);
}
event.error(Errors.INVALID_CODE);
if (parseResult.getClientSession() != null) {
session.sessions().removeClientSession(realm, parseResult.getClientSession());
// Attempt to use same code twice should invalidate existing clientSession
ClientLoginSessionModel clientSession = parseResult.getClientSession();
if (clientSession != null) {
UserSessionModel userSession = clientSession.getUserSession();
String clientUUID = clientSession.getClient().getId();
userSession.getClientLoginSessions().remove(clientUUID);
}
throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT, "Code not valid", Response.Status.BAD_REQUEST);
}
ClientSessionModel clientSession = parseResult.getClientSession();
ClientLoginSessionModel clientSession = parseResult.getClientSession();
event.detail(Details.CODE_ID, clientSession.getId());
if (!parseResult.getCode().isValid(ClientSessionModel.Action.CODE_TO_TOKEN.name(), ClientSessionCode.ActionType.CLIENT)) {
if (!parseResult.getCode().isValid(ClientLoginSessionModel.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
parseResult.getCode().setAction(null);
// TODO: Maybe rather create userSession even at this stage? Not sure...
UserSessionModel userSession = clientSession.getUserSession();
if (userSession == null) {
@ -355,7 +362,8 @@ public class TokenEndpoint {
if (!result.isOfflineToken()) {
UserSessionModel userSession = session.sessions().getUserSession(realm, res.getSessionState());
updateClientSessions(userSession.getClientSessions());
ClientLoginSessionModel clientSession = userSession.getClientLoginSessions().get(client.getId());
updateClientSession(clientSession);
updateUserSessionFromClientAuth(userSession);
}
@ -369,7 +377,7 @@ public class TokenEndpoint {
return Cors.add(request, Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).auth().allowedOrigins(uriInfo, client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
}
private void updateClientSession(ClientSessionModel clientSession) {
private void updateClientSession(ClientLoginSessionModel clientSession) {
if(clientSession == null) {
ServicesLogger.LOGGER.clientSessionNull();
@ -388,26 +396,6 @@ public class TokenEndpoint {
}
}
private void updateClientSessions(List<ClientSessionModel> clientSessions) {
if(clientSessions == null) {
ServicesLogger.LOGGER.clientSessionNull();
return;
}
for (ClientSessionModel clientSession : clientSessions) {
if(clientSession == null) {
ServicesLogger.LOGGER.clientSessionNull();
continue;
}
if(clientSession.getClient() == null) {
ServicesLogger.LOGGER.clientModelNull();
continue;
}
if(client.getId().equals(clientSession.getClient().getId())) {
updateClientSession(clientSession);
}
}
}
private void updateUserSessionFromClientAuth(UserSessionModel userSession) {
for (Map.Entry<String, String> attr : clientAuthAttributes.entrySet()) {
userSession.setNote(attr.getKey(), attr.getValue());
@ -428,17 +416,16 @@ public class TokenEndpoint {
}
String scope = formParams.getFirst(OAuth2Constants.SCOPE);
UserSessionProvider sessions = session.sessions();
ClientSessionModel clientSession = sessions.createClientSession(realm, client);
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
clientSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
LoginSessionModel loginSession = session.loginSessions().createLoginSession(realm, client, false);
loginSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
loginSession.setAction(ClientLoginSessionModel.Action.AUTHENTICATE.name());
loginSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
loginSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
AuthenticationFlowModel flow = realm.getDirectGrantFlow();
String flowId = flow.getId();
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)
processor.setLoginSession(loginSession)
.setFlowId(flowId)
.setConnection(clientConnection)
.setEventBuilder(event)
@ -449,13 +436,13 @@ public class TokenEndpoint {
Response challenge = processor.authenticateOnly();
if (challenge != null) return challenge;
processor.evaluateRequiredActionTriggers();
UserModel user = clientSession.getAuthenticatedUser();
UserModel user = loginSession.getAuthenticatedUser();
if (user.getRequiredActions() != null && user.getRequiredActions().size() > 0) {
event.error(Errors.RESOLVE_REQUIRED_ACTIONS);
throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT, "Account is not fully set up", Response.Status.BAD_REQUEST);
}
processor.attachSession();
ClientLoginSessionModel clientSession = processor.attachSession();
UserSessionModel userSession = processor.getUserSession();
updateUserSessionFromClientAuth(userSession);
@ -505,17 +492,15 @@ public class TokenEndpoint {
String scope = formParams.getFirst(OAuth2Constants.SCOPE);
UserSessionProvider sessions = session.sessions();
LoginSessionModel loginSession = session.loginSessions().createLoginSession(realm, client, false);
loginSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
loginSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
loginSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
ClientSessionModel clientSession = sessions.createClientSession(realm, client);
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
clientSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
UserSessionModel userSession = sessions.createUserSession(realm, clientUser, clientUsername, clientConnection.getRemoteAddr(), ServiceAccountConstants.CLIENT_AUTH, false, null, null);
UserSessionModel userSession = session.sessions().createUserSession(realm, clientUser, clientUsername, clientConnection.getRemoteAddr(), ServiceAccountConstants.CLIENT_AUTH, false, null, null);
event.session(userSession);
TokenManager.attachClientSession(userSession, clientSession);
ClientLoginSessionModel clientSession = TokenManager.attachLoginSession(session, userSession, loginSession);
// Notes about client details
userSession.setNote(ServiceAccountConstants.CLIENT_ID, client.getClientId());

View file

@ -29,6 +29,7 @@ import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.jose.jws.Algorithm;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;

View file

@ -18,7 +18,7 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.Config;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ProtocolMapperModel;
@ -61,7 +61,7 @@ public abstract class AbstractOIDCProtocolMapper implements ProtocolMapper {
}
public AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession) {
UserSessionModel userSession, ClientLoginSessionModel clientSession) {
if (!OIDCAttributeMapperHelper.includeInUserInfo(mappingModel)) {
return token;
@ -72,7 +72,7 @@ public abstract class AbstractOIDCProtocolMapper implements ProtocolMapper {
}
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession) {
UserSessionModel userSession, ClientLoginSessionModel clientSession) {
if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)){
return token;
@ -83,7 +83,7 @@ public abstract class AbstractOIDCProtocolMapper implements ProtocolMapper {
}
public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession) {
UserSessionModel userSession, ClientLoginSessionModel clientSession) {
if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)){
return token;

View file

@ -1,7 +1,7 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperContainerModel;
import org.keycloak.models.ProtocolMapperModel;
@ -64,19 +64,19 @@ public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapp
}
@Override
public final IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public final IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
setSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId()));
return token;
}
@Override
public final AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public final AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
setSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId()));
return token;
}
@Override
public final AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public final AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
setSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId()));
return token;
}

View file

@ -17,14 +17,11 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import java.util.ArrayList;

View file

@ -17,15 +17,12 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import java.util.ArrayList;

View file

@ -17,13 +17,10 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import java.util.ArrayList;

View file

@ -17,7 +17,7 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@ -82,7 +82,7 @@ public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAcc
@Override
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession) {
UserSessionModel userSession, ClientLoginSessionModel clientSession) {
String role = mappingModel.getConfig().get(ROLE_CONFIG);
String[] scopedRole = KeycloakModelUtils.parseRole(role);

View file

@ -17,7 +17,7 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@ -30,5 +30,5 @@ import org.keycloak.representations.AccessToken;
public interface OIDCAccessTokenMapper {
AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession);
UserSessionModel userSession, ClientLoginSessionModel clientSession);
}

View file

@ -17,7 +17,7 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@ -30,5 +30,5 @@ import org.keycloak.representations.IDToken;
public interface OIDCIDTokenMapper {
IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession);
UserSessionModel userSession, ClientLoginSessionModel clientSession);
}

View file

@ -17,7 +17,7 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@ -25,7 +25,6 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import java.util.ArrayList;
import java.util.HashMap;
@ -90,7 +89,7 @@ public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
@Override
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession) {
UserSessionModel userSession, ClientLoginSessionModel clientSession) {
String role = mappingModel.getConfig().get(ROLE_CONFIG);
String newName = mappingModel.getConfig().get(NEW_ROLE_NAME);

View file

@ -17,15 +17,12 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import java.util.ArrayList;

View file

@ -17,7 +17,7 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@ -29,5 +29,5 @@ import org.keycloak.representations.AccessToken;
public interface UserInfoTokenMapper {
AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession);
UserSessionModel userSession, ClientLoginSessionModel clientSession);
}

View file

@ -17,14 +17,11 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import java.util.ArrayList;

View file

@ -17,14 +17,11 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import java.util.ArrayList;

View file

@ -30,8 +30,8 @@ import org.keycloak.dom.saml.v2.assertion.AssertionType;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.protocol.ResponseType;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
@ -40,7 +40,6 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.RestartLoginCookie;
import org.keycloak.protocol.saml.mappers.SAMLAttributeStatementMapper;
import org.keycloak.protocol.saml.mappers.SAMLLoginResponseMapper;
import org.keycloak.protocol.saml.mappers.SAMLRoleListMapper;
@ -61,6 +60,8 @@ import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.sessions.CommonClientSessionModel;
import org.keycloak.sessions.LoginSessionModel;
import org.w3c.dom.Document;
import javax.ws.rs.core.HttpHeaders;
@ -156,9 +157,9 @@ public class SamlProtocol implements LoginProtocol {
}
@Override
public Response sendError(ClientSessionModel clientSession, Error error) {
public Response sendError(LoginSessionModel loginSession, Error error) {
try {
ClientModel client = clientSession.getClient();
ClientModel client = loginSession.getClient();
if ("true".equals(client.getAttribute(SAML_IDP_INITIATED_LOGIN))) {
if (error == Error.CANCELLED_BY_USER) {
@ -173,9 +174,9 @@ public class SamlProtocol implements LoginProtocol {
return ErrorPage.error(session, translateErrorToIdpInitiatedErrorMessage(error));
}
} else {
SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder().destination(clientSession.getRedirectUri()).issuer(getResponseIssuer(realm)).status(translateErrorToSAMLStatus(error).get());
SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder().destination(loginSession.getRedirectUri()).issuer(getResponseIssuer(realm)).status(translateErrorToSAMLStatus(error).get());
try {
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder().relayState(clientSession.getNote(GeneralConstants.RELAY_STATE));
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder().relayState(loginSession.getNote(GeneralConstants.RELAY_STATE));
SamlClient samlClient = new SamlClient(client);
KeyManager keyManager = session.keys();
if (samlClient.requiresRealmSignature()) {
@ -198,22 +199,23 @@ public class SamlProtocol implements LoginProtocol {
binding.encrypt(publicKey);
}
Document document = builder.buildDocument();
return buildErrorResponse(clientSession, binding, document);
return buildErrorResponse(loginSession, binding, document);
} catch (Exception e) {
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
}
}
} finally {
RestartLoginCookie.expireRestartCookie(realm, session.getContext().getConnection(), uriInfo);
session.sessions().removeClientSession(realm, clientSession);
// TODO:mposolda
//RestartLoginCookie.expireRestartCookie(realm, session.getContext().getConnection(), uriInfo);
session.loginSessions().removeLoginSession(realm, loginSession);
}
}
protected Response buildErrorResponse(ClientSessionModel clientSession, JaxrsSAML2BindingBuilder binding, Document document) throws ConfigurationException, ProcessingException, IOException {
if (isPostBinding(clientSession)) {
return binding.postBinding(document).response(clientSession.getRedirectUri());
protected Response buildErrorResponse(LoginSessionModel loginSession, JaxrsSAML2BindingBuilder binding, Document document) throws ConfigurationException, ProcessingException, IOException {
if (isPostBinding(loginSession)) {
return binding.postBinding(document).response(loginSession.getRedirectUri());
} else {
return binding.redirectBinding(document).response(clientSession.getRedirectUri());
return binding.redirectBinding(document).response(loginSession.getRedirectUri());
}
}
@ -248,10 +250,10 @@ public class SamlProtocol implements LoginProtocol {
return RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString();
}
protected boolean isPostBinding(ClientSessionModel clientSession) {
ClientModel client = clientSession.getClient();
protected boolean isPostBinding(CommonClientSessionModel loginSession) {
ClientModel client = loginSession.getClient();
SamlClient samlClient = new SamlClient(client);
return SamlProtocol.SAML_POST_BINDING.equals(clientSession.getNote(SamlProtocol.SAML_BINDING)) || samlClient.forcePostBinding();
return SamlProtocol.SAML_POST_BINDING.equals(loginSession.getNote(SamlProtocol.SAML_BINDING)) || samlClient.forcePostBinding();
}
public static boolean isLogoutPostBindingForInitiator(UserSessionModel session) {
@ -259,7 +261,7 @@ public class SamlProtocol implements LoginProtocol {
return SamlProtocol.SAML_POST_BINDING.equals(note);
}
protected boolean isLogoutPostBindingForClient(ClientSessionModel clientSession) {
protected boolean isLogoutPostBindingForClient(ClientLoginSessionModel clientSession) {
ClientModel client = clientSession.getClient();
SamlClient samlClient = new SamlClient(client);
String logoutPostUrl = client.getAttribute(SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE);
@ -284,7 +286,7 @@ public class SamlProtocol implements LoginProtocol {
return (logoutRedirectUrl == null || logoutRedirectUrl.trim().isEmpty());
}
protected String getNameIdFormat(SamlClient samlClient, ClientSessionModel clientSession) {
protected String getNameIdFormat(SamlClient samlClient, CommonClientSessionModel clientSession) {
String nameIdFormat = clientSession.getNote(GeneralConstants.NAMEID_FORMAT);
boolean forceFormat = samlClient.forceNameIDFormat();
@ -297,7 +299,7 @@ public class SamlProtocol implements LoginProtocol {
return nameIdFormat;
}
protected String getNameId(String nameIdFormat, ClientSessionModel clientSession, UserSessionModel userSession) {
protected String getNameId(String nameIdFormat, CommonClientSessionModel clientSession, UserSessionModel userSession) {
if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
return userSession.getUser().getEmail();
} else if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get())) {
@ -327,7 +329,7 @@ public class SamlProtocol implements LoginProtocol {
*
* @return the user's persistent NameId
*/
protected String getPersistentNameId(final ClientSessionModel clientSession, final UserSessionModel userSession) {
protected String getPersistentNameId(final CommonClientSessionModel clientSession, final UserSessionModel userSession) {
// attempt to retrieve the UserID for the client-specific attribute
final UserModel user = userSession.getUser();
final String clientNameId = String.format("%s.%s", SAML_PERSISTENT_NAME_ID_FOR,
@ -351,8 +353,8 @@ public class SamlProtocol implements LoginProtocol {
}
@Override
public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
ClientSessionModel clientSession = accessCode.getClientSession();
public Response authenticated(UserSessionModel userSession, ClientSessionCode<ClientLoginSessionModel> accessCode) {
ClientLoginSessionModel clientSession = accessCode.getClientSession();
ClientModel client = clientSession.getClient();
SamlClient samlClient = new SamlClient(client);
String requestID = clientSession.getNote(SAML_REQUEST_ID);
@ -460,7 +462,7 @@ public class SamlProtocol implements LoginProtocol {
}
}
protected Response buildAuthenticatedResponse(ClientSessionModel clientSession, String redirectUri, Document samlDocument, JaxrsSAML2BindingBuilder bindingBuilder) throws ConfigurationException, ProcessingException, IOException {
protected Response buildAuthenticatedResponse(ClientLoginSessionModel clientSession, String redirectUri, Document samlDocument, JaxrsSAML2BindingBuilder bindingBuilder) throws ConfigurationException, ProcessingException, IOException {
if (isPostBinding(clientSession)) {
return bindingBuilder.postBinding(samlDocument).response(redirectUri);
} else {
@ -479,7 +481,7 @@ public class SamlProtocol implements LoginProtocol {
}
public AttributeStatementType populateAttributeStatements(List<ProtocolMapperProcessor<SAMLAttributeStatementMapper>> attributeStatementMappers, KeycloakSession session, UserSessionModel userSession,
ClientSessionModel clientSession) {
ClientLoginSessionModel clientSession) {
AttributeStatementType attributeStatement = new AttributeStatementType();
for (ProtocolMapperProcessor<SAMLAttributeStatementMapper> processor : attributeStatementMappers) {
processor.mapper.transformAttributeStatement(attributeStatement, processor.model, session, userSession, clientSession);
@ -488,14 +490,14 @@ public class SamlProtocol implements LoginProtocol {
return attributeStatement;
}
public ResponseType transformLoginResponse(List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> mappers, ResponseType response, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public ResponseType transformLoginResponse(List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> mappers, ResponseType response, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
for (ProtocolMapperProcessor<SAMLLoginResponseMapper> processor : mappers) {
response = processor.mapper.transformLoginResponse(response, processor.model, session, userSession, clientSession);
}
return response;
}
public void populateRoles(ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession,
public void populateRoles(ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession,
final AttributeStatementType existingAttributeStatement) {
if (roleListMapper == null)
return;
@ -509,8 +511,8 @@ public class SamlProtocol implements LoginProtocol {
} else {
logoutServiceUrl = client.getAttribute(SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE);
}
if (logoutServiceUrl == null && client instanceof ClientModel)
logoutServiceUrl = ((ClientModel) client).getManagementUrl();
if (logoutServiceUrl == null)
logoutServiceUrl = client.getManagementUrl();
if (logoutServiceUrl == null || logoutServiceUrl.trim().equals(""))
return null;
return ResourceAdminManager.resolveUri(uriInfo.getRequestUri(), client.getRootUrl(), logoutServiceUrl);
@ -518,11 +520,9 @@ public class SamlProtocol implements LoginProtocol {
}
@Override
public Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
public Response frontchannelLogout(UserSessionModel userSession, ClientLoginSessionModel clientSession) {
ClientModel client = clientSession.getClient();
SamlClient samlClient = new SamlClient(client);
if (!(client instanceof ClientModel))
return null;
try {
boolean postBinding = isLogoutPostBindingForClient(clientSession);
String bindingUri = getLogoutServiceUrl(uriInfo, client, postBinding ? SAML_POST_BINDING : SAML_REDIRECT_BINDING);
@ -615,7 +615,7 @@ public class SamlProtocol implements LoginProtocol {
}
@Override
public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
public void backchannelLogout(UserSessionModel userSession, ClientLoginSessionModel clientSession) {
ClientModel client = clientSession.getClient();
SamlClient samlClient = new SamlClient(client);
String logoutUrl = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING);
@ -674,7 +674,7 @@ public class SamlProtocol implements LoginProtocol {
}
protected SAML2LogoutRequestBuilder createLogoutRequest(String logoutUrl, ClientSessionModel clientSession, ClientModel client) {
protected SAML2LogoutRequestBuilder createLogoutRequest(String logoutUrl, ClientLoginSessionModel clientSession, ClientModel client) {
// build userPrincipal with subject used at login
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder().assertionExpiration(realm.getAccessCodeLifespan()).issuer(getResponseIssuer(realm)).sessionIndex(clientSession.getId())
.userPrincipal(clientSession.getNote(SAML_NAME_ID), clientSession.getNote(SAML_NAME_ID_FORMAT)).destination(logoutUrl);
@ -682,7 +682,7 @@ public class SamlProtocol implements LoginProtocol {
}
@Override
public boolean requireReauthentication(UserSessionModel userSession, ClientSessionModel clientSession) {
public boolean requireReauthentication(UserSessionModel userSession, LoginSessionModel clientSession) {
// Not yet supported
return false;
}

View file

@ -86,6 +86,7 @@ import org.keycloak.rotation.HardcodedKeyLocator;
import org.keycloak.rotation.KeyLocator;
import org.keycloak.saml.SPMetadataDescriptor;
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
import org.keycloak.sessions.LoginSessionModel;
/**
* Resource class for the oauth/openid connect token service
@ -270,13 +271,13 @@ public class SamlService extends AuthorizationEndpointBase {
return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI);
}
ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
clientSession.setAuthMethod(SamlProtocol.LOGIN_PROTOCOL);
clientSession.setRedirectUri(redirect);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
clientSession.setNote(SamlProtocol.SAML_BINDING, bindingType);
clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
clientSession.setNote(SamlProtocol.SAML_REQUEST_ID, requestAbstractType.getID());
LoginSessionModel loginSession = session.loginSessions().createLoginSession(realm, client, true);
loginSession.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
loginSession.setRedirectUri(redirect);
loginSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
loginSession.setNote(SamlProtocol.SAML_BINDING, bindingType);
loginSession.setNote(GeneralConstants.RELAY_STATE, relayState);
loginSession.setNote(SamlProtocol.SAML_REQUEST_ID, requestAbstractType.getID());
// Handle NameIDPolicy from SP
NameIDPolicyType nameIdPolicy = requestAbstractType.getNameIDPolicy();
@ -285,7 +286,7 @@ public class SamlService extends AuthorizationEndpointBase {
String nameIdFormat = nameIdFormatUri.toString();
// TODO: Handle AllowCreate too, relevant for persistent NameID.
if (isSupportedNameIdFormat(nameIdFormat)) {
clientSession.setNote(GeneralConstants.NAMEID_FORMAT, nameIdFormat);
loginSession.setNote(GeneralConstants.NAMEID_FORMAT, nameIdFormat);
} else {
event.detail(Details.REASON, "unsupported_nameid_format");
event.error(Errors.INVALID_SAML_AUTHN_REQUEST);
@ -301,13 +302,13 @@ public class SamlService extends AuthorizationEndpointBase {
BaseIDAbstractType baseID = subject.getSubType().getBaseID();
if (baseID != null && baseID instanceof NameIDType) {
NameIDType nameID = (NameIDType) baseID;
clientSession.setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, nameID.getValue());
loginSession.setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, nameID.getValue());
}
}
}
return newBrowserAuthentication(clientSession, requestAbstractType.isIsPassive(), redirectToAuthentication);
return newBrowserAuthentication(loginSession, requestAbstractType.isIsPassive(), redirectToAuthentication);
}
protected String getBindingType(AuthnRequestType requestAbstractType) {
@ -518,13 +519,13 @@ public class SamlService extends AuthorizationEndpointBase {
}
protected Response newBrowserAuthentication(ClientSessionModel clientSession, boolean isPassive, boolean redirectToAuthentication) {
protected Response newBrowserAuthentication(LoginSessionModel loginSession, boolean isPassive, boolean redirectToAuthentication) {
SamlProtocol samlProtocol = new SamlProtocol().setEventBuilder(event).setHttpHeaders(headers).setRealm(realm).setSession(session).setUriInfo(uriInfo);
return newBrowserAuthentication(clientSession, isPassive, redirectToAuthentication, samlProtocol);
return newBrowserAuthentication(loginSession, isPassive, redirectToAuthentication, samlProtocol);
}
protected Response newBrowserAuthentication(ClientSessionModel clientSession, boolean isPassive, boolean redirectToAuthentication, SamlProtocol samlProtocol) {
return handleBrowserAuthenticationRequest(clientSession, samlProtocol, isPassive, redirectToAuthentication);
protected Response newBrowserAuthentication(LoginSessionModel loginSession, boolean isPassive, boolean redirectToAuthentication, SamlProtocol samlProtocol) {
return handleBrowserAuthenticationRequest(loginSession, samlProtocol, isPassive, redirectToAuthentication);
}
/**
@ -615,9 +616,9 @@ public class SamlService extends AuthorizationEndpointBase {
return ErrorPage.error(session, Messages.INVALID_REDIRECT_URI);
}
ClientSessionModel clientSession = createClientSessionForIdpInitiatedSso(this.session, this.realm, client, relayState);
LoginSessionModel loginSession = createLoginSessionForIdpInitiatedSso(this.session, this.realm, client, relayState);
return newBrowserAuthentication(clientSession, false, false);
return newBrowserAuthentication(loginSession, false, false);
}
/**
@ -631,7 +632,7 @@ public class SamlService extends AuthorizationEndpointBase {
* @param relayState Optional relay state - free field as per SAML specification
* @return
*/
public static ClientSessionModel createClientSessionForIdpInitiatedSso(KeycloakSession session, RealmModel realm, ClientModel client, String relayState) {
public static LoginSessionModel createLoginSessionForIdpInitiatedSso(KeycloakSession session, RealmModel realm, ClientModel client, String relayState) {
String bindingType = SamlProtocol.SAML_POST_BINDING;
if (client.getManagementUrl() == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE) == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE) != null) {
bindingType = SamlProtocol.SAML_REDIRECT_BINDING;
@ -647,21 +648,21 @@ public class SamlService extends AuthorizationEndpointBase {
redirect = client.getManagementUrl();
}
ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
clientSession.setAuthMethod(SamlProtocol.LOGIN_PROTOCOL);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
clientSession.setNote(SamlProtocol.SAML_BINDING, SamlProtocol.SAML_POST_BINDING);
clientSession.setNote(SamlProtocol.SAML_IDP_INITIATED_LOGIN, "true");
clientSession.setRedirectUri(redirect);
LoginSessionModel loginSession = session.loginSessions().createLoginSession(realm, client, true);
loginSession.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
loginSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
loginSession.setNote(SamlProtocol.SAML_BINDING, SamlProtocol.SAML_POST_BINDING);
loginSession.setNote(SamlProtocol.SAML_IDP_INITIATED_LOGIN, "true");
loginSession.setRedirectUri(redirect);
if (relayState == null) {
relayState = client.getAttribute(SamlProtocol.SAML_IDP_INITIATED_SSO_RELAY_STATE);
}
if (relayState != null && !relayState.trim().equals("")) {
clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
loginSession.setNote(GeneralConstants.RELAY_STATE, relayState);
}
return clientSession;
return loginSession;
}
@POST

View file

@ -19,7 +19,7 @@ package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
@ -117,7 +117,7 @@ public class GroupMembershipMapper extends AbstractSAMLProtocolMapper implements
@Override
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
String single = mappingModel.getConfig().get(SINGLE_GROUP_ATTRIBUTE);
boolean singleAttribute = Boolean.parseBoolean(single);

View file

@ -18,7 +18,7 @@
package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@ -76,7 +76,7 @@ public class HardcodedAttributeMapper extends AbstractSAMLProtocolMapper impleme
}
@Override
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
String attributeValue = mappingModel.getConfig().get(ATTRIBUTE_VALUE);
AttributeStatementHelper.addAttribute(attributeStatement, mappingModel, attributeValue);

View file

@ -19,7 +19,7 @@ package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ProtocolMapperModel;
@ -111,14 +111,14 @@ public class RoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRo
}
@Override
public void mapRoles(AttributeStatementType roleAttributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public void mapRoles(AttributeStatementType roleAttributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
String single = mappingModel.getConfig().get(SINGLE_ROLE_ATTRIBUTE);
boolean singleAttribute = Boolean.parseBoolean(single);
List<SamlProtocol.ProtocolMapperProcessor<SAMLRoleNameMapper>> roleNameMappers = new LinkedList<>();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
AttributeType singleAttributeType = null;
Set<ProtocolMapperModel> requestedProtocolMappers = new ClientSessionCode(session, clientSession.getRealm(), clientSession).getRequestedProtocolMappers();
Set<ProtocolMapperModel> requestedProtocolMappers = ClientSessionCode.getRequestedProtocolMappers(clientSession.getProtocolMappers(), clientSession.getClient());
for (ProtocolMapperModel mapping : requestedProtocolMappers) {
ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());

View file

@ -18,7 +18,7 @@
package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@ -30,5 +30,5 @@ import org.keycloak.models.UserSessionModel;
public interface SAMLAttributeStatementMapper {
void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession);
UserSessionModel userSession, ClientLoginSessionModel clientSession);
}

View file

@ -18,7 +18,7 @@
package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.protocol.ResponseType;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@ -30,5 +30,5 @@ import org.keycloak.models.UserSessionModel;
public interface SAMLLoginResponseMapper {
ResponseType transformLoginResponse(ResponseType response, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession);
UserSessionModel userSession, ClientLoginSessionModel clientSession);
}

View file

@ -18,7 +18,7 @@
package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@ -30,5 +30,5 @@ import org.keycloak.models.UserSessionModel;
public interface SAMLRoleListMapper {
void mapRoles(AttributeStatementType roleAttributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionModel clientSession);
UserSessionModel userSession, ClientLoginSessionModel clientSession);
}

View file

@ -18,7 +18,7 @@
package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel;
@ -77,7 +77,7 @@ public class UserAttributeStatementMapper extends AbstractSAMLProtocolMapper imp
}
@Override
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
UserModel user = userSession.getUser();
String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE);
List<String> attributeValues = KeycloakModelUtils.resolveAttribute(user, attributeName);

View file

@ -18,7 +18,7 @@
package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserModel;
@ -76,7 +76,7 @@ public class UserPropertyAttributeStatementMapper extends AbstractSAMLProtocolMa
}
@Override
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
UserModel user = userSession.getUser();
String propertyName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE);
String propertyValue = ProtocolMapperUtils.getUserModelValue(user, propertyName);

View file

@ -18,7 +18,7 @@
package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@ -74,7 +74,7 @@ public class UserSessionNoteStatementMapper extends AbstractSAMLProtocolMapper i
}
@Override
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientLoginSessionModel clientSession) {
String note = mappingModel.getConfig().get("note");
String value = userSession.getNote(note);
if (value == null) return;

View file

@ -20,8 +20,8 @@ package org.keycloak.protocol.saml.profile.ecp;
import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
@ -35,6 +35,7 @@ import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.sessions.LoginSessionModel;
import org.w3c.dom.Document;
import javax.ws.rs.core.Response;
@ -85,15 +86,15 @@ public class SamlEcpProfileService extends SamlService {
}
@Override
protected Response newBrowserAuthentication(ClientSessionModel clientSession, boolean isPassive, boolean redirectToAuthentication, SamlProtocol samlProtocol) {
return super.newBrowserAuthentication(clientSession, isPassive, redirectToAuthentication, createEcpSamlProtocol());
protected Response newBrowserAuthentication(LoginSessionModel loginSession, boolean isPassive, boolean redirectToAuthentication, SamlProtocol samlProtocol) {
return super.newBrowserAuthentication(loginSession, isPassive, redirectToAuthentication, createEcpSamlProtocol());
}
private SamlProtocol createEcpSamlProtocol() {
return new SamlProtocol() {
// method created to send a SOAP Binding response instead of a HTTP POST response
@Override
protected Response buildAuthenticatedResponse(ClientSessionModel clientSession, String redirectUri, Document samlDocument, JaxrsSAML2BindingBuilder bindingBuilder) throws ConfigurationException, ProcessingException, IOException {
protected Response buildAuthenticatedResponse(ClientLoginSessionModel clientSession, String redirectUri, Document samlDocument, JaxrsSAML2BindingBuilder bindingBuilder) throws ConfigurationException, ProcessingException, IOException {
Document document = bindingBuilder.postBinding(samlDocument).getDocument();
try {
@ -113,7 +114,7 @@ public class SamlEcpProfileService extends SamlService {
}
}
private void createRequestAuthenticatedHeader(ClientSessionModel clientSession, Soap.SoapMessageBuilder messageBuilder) {
private void createRequestAuthenticatedHeader(ClientLoginSessionModel clientSession, Soap.SoapMessageBuilder messageBuilder) {
ClientModel client = clientSession.getClient();
if ("true".equals(client.getAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) {
@ -133,7 +134,7 @@ public class SamlEcpProfileService extends SamlService {
}
@Override
protected Response buildErrorResponse(ClientSessionModel clientSession, JaxrsSAML2BindingBuilder binding, Document document) throws ConfigurationException, ProcessingException, IOException {
protected Response buildErrorResponse(LoginSessionModel clientSession, JaxrsSAML2BindingBuilder binding, Document document) throws ConfigurationException, ProcessingException, IOException {
return Soap.createMessage().addToBody(document).build();
}

View file

@ -104,7 +104,7 @@ public class HttpBasicAuthenticator implements AuthenticatorFactory {
boolean valid = context.getSession().userCredentialManager().isValid(realm, user, UserCredentialModel.password(password));
if (valid) {
context.getClientSession().setAuthenticatedUser(user);
context.getLoginSession().setAuthenticatedUser(user);
context.success();
} else {
context.getEvent().user(user);

View file

@ -33,6 +33,7 @@ import org.keycloak.models.cache.CacheRealmProvider;
import org.keycloak.models.cache.UserCache;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.sessions.LoginSessionProvider;
import org.keycloak.storage.UserStorageManager;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
@ -54,10 +55,10 @@ public class DefaultKeycloakSession implements KeycloakSession {
private final DefaultKeycloakTransactionManager transactionManager;
private final Map<String, Object> attributes = new HashMap<>();
private RealmProvider model;
private UserProvider userModel;
private UserStorageManager userStorageManager;
private UserCredentialStoreManager userCredentialStorageManager;
private UserSessionProvider sessionProvider;
private LoginSessionProvider loginSessionProvider;
private UserFederatedStorageProvider userFederatedStorageProvider;
private KeycloakContext context;
private KeyManager keyManager;
@ -236,6 +237,14 @@ public class DefaultKeycloakSession implements KeycloakSession {
return sessionProvider;
}
@Override
public LoginSessionProvider loginSessions() {
if (loginSessionProvider == null) {
loginSessionProvider = getProvider(LoginSessionProvider.class);
}
return loginSessionProvider;
}
@Override
public KeyManager keys() {
if (keyManager == null) {

View file

@ -17,6 +17,7 @@
package org.keycloak.services.managers;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.RealmModel;
@ -35,7 +36,7 @@ public class Auth {
private final UserModel user;
private final ClientModel client;
private final UserSessionModel session;
private ClientSessionModel clientSession;
private ClientLoginSessionModel clientSession;
public Auth(RealmModel realm, AccessToken token, UserModel user, ClientModel client, UserSessionModel session, boolean cookie) {
this.cookie = cookie;
@ -71,11 +72,11 @@ public class Auth {
return session;
}
public ClientSessionModel getClientSession() {
public ClientLoginSessionModel getClientSession() {
return clientSession;
}
public void setClientSession(ClientSessionModel clientSession) {
public void setClientSession(ClientLoginSessionModel clientSession) {
this.clientSession = clientSession;
}

View file

@ -20,6 +20,7 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.TokenVerifier;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionContextResult;
import org.keycloak.authentication.RequiredActionFactory;
@ -35,8 +36,8 @@ import org.keycloak.events.EventType;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.jose.jws.AlgorithmType;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
@ -58,6 +59,7 @@ import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.util.CookieHelper;
import org.keycloak.services.util.P3PHelper;
import org.keycloak.sessions.LoginSessionModel;
import javax.crypto.SecretKey;
import javax.ws.rs.core.Cookie;
@ -68,6 +70,7 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.security.PublicKey;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@ -159,7 +162,7 @@ public class AuthenticationManager {
logger.debugv("Logging out: {0} ({1})", user.getUsername(), userSession.getId());
expireUserSessionCookie(session, userSession, realm, uriInfo, headers, connection);
for (ClientSessionModel clientSession : userSession.getClientSessions()) {
for (ClientLoginSessionModel clientSession : userSession.getClientLoginSessions().values()) {
backchannelLogoutClientSession(session, realm, clientSession, userSession, uriInfo, headers);
}
if (logoutBroker) {
@ -169,6 +172,7 @@ public class AuthenticationManager {
try {
identityProvider.backchannelLogout(session, userSession, uriInfo, realm);
} catch (Exception e) {
logger.warn("Exception at broker backchannel logout for broker " + brokerId, e);
}
}
}
@ -176,17 +180,17 @@ public class AuthenticationManager {
session.sessions().removeUserSession(realm, userSession);
}
public static void backchannelLogoutClientSession(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession, UserSessionModel userSession, UriInfo uriInfo, HttpHeaders headers) {
public static void backchannelLogoutClientSession(KeycloakSession session, RealmModel realm, ClientLoginSessionModel clientSession, UserSessionModel userSession, UriInfo uriInfo, HttpHeaders headers) {
ClientModel client = clientSession.getClient();
if (client instanceof ClientModel && !client.isFrontchannelLogout() && !ClientSessionModel.Action.LOGGED_OUT.name().equals(clientSession.getAction())) {
String authMethod = clientSession.getAuthMethod();
if (!client.isFrontchannelLogout() && !ClientLoginSessionModel.Action.LOGGED_OUT.name().equals(clientSession.getAction())) {
String authMethod = clientSession.getProtocol();
if (authMethod == null) return; // must be a keycloak service like account
LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod);
protocol.setRealm(realm)
.setHttpHeaders(headers)
.setUriInfo(uriInfo);
protocol.backchannelLogout(userSession, clientSession);
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
clientSession.setAction(ClientLoginSessionModel.Action.LOGGED_OUT.name());
}
}
@ -197,8 +201,8 @@ public class AuthenticationManager {
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
for (UserSessionModel userSession : userSessions) {
List<ClientSessionModel> clientSessions = userSession.getClientSessions();
for (ClientSessionModel clientSession : clientSessions) {
Collection<ClientLoginSessionModel> clientSessions = userSession.getClientLoginSessions().values();
for (ClientLoginSessionModel clientSession : clientSessions) {
if (clientSession.getClient().getId().equals(clientId)) {
AuthenticationManager.backchannelLogoutClientSession(session, realm, clientSession, userSession, uriInfo, headers);
TokenManager.dettachClientSession(session.sessions(), realm, clientSession);
@ -215,16 +219,16 @@ public class AuthenticationManager {
if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
userSession.setState(UserSessionModel.State.LOGGING_OUT);
}
List<ClientSessionModel> redirectClients = new LinkedList<ClientSessionModel>();
for (ClientSessionModel clientSession : userSession.getClientSessions()) {
List<ClientLoginSessionModel> redirectClients = new LinkedList<>();
for (ClientLoginSessionModel clientSession : userSession.getClientLoginSessions().values()) {
ClientModel client = clientSession.getClient();
if (ClientSessionModel.Action.LOGGED_OUT.name().equals(clientSession.getAction())) continue;
if (ClientLoginSessionModel.Action.LOGGED_OUT.name().equals(clientSession.getAction())) continue;
if (client.isFrontchannelLogout()) {
String authMethod = clientSession.getAuthMethod();
String authMethod = clientSession.getProtocol();
if (authMethod == null) continue; // must be a keycloak service like account
redirectClients.add(clientSession);
} else {
String authMethod = clientSession.getAuthMethod();
String authMethod = clientSession.getProtocol();
if (authMethod == null) continue; // must be a keycloak service like account
LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod);
protocol.setRealm(realm)
@ -233,21 +237,21 @@ public class AuthenticationManager {
try {
logger.debugv("backchannel logout to: {0}", client.getClientId());
protocol.backchannelLogout(userSession, clientSession);
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
clientSession.setAction(ClientLoginSessionModel.Action.LOGGED_OUT.name());
} catch (Exception e) {
ServicesLogger.LOGGER.failedToLogoutClient(e);
}
}
}
for (ClientSessionModel nextRedirectClient : redirectClients) {
String authMethod = nextRedirectClient.getAuthMethod();
for (ClientLoginSessionModel nextRedirectClient : redirectClients) {
String authMethod = nextRedirectClient.getProtocol();
LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod);
protocol.setRealm(realm)
.setHttpHeaders(headers)
.setUriInfo(uriInfo);
// setting this to logged out cuz I"m not sure protocols can always verify that the client was logged out or not
nextRedirectClient.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
nextRedirectClient.setAction(ClientLoginSessionModel.Action.LOGGED_OUT.name());
try {
logger.debugv("frontchannel logout to: {0}", nextRedirectClient.getClient().getClientId());
Response response = protocol.frontchannelLogout(userSession, nextRedirectClient);
@ -410,11 +414,11 @@ public class AuthenticationManager {
public static Response redirectAfterSuccessfulFlow(KeycloakSession session, RealmModel realm, UserSessionModel userSession,
ClientSessionModel clientSession,
ClientLoginSessionModel clientSession,
HttpRequest request, UriInfo uriInfo, ClientConnection clientConnection,
EventBuilder event) {
LoginProtocol protocol = session.getProvider(LoginProtocol.class, clientSession.getAuthMethod());
protocol.setRealm(realm)
EventBuilder event, String protocol) {
LoginProtocol protocolImpl = session.getProvider(LoginProtocol.class, protocol);
protocolImpl.setRealm(realm)
.setHttpHeaders(request.getHttpHeaders())
.setUriInfo(uriInfo)
.setEventBuilder(event);
@ -423,7 +427,7 @@ public class AuthenticationManager {
}
public static Response redirectAfterSuccessfulFlow(KeycloakSession session, RealmModel realm, UserSessionModel userSession,
ClientSessionModel clientSession,
ClientLoginSessionModel clientSession,
HttpRequest request, UriInfo uriInfo, ClientConnection clientConnection,
EventBuilder event, LoginProtocol protocol) {
Cookie sessionCookie = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_SESSION_COOKIE);
@ -460,32 +464,33 @@ public class AuthenticationManager {
userSession.setNote(AUTH_TIME, String.valueOf(authTime));
}
return protocol.authenticated(userSession, new ClientSessionCode(session, realm, clientSession));
return protocol.authenticated(userSession, new ClientSessionCode<>(session, realm, clientSession));
}
public static boolean isSSOAuthentication(ClientSessionModel clientSession) {
public static boolean isSSOAuthentication(ClientLoginSessionModel clientSession) {
String ssoAuth = clientSession.getNote(SSO_AUTH);
return Boolean.parseBoolean(ssoAuth);
}
public static Response nextActionAfterAuthentication(KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession,
public static Response nextActionAfterAuthentication(KeycloakSession session, LoginSessionModel loginSession,
ClientConnection clientConnection,
HttpRequest request, UriInfo uriInfo, EventBuilder event) {
Response requiredAction = actionRequired(session, userSession, clientSession, clientConnection, request, uriInfo, event);
Response requiredAction = actionRequired(session, loginSession, clientConnection, request, uriInfo, event);
if (requiredAction != null) return requiredAction;
return finishedRequiredActions(session, userSession, clientSession, clientConnection, request, uriInfo, event);
return finishedRequiredActions(session, loginSession, clientConnection, request, uriInfo, event);
}
public static Response finishedRequiredActions(KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession, ClientConnection clientConnection, HttpRequest request, UriInfo uriInfo, EventBuilder event) {
if (clientSession.getNote(END_AFTER_REQUIRED_ACTIONS) != null) {
public static Response finishedRequiredActions(KeycloakSession session, LoginSessionModel loginSession,
ClientConnection clientConnection, HttpRequest request, UriInfo uriInfo, EventBuilder event) {
if (loginSession.getNote(END_AFTER_REQUIRED_ACTIONS) != null) {
LoginFormsProvider infoPage = session.getProvider(LoginFormsProvider.class)
.setSuccess(Messages.ACCOUNT_UPDATED);
if (clientSession.getNote(SET_REDIRECT_URI_AFTER_REQUIRED_ACTIONS) != null) {
if (clientSession.getRedirectUri() != null) {
infoPage.setAttribute("pageRedirectUri", clientSession.getRedirectUri());
if (loginSession.getNote(SET_REDIRECT_URI_AFTER_REQUIRED_ACTIONS) != null) {
if (loginSession.getRedirectUri() != null) {
infoPage.setAttribute("pageRedirectUri", loginSession.getRedirectUri());
}
} else {
@ -493,31 +498,32 @@ public class AuthenticationManager {
}
Response response = infoPage
.createInfoPage();
session.sessions().removeUserSession(session.getContext().getRealm(), userSession);
return response;
}
event.success();
RealmModel realm = clientSession.getRealm();
return redirectAfterSuccessfulFlow(session, realm , userSession, clientSession, request, uriInfo, clientConnection, event);
RealmModel realm = loginSession.getRealm();
ClientLoginSessionModel clientSession = AuthenticationProcessor.attachSession(loginSession, null, session, realm, clientConnection, event);
return redirectAfterSuccessfulFlow(session, realm , clientSession.getUserSession(), clientSession, request, uriInfo, clientConnection, event, loginSession.getProtocol());
}
public static boolean isActionRequired(final KeycloakSession session, final UserSessionModel userSession, final ClientSessionModel clientSession,
public static boolean isActionRequired(final KeycloakSession session, final LoginSessionModel loginSession,
final ClientConnection clientConnection,
final HttpRequest request, final UriInfo uriInfo, final EventBuilder event) {
final RealmModel realm = clientSession.getRealm();
final UserModel user = userSession.getUser();
final ClientModel client = clientSession.getClient();
final RealmModel realm = loginSession.getRealm();
final UserModel user = loginSession.getAuthenticatedUser();
final ClientModel client = loginSession.getClient();
evaluateRequiredActionTriggers(session, userSession, clientSession, clientConnection, request, uriInfo, event, realm, user);
evaluateRequiredActionTriggers(session, loginSession, clientConnection, request, uriInfo, event, realm, user);
if (!user.getRequiredActions().isEmpty() || !clientSession.getRequiredActions().isEmpty()) return true;
if (!user.getRequiredActions().isEmpty() || !loginSession.getRequiredActions().isEmpty()) return true;
if (client.isConsentRequired()) {
UserConsentModel grantedConsent = session.users().getConsentByClient(realm, user.getId(), client.getId());
ClientSessionCode accessCode = new ClientSessionCode(session, realm, clientSession);
ClientSessionCode<LoginSessionModel> accessCode = new ClientSessionCode<>(session, realm, loginSession);
for (RoleModel r : accessCode.getRequestedRoles()) {
// Consent already granted by user
@ -544,27 +550,27 @@ public class AuthenticationManager {
}
public static Response actionRequired(final KeycloakSession session, final UserSessionModel userSession, final ClientSessionModel clientSession,
public static Response actionRequired(final KeycloakSession session, final LoginSessionModel loginSession,
final ClientConnection clientConnection,
final HttpRequest request, final UriInfo uriInfo, final EventBuilder event) {
final RealmModel realm = clientSession.getRealm();
final UserModel user = userSession.getUser();
final ClientModel client = clientSession.getClient();
final RealmModel realm = loginSession.getRealm();
final UserModel user = loginSession.getAuthenticatedUser();
final ClientModel client = loginSession.getClient();
evaluateRequiredActionTriggers(session, userSession, clientSession, clientConnection, request, uriInfo, event, realm, user);
evaluateRequiredActionTriggers(session, loginSession, clientConnection, request, uriInfo, event, realm, user);
logger.debugv("processAccessCode: go to oauth page?: {0}", client.isConsentRequired());
event.detail(Details.CODE_ID, clientSession.getId());
event.detail(Details.CODE_ID, loginSession.getId());
Set<String> requiredActions = user.getRequiredActions();
Response action = executionActions(session, userSession, clientSession, request, event, realm, user, requiredActions);
Response action = executionActions(session, loginSession, request, event, realm, user, requiredActions);
if (action != null) return action;
// executionActions() method should remove any duplicate actions that might be in the clientSession
requiredActions = clientSession.getRequiredActions();
action = executionActions(session, userSession, clientSession, request, event, realm, user, requiredActions);
requiredActions = loginSession.getRequiredActions();
action = executionActions(session, loginSession, request, event, realm, user, requiredActions);
if (action != null) return action;
if (client.isConsentRequired()) {
@ -573,7 +579,7 @@ public class AuthenticationManager {
List<RoleModel> realmRoles = new LinkedList<>();
MultivaluedMap<String, RoleModel> resourceRoles = new MultivaluedMapImpl<>();
ClientSessionCode accessCode = new ClientSessionCode(session, realm, clientSession);
ClientSessionCode<LoginSessionModel> accessCode = new ClientSessionCode<>(session, realm, loginSession);
for (RoleModel r : accessCode.getRequestedRoles()) {
// Consent already granted by user
@ -599,13 +605,15 @@ public class AuthenticationManager {
// Skip grant screen if everything was already approved by this user
if (realmRoles.size() > 0 || resourceRoles.size() > 0 || protocolMappers.size() > 0) {
accessCode.setAction(ClientSessionModel.Action.REQUIRED_ACTIONS.name());
clientSession.setNote(CURRENT_REQUIRED_ACTION, ClientSessionModel.Action.OAUTH_GRANT.name());
accessCode.
setAction(ClientLoginSessionModel.Action.REQUIRED_ACTIONS.name());
loginSession.setNote(CURRENT_REQUIRED_ACTION, ClientLoginSessionModel.Action.OAUTH_GRANT.name());
return session.getProvider(LoginFormsProvider.class)
.setClientSessionCode(accessCode.getCode())
.setAccessRequest(realmRoles, resourceRoles, protocolMappers)
.createOAuthGrant(clientSession);
.createOAuthGrant();
} else {
String consentDetail = (grantedConsent != null) ? Details.CONSENT_VALUE_PERSISTED_CONSENT : Details.CONSENT_VALUE_NO_CONSENT_REQUIRED;
event.detail(Details.CONSENT, consentDetail);
@ -617,7 +625,7 @@ public class AuthenticationManager {
}
protected static Response executionActions(KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession,
protected static Response executionActions(KeycloakSession session, LoginSessionModel loginSession,
HttpRequest request, EventBuilder event, RealmModel realm, UserModel user,
Set<String> requiredActions) {
for (String action : requiredActions) {
@ -635,34 +643,34 @@ public class AuthenticationManager {
throw new RuntimeException("Unable to find factory for Required Action: " + model.getProviderId() + " did you forget to declare it in a META-INF/services file?");
}
RequiredActionProvider actionProvider = factory.create(session);
RequiredActionContextResult context = new RequiredActionContextResult(userSession, clientSession, realm, event, session, request, user, factory);
RequiredActionContextResult context = new RequiredActionContextResult(loginSession, realm, event, session, request, user, factory);
actionProvider.requiredActionChallenge(context);
if (context.getStatus() == RequiredActionContext.Status.FAILURE) {
LoginProtocol protocol = context.getSession().getProvider(LoginProtocol.class, context.getClientSession().getAuthMethod());
LoginProtocol protocol = context.getSession().getProvider(LoginProtocol.class, context.getLoginSession().getProtocol());
protocol.setRealm(context.getRealm())
.setHttpHeaders(context.getHttpRequest().getHttpHeaders())
.setUriInfo(context.getUriInfo())
.setEventBuilder(event);
Response response = protocol.sendError(context.getClientSession(), Error.CONSENT_DENIED);
Response response = protocol.sendError(context.getLoginSession(), Error.CONSENT_DENIED);
event.error(Errors.REJECTED_BY_USER);
return response;
}
else if (context.getStatus() == RequiredActionContext.Status.CHALLENGE) {
clientSession.setNote(CURRENT_REQUIRED_ACTION, model.getProviderId());
loginSession.setNote(CURRENT_REQUIRED_ACTION, model.getProviderId());
return context.getChallenge();
}
else if (context.getStatus() == RequiredActionContext.Status.SUCCESS) {
event.clone().event(EventType.CUSTOM_REQUIRED_ACTION).detail(Details.CUSTOM_REQUIRED_ACTION, factory.getId()).success();
// don't have to perform the same action twice, so remove it from both the user and session required actions
clientSession.getUserSession().getUser().removeRequiredAction(factory.getId());
clientSession.removeRequiredAction(factory.getId());
loginSession.getAuthenticatedUser().removeRequiredAction(factory.getId());
loginSession.removeRequiredAction(factory.getId());
}
}
return null;
}
public static void evaluateRequiredActionTriggers(final KeycloakSession session, final UserSessionModel userSession, final ClientSessionModel clientSession, final ClientConnection clientConnection, final HttpRequest request, final UriInfo uriInfo, final EventBuilder event, final RealmModel realm, final UserModel user) {
public static void evaluateRequiredActionTriggers(final KeycloakSession session, final LoginSessionModel loginSession, final ClientConnection clientConnection, final HttpRequest request, final UriInfo uriInfo, final EventBuilder event, final RealmModel realm, final UserModel user) {
// see if any required actions need triggering, i.e. an expired password
for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) {
@ -672,7 +680,7 @@ public class AuthenticationManager {
throw new RuntimeException("Unable to find factory for Required Action: " + model.getProviderId() + " did you forget to declare it in a META-INF/services file?");
}
RequiredActionProvider provider = factory.create(session);
RequiredActionContextResult result = new RequiredActionContextResult(userSession, clientSession, realm, event, session, request, user, factory) {
RequiredActionContextResult result = new RequiredActionContextResult(loginSession, realm, event, session, request, user, factory) {
@Override
public void challenge(Response response) {
throw new RuntimeException("Not allowed to call challenge() within evaluateTriggers()");

View file

@ -24,8 +24,8 @@ import org.keycloak.common.util.StringPropertyReplacer;
import org.keycloak.common.util.Time;
import org.keycloak.connections.httpclient.HttpClientProvider;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.models.ClientLoginSessionModel;
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;
@ -113,7 +113,7 @@ public class ResourceAdminManager {
protected void logoutUserSessions(URI requestUri, RealmModel realm, List<UserSessionModel> userSessions) {
// Map from "app" to clientSessions for this app
MultivaluedHashMap<ClientModel, ClientSessionModel> clientSessions = new MultivaluedHashMap<ClientModel, ClientSessionModel>();
MultivaluedHashMap<String, ClientLoginSessionModel> clientSessions = new MultivaluedHashMap<>();
for (UserSessionModel userSession : userSessions) {
putClientSessions(clientSessions, userSession);
}
@ -121,37 +121,40 @@ public class ResourceAdminManager {
logger.debugv("logging out {0} resources ", clientSessions.size());
//logger.infov("logging out resources: {0}", clientSessions);
for (Map.Entry<ClientModel, List<ClientSessionModel>> entry : clientSessions.entrySet()) {
logoutClientSessions(requestUri, realm, entry.getKey(), entry.getValue());
for (Map.Entry<String, List<ClientLoginSessionModel>> entry : clientSessions.entrySet()) {
if (entry.getValue().size() == 0) {
continue;
}
logoutClientSessions(requestUri, realm, entry.getValue().get(0).getClient(), entry.getValue());
}
}
private void putClientSessions(MultivaluedHashMap<ClientModel, ClientSessionModel> clientSessions, UserSessionModel userSession) {
for (ClientSessionModel clientSession : userSession.getClientSessions()) {
ClientModel client = clientSession.getClient();
clientSessions.add(client, clientSession);
private void putClientSessions(MultivaluedHashMap<String, ClientLoginSessionModel> clientSessions, UserSessionModel userSession) {
for (Map.Entry<String, ClientLoginSessionModel> entry : userSession.getClientLoginSessions().entrySet()) {
clientSessions.add(entry.getKey(), entry.getValue());
}
}
public void logoutUserFromClient(URI requestUri, RealmModel realm, ClientModel resource, UserModel user) {
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
List<ClientSessionModel> ourAppClientSessions = null;
List<ClientLoginSessionModel> ourAppClientSessions = new LinkedList<>();
if (userSessions != null) {
MultivaluedHashMap<ClientModel, ClientSessionModel> clientSessions = new MultivaluedHashMap<ClientModel, ClientSessionModel>();
for (UserSessionModel userSession : userSessions) {
putClientSessions(clientSessions, userSession);
ClientLoginSessionModel clientSession = userSession.getClientLoginSessions().get(resource.getId());
if (clientSession != null) {
ourAppClientSessions.add(clientSession);
}
}
ourAppClientSessions = clientSessions.get(resource);
}
logoutClientSessions(requestUri, realm, resource, ourAppClientSessions);
}
public boolean logoutClientSession(URI requestUri, RealmModel realm, ClientModel resource, ClientSessionModel clientSession) {
public boolean logoutClientSession(URI requestUri, RealmModel realm, ClientModel resource, ClientLoginSessionModel clientSession) {
return logoutClientSessions(requestUri, realm, resource, Arrays.asList(clientSession));
}
protected boolean logoutClientSessions(URI requestUri, RealmModel realm, ClientModel resource, List<ClientSessionModel> clientSessions) {
protected boolean logoutClientSessions(URI requestUri, RealmModel realm, ClientModel resource, List<ClientLoginSessionModel> clientSessions) {
String managementUrl = getManagementUrl(requestUri, resource);
if (managementUrl != null) {
@ -160,7 +163,7 @@ public class ResourceAdminManager {
List<String> userSessions = new LinkedList<>();
if (clientSessions != null && clientSessions.size() > 0) {
adapterSessionIds = new MultivaluedHashMap<String, String>();
for (ClientSessionModel clientSession : clientSessions) {
for (ClientLoginSessionModel clientSession : clientSessions) {
String adapterSessionId = clientSession.getNote(AdapterConstants.CLIENT_SESSION_STATE);
if (adapterSessionId != null) {
String host = clientSession.getNote(AdapterConstants.CLIENT_SESSION_HOST);

View file

@ -18,11 +18,10 @@ package org.keycloak.services.managers;
import org.jboss.logging.Logger;
import org.keycloak.common.util.Time;
import org.keycloak.models.ClientLoginSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
@ -31,7 +30,6 @@ import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.services.ServicesLogger;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@ -52,7 +50,7 @@ public class UserSessionManager {
this.persister = session.getProvider(UserSessionPersisterProvider.class);
}
public void createOrUpdateOfflineSession(ClientSessionModel clientSession, UserSessionModel userSession) {
public void createOrUpdateOfflineSession(ClientLoginSessionModel clientSession, UserSessionModel userSession) {
UserModel user = userSession.getUser();
// Create and persist offline userSession if we don't have one
@ -65,50 +63,50 @@ public class UserSessionManager {
}
// Create and persist clientSession
ClientSessionModel offlineClientSession = kcSession.sessions().getOfflineClientSession(clientSession.getRealm(), clientSession.getId());
ClientLoginSessionModel offlineClientSession = offlineUserSession.getClientLoginSessions().get(clientSession.getClient().getId());
if (offlineClientSession == null) {
createOfflineClientSession(user, clientSession, offlineUserSession);
}
}
// userSessionId is provided from offline token. It's used just to verify if it match the ID from clientSession representation
public ClientSessionModel findOfflineClientSession(RealmModel realm, String clientSessionId) {
return kcSession.sessions().getOfflineClientSession(realm, clientSessionId);
public UserSessionModel findOfflineUserSession(RealmModel realm, String userSessionId) {
return kcSession.sessions().getOfflineUserSession(realm, userSessionId);
}
public Set<ClientModel> findClientsWithOfflineToken(RealmModel realm, UserModel user) {
List<ClientSessionModel> clientSessions = kcSession.sessions().getOfflineClientSessions(realm, user);
List<UserSessionModel> userSessions = kcSession.sessions().getOfflineUserSessions(realm, user);
Set<ClientModel> clients = new HashSet<>();
for (ClientSessionModel clientSession : clientSessions) {
clients.add(clientSession.getClient());
for (UserSessionModel userSession : userSessions) {
Set<String> clientIds = userSession.getClientLoginSessions().keySet();
for (String clientUUID : clientIds) {
ClientModel client = realm.getClientById(clientUUID);
clients.add(client);
}
}
return clients;
}
public List<UserSessionModel> findOfflineSessions(RealmModel realm, ClientModel client, UserModel user) {
List<ClientSessionModel> clientSessions = kcSession.sessions().getOfflineClientSessions(realm, user);
List<UserSessionModel> userSessions = new LinkedList<>();
for (ClientSessionModel clientSession : clientSessions) {
userSessions.add(clientSession.getUserSession());
}
return userSessions;
public List<UserSessionModel> findOfflineSessions(RealmModel realm, UserModel user) {
return kcSession.sessions().getOfflineUserSessions(realm, user);
}
public boolean revokeOfflineToken(UserModel user, ClientModel client) {
RealmModel realm = client.getRealm();
List<ClientSessionModel> clientSessions = kcSession.sessions().getOfflineClientSessions(realm, user);
List<UserSessionModel> userSessions = kcSession.sessions().getOfflineUserSessions(realm, user);
boolean anyRemoved = false;
for (ClientSessionModel clientSession : clientSessions) {
if (clientSession.getClient().getId().equals(client.getId())) {
for (UserSessionModel userSession : userSessions) {
ClientLoginSessionModel clientSession = userSession.getClientLoginSessions().get(client.getId());
if (clientSession != null) {
if (logger.isTraceEnabled()) {
logger.tracef("Removing existing offline token for user '%s' and client '%s' . ClientSessionID was '%s' .",
user.getUsername(), client.getClientId(), clientSession.getId());
logger.tracef("Removing existing offline token for user '%s' and client '%s' .",
user.getUsername(), client.getClientId());
}
kcSession.sessions().removeOfflineClientSession(realm, clientSession.getId());
userSession.getClientLoginSessions().remove(client.getClientId());
persister.removeClientSession(clientSession.getId(), true);
checkOfflineUserSessionHasClientSessions(realm, user, clientSession.getUserSession(), clientSessions);
checkOfflineUserSessionHasClientSessions(realm, user, userSession);
anyRemoved = true;
}
}
@ -124,7 +122,7 @@ public class UserSessionManager {
persister.removeUserSession(userSession.getId(), true);
}
public boolean isOfflineTokenAllowed(ClientSessionModel clientSession) {
public boolean isOfflineTokenAllowed(ClientLoginSessionModel clientSession) {
RoleModel offlineAccessRole = clientSession.getRealm().getRole(Constants.OFFLINE_ACCESS_ROLE);
if (offlineAccessRole == null) {
ServicesLogger.LOGGER.roleNotInRealm(Constants.OFFLINE_ACCESS_ROLE);
@ -144,30 +142,27 @@ public class UserSessionManager {
return offlineUserSession;
}
private void createOfflineClientSession(UserModel user, ClientSessionModel clientSession, UserSessionModel userSession) {
private void createOfflineClientSession(UserModel user, ClientLoginSessionModel clientSession, UserSessionModel offlineUserSession) {
if (logger.isTraceEnabled()) {
logger.tracef("Creating new offline token client session. ClientSessionId: '%s', UserSessionID: '%s' , Username: '%s', Client: '%s'" ,
clientSession.getId(), userSession.getId(), user.getUsername(), clientSession.getClient().getClientId());
clientSession.getId(), offlineUserSession.getId(), user.getUsername(), clientSession.getClient().getClientId());
}
ClientSessionModel offlineClientSession = kcSession.sessions().createOfflineClientSession(clientSession);
offlineClientSession.setUserSession(userSession);
persister.createClientSession(clientSession, true);
ClientLoginSessionModel offlineClientSession = kcSession.sessions().createOfflineClientSession(clientSession);
offlineUserSession.getClientLoginSessions().put(clientSession.getClient().getId(), offlineClientSession);
persister.createClientSession(offlineUserSession, clientSession, true);
}
// Check if userSession has any offline clientSessions attached to it. Remove userSession if not
private void checkOfflineUserSessionHasClientSessions(RealmModel realm, UserModel user, UserSessionModel userSession, List<ClientSessionModel> clientSessions) {
String userSessionId = userSession.getId();
for (ClientSessionModel clientSession : clientSessions) {
if (clientSession.getUserSession().getId().equals(userSessionId)) {
return;
}
private void checkOfflineUserSessionHasClientSessions(RealmModel realm, UserModel user, UserSessionModel userSession) {
if (userSession.getClientLoginSessions().size() > 0) {
return;
}
if (logger.isTraceEnabled()) {
logger.tracef("Removing offline userSession for user %s as it doesn't have any client sessions attached. UserSessionID: %s", user.getUsername(), userSessionId);
logger.tracef("Removing offline userSession for user %s as it doesn't have any client sessions attached. UserSessionID: %s", user.getUsername(), userSession.getId());
}
kcSession.sessions().removeOfflineUserSession(realm, userSession);
persister.removeUserSession(userSessionId, true);
persister.removeUserSession(userSession.getId(), true);
}
}

Some files were not shown because too many files have changed in this diff Show more