KEYCLOAK-5350
This commit is contained in:
parent
3a7fd9e732
commit
64f8d7ce25
10 changed files with 533 additions and 19 deletions
|
@ -26,6 +26,7 @@ import org.keycloak.common.util.Time;
|
|||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OfflineUserSessionModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -839,12 +840,22 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
entity.setBrokerSessionId(userSession.getBrokerSessionId());
|
||||
entity.setBrokerUserId(userSession.getBrokerUserId());
|
||||
entity.setIpAddress(userSession.getIpAddress());
|
||||
entity.setLoginUsername(userSession.getLoginUsername());
|
||||
entity.setNotes(userSession.getNotes() == null ? new ConcurrentHashMap<>() : userSession.getNotes());
|
||||
entity.setAuthenticatedClientSessions(new AuthenticatedClientSessionStore());
|
||||
entity.setRememberMe(userSession.isRememberMe());
|
||||
entity.setState(userSession.getState());
|
||||
if (userSession instanceof OfflineUserSessionModel) {
|
||||
// this is a hack so that UserModel doesn't have to be available when offline token is imported.
|
||||
// see related JIRA - KEYCLOAK-5350 and corresponding test
|
||||
OfflineUserSessionModel oline = (OfflineUserSessionModel)userSession;
|
||||
entity.setUser(oline.getUserId());
|
||||
// NOTE: Hack
|
||||
// We skip calling entity.setLoginUsername(userSession.getLoginUsername())
|
||||
|
||||
} else {
|
||||
entity.setLoginUsername(userSession.getLoginUsername());
|
||||
entity.setUser(userSession.getUser().getId());
|
||||
}
|
||||
|
||||
entity.setStarted(userSession.getStarted());
|
||||
entity.setLastSessionRefresh(userSession.getLastSessionRefresh());
|
||||
|
|
|
@ -176,8 +176,14 @@ public class UserSessionAdapter implements UserSessionModel {
|
|||
|
||||
@Override
|
||||
public String getLoginUsername() {
|
||||
if (entity.getLoginUsername() == null) {
|
||||
// this is a hack so that UserModel doesn't have to be available when offline token is imported.
|
||||
// see related JIRA - KEYCLOAK-5350 and corresponding test
|
||||
return getUser().getUsername();
|
||||
} else {
|
||||
return entity.getLoginUsername();
|
||||
}
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return entity.getIpAddress();
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.models.jpa.session;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -42,6 +43,7 @@ import java.util.Map;
|
|||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class JpaUserSessionPersisterProvider implements UserSessionPersisterProvider {
|
||||
private static final Logger logger = Logger.getLogger(JpaUserSessionPersisterProvider.class);
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final EntityManager em;
|
||||
|
@ -205,15 +207,19 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
List<String> userSessionIds = new ArrayList<>();
|
||||
for (PersistentUserSessionEntity entity : results) {
|
||||
RealmModel realm = session.realms().getRealm(entity.getRealmId());
|
||||
try {
|
||||
UserModel user = session.users().getUserById(entity.getUserId(), realm);
|
||||
|
||||
// Case when user was deleted in the meantime
|
||||
if (user == null) {
|
||||
onUserRemoved(realm, entity.getUserId());
|
||||
return loadUserSessions(firstResult, maxResults, offline);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debugv(e,"Failed to load user with id {0}", entity.getUserId());
|
||||
}
|
||||
|
||||
result.add(toAdapter(realm, user, entity));
|
||||
|
||||
result.add(toAdapter(realm, entity));
|
||||
userSessionIds.add(entity.getUserSessionId());
|
||||
}
|
||||
|
||||
|
@ -247,14 +253,14 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
return result;
|
||||
}
|
||||
|
||||
private PersistentUserSessionAdapter toAdapter(RealmModel realm, UserModel user, PersistentUserSessionEntity entity) {
|
||||
private PersistentUserSessionAdapter toAdapter(RealmModel realm, PersistentUserSessionEntity entity) {
|
||||
PersistentUserSessionModel model = new PersistentUserSessionModel();
|
||||
model.setUserSessionId(entity.getUserSessionId());
|
||||
model.setLastSessionRefresh(entity.getLastSessionRefresh());
|
||||
model.setData(entity.getData());
|
||||
|
||||
Map<String, AuthenticatedClientSessionModel> clientSessions = new HashMap<>();
|
||||
return new PersistentUserSessionAdapter(model, realm, user, clientSessions);
|
||||
return new PersistentUserSessionAdapter(session, model, realm, entity.getUserId(), clientSessions);
|
||||
}
|
||||
|
||||
private PersistentAuthenticatedClientSessionAdapter toAdapter(RealmModel realm, PersistentUserSessionAdapter userSession, PersistentClientSessionEntity entity) {
|
||||
|
@ -263,7 +269,7 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
|
|||
PersistentClientSessionModel model = new PersistentClientSessionModel();
|
||||
model.setClientId(entity.getClientId());
|
||||
model.setUserSessionId(userSession.getId());
|
||||
model.setUserId(userSession.getUser().getId());
|
||||
model.setUserId(userSession.getUserId());
|
||||
model.setTimestamp(entity.getTimestamp());
|
||||
model.setData(entity.getData());
|
||||
return new PersistentAuthenticatedClientSessionAdapter(model, realm, client, userSession);
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Hacked extension to UserSessionModel so that user id can be obtain directly so
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface OfflineUserSessionModel extends UserSessionModel {
|
||||
public String getUserId();
|
||||
}
|
|
@ -19,7 +19,9 @@ package org.keycloak.models.session;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.OfflineUserSessionModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
@ -33,11 +35,14 @@ import java.util.Map;
|
|||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class PersistentUserSessionAdapter implements UserSessionModel {
|
||||
public class PersistentUserSessionAdapter implements OfflineUserSessionModel {
|
||||
|
||||
private final PersistentUserSessionModel model;
|
||||
private final UserModel user;
|
||||
private UserModel user;
|
||||
private String userId;
|
||||
private String username;
|
||||
private final RealmModel realm;
|
||||
private KeycloakSession session;
|
||||
private final Map<String, AuthenticatedClientSessionModel> authenticatedClientSessions;
|
||||
|
||||
private PersistentUserSessionData data;
|
||||
|
@ -60,14 +65,16 @@ public class PersistentUserSessionAdapter implements UserSessionModel {
|
|||
this.model.setLastSessionRefresh(other.getLastSessionRefresh());
|
||||
|
||||
this.user = other.getUser();
|
||||
this.userId = this.user.getId();
|
||||
this.realm = other.getRealm();
|
||||
this.authenticatedClientSessions = other.getAuthenticatedClientSessions();
|
||||
}
|
||||
|
||||
public PersistentUserSessionAdapter(PersistentUserSessionModel model, RealmModel realm, UserModel user, Map<String, AuthenticatedClientSessionModel> clientSessions) {
|
||||
public PersistentUserSessionAdapter(KeycloakSession session, PersistentUserSessionModel model, RealmModel realm, String userId, Map<String, AuthenticatedClientSessionModel> clientSessions) {
|
||||
this.session = session;
|
||||
this.model = model;
|
||||
this.realm = realm;
|
||||
this.user = user;
|
||||
this.userId = userId;
|
||||
this.authenticatedClientSessions = clientSessions;
|
||||
}
|
||||
|
||||
|
@ -113,9 +120,17 @@ public class PersistentUserSessionAdapter implements UserSessionModel {
|
|||
|
||||
@Override
|
||||
public UserModel getUser() {
|
||||
if (user == null) {
|
||||
user = session.users().getUserById(userId, realm);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm() {
|
||||
return realm;
|
||||
|
@ -123,7 +138,7 @@ public class PersistentUserSessionAdapter implements UserSessionModel {
|
|||
|
||||
@Override
|
||||
public String getLoginUsername() {
|
||||
return user.getUsername();
|
||||
return getUser().getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -349,6 +349,13 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory {
|
|||
throw new AssertionError("No type received within timeout");
|
||||
}
|
||||
}
|
||||
public Event event() {
|
||||
try {
|
||||
return events.poll(10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError("No type received within timeout");
|
||||
}
|
||||
}
|
||||
|
||||
public Event assertEvent(Event actual) {
|
||||
if (expected.getError() != null && !expected.getType().toString().endsWith("_ERROR")) {
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.federation.storage;
|
||||
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.credential.CredentialInput;
|
||||
import org.keycloak.credential.CredentialInputUpdater;
|
||||
import org.keycloak.credential.CredentialInputValidator;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.UserModelDelegate;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.user.ImportedUserValidation;
|
||||
import org.keycloak.storage.user.UserLookupProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class FailableHardcodedStorageProvider implements UserStorageProvider, UserLookupProvider, ImportedUserValidation, CredentialInputUpdater, CredentialInputValidator {
|
||||
|
||||
public static String username = "billb";
|
||||
public static String password = "password";
|
||||
public static String email = "billb@nowhere.com";
|
||||
public static String first = "Bill";
|
||||
public static String last = "Burke";
|
||||
public static MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
|
||||
|
||||
public static boolean fail;
|
||||
|
||||
protected ComponentModel model;
|
||||
protected KeycloakSession session;
|
||||
protected boolean componentFail;
|
||||
|
||||
public FailableHardcodedStorageProvider(ComponentModel model, KeycloakSession session) {
|
||||
this.model = model;
|
||||
this.session = session;
|
||||
componentFail = model.getConfig().getFirst("fail") != null && model.getConfig().getFirst("fail").equalsIgnoreCase("true");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCredentialType(String credentialType) {
|
||||
if (fail || componentFail) throw new RuntimeException("FORCED FAILURE");
|
||||
return CredentialModel.PASSWORD.equals(credentialType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (fail || componentFail) throw new RuntimeException("FORCED FAILURE");
|
||||
if (!(input instanceof UserCredentialModel)) return false;
|
||||
if (!user.getUsername().equals(username)) throw new RuntimeException("UNKNOWN USER!");
|
||||
|
||||
if (input.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
password = ((UserCredentialModel)input).getValue();
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
|
||||
if (fail || componentFail) throw new RuntimeException("FORCED FAILURE");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
|
||||
if (fail || componentFail) throw new RuntimeException("FORCED FAILURE");
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
|
||||
if (fail || componentFail) throw new RuntimeException("FORCED FAILURE");
|
||||
return CredentialModel.PASSWORD.equals(credentialType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
|
||||
if (fail || componentFail) throw new RuntimeException("FORCED FAILURE");
|
||||
if (!(input instanceof UserCredentialModel)) return false;
|
||||
if (!user.getUsername().equals("billb")) throw new RuntimeException("UNKNOWN USER!");
|
||||
if (input.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||
return password != null && password.equals( ((UserCredentialModel)input).getValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Delegate extends UserModelDelegate {
|
||||
public Delegate(UserModel delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsername(String name) {
|
||||
super.setUsername(name);
|
||||
name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSingleAttribute(String name, String value) {
|
||||
super.setSingleAttribute(name, value);
|
||||
attributes.putSingle(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, List<String> values) {
|
||||
super.setAttribute(name, values);
|
||||
attributes.put(name, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
super.removeAttribute(name);
|
||||
attributes.remove(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFirstName(String firstName) {
|
||||
super.setFirstName(firstName);
|
||||
first = firstName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastName(String lastName) {
|
||||
super.setLastName(lastName);
|
||||
last = lastName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEmail(String em) {
|
||||
super.setEmail(em);
|
||||
email = em;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel validate(RealmModel realm, UserModel user) {
|
||||
if (fail || componentFail) throw new RuntimeException("FORCED FAILURE");
|
||||
return new Delegate(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserById(String id, RealmModel realm) {
|
||||
if (fail || componentFail) throw new RuntimeException("FORCED FAILURE");
|
||||
throw new RuntimeException("THIS IMPORTS SHOULD NEVER BE CALLED");
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(String uname, RealmModel realm) {
|
||||
if (fail || componentFail) throw new RuntimeException("FORCED FAILURE");
|
||||
if (!username.equals(uname)) return null;
|
||||
UserModel local = session.userLocalStorage().getUserByUsername(uname, realm);
|
||||
if (local != null && !model.getId().equals(local.getFederationLink())) {
|
||||
throw new RuntimeException("local storage has wrong federation link");
|
||||
}
|
||||
if (local != null) return new Delegate(local);
|
||||
local = session.userLocalStorage().addUser(realm, uname);
|
||||
local.setEnabled(true);
|
||||
local.setFirstName(first);
|
||||
local.setLastName(last);
|
||||
local.setEmail(email);
|
||||
local.setFederationLink(model.getId());
|
||||
for (String key : attributes.keySet()) {
|
||||
List<String> values = attributes.get(key);
|
||||
if (values == null) continue;
|
||||
local.setAttribute(key, values);
|
||||
}
|
||||
return new Delegate(local);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(String email, RealmModel realm) {
|
||||
if (fail || componentFail) throw new RuntimeException("FORCED FAILURE");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.federation.storage;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class FailableHardcodedStorageProviderFactory implements UserStorageProviderFactory<FailableHardcodedStorageProvider> {
|
||||
|
||||
public static final String PROVIDER_ID = "failable-hardcoded-storage";
|
||||
|
||||
@Override
|
||||
public FailableHardcodedStorageProvider create(KeycloakSession session, ComponentModel model) {
|
||||
return new FailableHardcodedStorageProvider(model, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
static List<ProviderConfigProperty> OPTIONS = new LinkedList<>();
|
||||
static {
|
||||
ProviderConfigProperty prop = new ProviderConfigProperty("fail", "fail", "If on, provider will throw exception", ProviderConfigProperty.BOOLEAN_TYPE, "false");
|
||||
OPTIONS.add(prop);
|
||||
}
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return OPTIONS;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.federation.storage;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.common.constants.ServiceAccountConstants;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.credential.CredentialAuthentication;
|
||||
import org.keycloak.credential.UserCredentialStoreManager;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.cache.CachedUserModel;
|
||||
import org.keycloak.models.cache.infinispan.UserAdapter;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.RefreshToken;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.Constants;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserStorageFailureTest {
|
||||
public static ComponentModel memoryProvider = null;
|
||||
@ClassRule
|
||||
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
UserStorageProviderModel model = new UserStorageProviderModel();
|
||||
model.setName("failure");
|
||||
model.setPriority(0);
|
||||
model.setProviderId(FailableHardcodedStorageProviderFactory.PROVIDER_ID);
|
||||
model.setParentId(appRealm.getId());
|
||||
memoryProvider = appRealm.addComponentModel(model);
|
||||
|
||||
ClientModel offlineClient = appRealm.addClient("offline-client");
|
||||
offlineClient.setEnabled(true);
|
||||
offlineClient.setDirectAccessGrantsEnabled(true);
|
||||
offlineClient.setSecret("secret");
|
||||
HashSet<String> redirects = new HashSet<>();
|
||||
redirects.add(Constants.AUTH_SERVER_ROOT + "/offline-client");
|
||||
offlineClient.setRedirectUris(redirects);
|
||||
offlineClient.setServiceAccountsEnabled(true);
|
||||
offlineClient.setFullScopeAllowed(true);
|
||||
|
||||
UserModel serviceAccount = manager.getSession().users().addUser(appRealm, ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + offlineClient.getClientId());
|
||||
serviceAccount.setEnabled(true);
|
||||
RoleModel role = appRealm.getRole("offline_access");
|
||||
Assert.assertNotNull(role);
|
||||
serviceAccount.grantRole(role);
|
||||
serviceAccount.setServiceAccountClientLink(offlineClient.getClientId());
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@Rule
|
||||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
@WebResource
|
||||
protected OAuthClient oauth;
|
||||
|
||||
@WebResource
|
||||
protected WebDriver driver;
|
||||
|
||||
@WebResource
|
||||
protected AppPage appPage;
|
||||
|
||||
@WebResource
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(keycloakRule);
|
||||
|
||||
|
||||
// this is a hack so that UserModel doesn't have to be available when offline token is imported.
|
||||
// see related JIRA - KEYCLOAK-5350 and corresponding test
|
||||
|
||||
/**
|
||||
* KEYCLOAK-5350
|
||||
*/
|
||||
@Test
|
||||
public void testKeycloak5350() {
|
||||
oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
|
||||
oauth.clientId("offline-client");
|
||||
oauth.redirectUri(Constants.AUTH_SERVER_ROOT + "/offline-client");
|
||||
oauth.doLogin("billb", "password");
|
||||
|
||||
Event loginEvent = events.expectLogin()
|
||||
.client("offline-client")
|
||||
.detail(Details.REDIRECT_URI, Constants.AUTH_SERVER_ROOT + "/offline-client")
|
||||
.event();
|
||||
|
||||
final String sessionId = loginEvent.getSessionId();
|
||||
String codeId = loginEvent.getDetails().get(Details.CODE_ID);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "secret");
|
||||
AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
|
||||
String offlineTokenString = tokenResponse.getRefreshToken();
|
||||
RefreshToken offlineToken = oauth.verifyRefreshToken(offlineTokenString);
|
||||
events.clear();
|
||||
|
||||
FailableHardcodedStorageProvider.fail = true;
|
||||
// restart server to make sure we can still boot if user storage is down
|
||||
keycloakRule.restartServer();
|
||||
|
||||
// test that once user storage provider is available again we can still access the token.
|
||||
FailableHardcodedStorageProvider.fail = false;
|
||||
tokenResponse = oauth.doRefreshTokenRequest(offlineTokenString, "secret");
|
||||
Assert.assertNotNull(tokenResponse.getAccessToken());
|
||||
token = oauth.verifyToken(tokenResponse.getAccessToken());
|
||||
offlineTokenString = tokenResponse.getRefreshToken();
|
||||
offlineToken = oauth.verifyRefreshToken(offlineTokenString);
|
||||
events.clear();
|
||||
}
|
||||
|
||||
@After
|
||||
public void resetTimeoffset() {
|
||||
Time.setOffset(0);
|
||||
|
||||
}
|
||||
|
||||
//@Test
|
||||
public void testIDE() throws Exception {
|
||||
Thread.sleep(100000000);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
org.keycloak.testsuite.federation.sync.SyncDummyUserFederationProviderFactory
|
||||
org.keycloak.testsuite.federation.storage.UserPropertyFileStorageFactory
|
||||
org.keycloak.testsuite.federation.storage.UserMapStorageFactory
|
||||
org.keycloak.testsuite.federation.storage.FailableHardcodedStorageProviderFactory
|
Loading…
Reference in a new issue