KEYCLOAK-4626 AuthenticationSessions: start
This commit is contained in:
parent
f392e79ad7
commit
83b29c5080
114 changed files with 4441 additions and 3633 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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> {
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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()");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue