fix deadlocks
This commit is contained in:
parent
d5b34a0b41
commit
9410adf9ce
10 changed files with 313 additions and 61 deletions
2
model/api/src/main/java/org/keycloak/models/utils/Pbkdf2PasswordEncoder.java
Normal file → Executable file
2
model/api/src/main/java/org/keycloak/models/utils/Pbkdf2PasswordEncoder.java
Normal file → Executable file
|
@ -24,7 +24,7 @@ public class Pbkdf2PasswordEncoder {
|
||||||
public static final String RNG_ALGORITHM = "SHA1PRNG";
|
public static final String RNG_ALGORITHM = "SHA1PRNG";
|
||||||
|
|
||||||
private static final int DERIVED_KEY_SIZE = 512;
|
private static final int DERIVED_KEY_SIZE = 512;
|
||||||
private static final int ITERATIONS = 20000;
|
private static final int ITERATIONS = 1;
|
||||||
|
|
||||||
private final int iterations;
|
private final int iterations;
|
||||||
private byte[] salt;
|
private byte[] salt;
|
||||||
|
|
|
@ -950,7 +950,12 @@ public class RealmAdapter implements RealmModel {
|
||||||
@Override
|
@Override
|
||||||
public List<AuthenticationProviderModel> getAuthenticationProviders() {
|
public List<AuthenticationProviderModel> getAuthenticationProviders() {
|
||||||
List<AuthenticationProviderEntity> entities = realm.getAuthenticationProviders();
|
List<AuthenticationProviderEntity> entities = realm.getAuthenticationProviders();
|
||||||
Collections.sort(entities, new Comparator<AuthenticationProviderEntity>() {
|
List<AuthenticationProviderEntity> copy = new ArrayList<AuthenticationProviderEntity>();
|
||||||
|
for (AuthenticationProviderEntity entity : entities) {
|
||||||
|
copy.add(entity);
|
||||||
|
|
||||||
|
}
|
||||||
|
Collections.sort(copy, new Comparator<AuthenticationProviderEntity>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(AuthenticationProviderEntity o1, AuthenticationProviderEntity o2) {
|
public int compare(AuthenticationProviderEntity o1, AuthenticationProviderEntity o2) {
|
||||||
|
@ -959,7 +964,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
|
|
||||||
});
|
});
|
||||||
List<AuthenticationProviderModel> result = new ArrayList<AuthenticationProviderModel>();
|
List<AuthenticationProviderModel> result = new ArrayList<AuthenticationProviderModel>();
|
||||||
for (AuthenticationProviderEntity entity : entities) {
|
for (AuthenticationProviderEntity entity : copy) {
|
||||||
result.add(new AuthenticationProviderModel(entity.getProviderName(), entity.isPasswordUpdateSupported(), entity.getConfig()));
|
result.add(new AuthenticationProviderModel(entity.getProviderName(), entity.isPasswordUpdateSupported(), entity.getConfig()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1420,8 +1425,8 @@ public class RealmAdapter implements RealmModel {
|
||||||
@Override
|
@Override
|
||||||
public UserSessionModel createUserSession(UserModel user, String ipAddress) {
|
public UserSessionModel createUserSession(UserModel user, String ipAddress) {
|
||||||
UserSessionEntity entity = new UserSessionEntity();
|
UserSessionEntity entity = new UserSessionEntity();
|
||||||
entity.setRealm(realm);
|
entity.setRealmId(realm.getId());
|
||||||
entity.setUser(((UserAdapter) user).getUser());
|
entity.setUserId(user.getId());
|
||||||
entity.setIpAddress(ipAddress);
|
entity.setIpAddress(ipAddress);
|
||||||
|
|
||||||
int currentTime = Time.currentTime();
|
int currentTime = Time.currentTime();
|
||||||
|
@ -1442,7 +1447,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
@Override
|
@Override
|
||||||
public List<UserSessionModel> getUserSessions(UserModel user) {
|
public List<UserSessionModel> getUserSessions(UserModel user) {
|
||||||
List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
|
List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
|
||||||
for (UserSessionEntity e : em.createNamedQuery("getUserSessionByUser", UserSessionEntity.class).setParameter("user", ((UserAdapter) user).getUser()).getResultList()) {
|
for (UserSessionEntity e : em.createNamedQuery("getUserSessionByUser", UserSessionEntity.class).setParameter("userId", user.getId()).getResultList()) {
|
||||||
sessions.add(new UserSessionAdapter(em, this, e));
|
sessions.add(new UserSessionAdapter(em, this, e));
|
||||||
}
|
}
|
||||||
return sessions;
|
return sessions;
|
||||||
|
@ -1455,8 +1460,8 @@ public class RealmAdapter implements RealmModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeUserSessions() {
|
public void removeUserSessions() {
|
||||||
em.createNamedQuery("removeClientUserSessionByRealm").setParameter("realm", realm).executeUpdate();
|
em.createNamedQuery("removeClientUserSessionByRealm").setParameter("realmId", realm.getId()).executeUpdate();
|
||||||
em.createNamedQuery("removeRealmUserSessions").setParameter("realm", realm).executeUpdate();
|
em.createNamedQuery("removeRealmUserSessions").setParameter("realmId", realm.getId()).executeUpdate();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1466,8 +1471,8 @@ public class RealmAdapter implements RealmModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeUserSessions(UserEntity user) {
|
private void removeUserSessions(UserEntity user) {
|
||||||
em.createNamedQuery("removeClientUserSessionByUser").setParameter("user", user).executeUpdate();
|
em.createNamedQuery("removeClientUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
|
||||||
em.createNamedQuery("removeUserSessionByUser").setParameter("user", user).executeUpdate();
|
em.createNamedQuery("removeUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -43,12 +43,12 @@ public class UserSessionAdapter implements UserSessionModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getUser() {
|
public UserModel getUser() {
|
||||||
return new UserAdapter(entity.getUser());
|
return realm.getUserById(entity.getUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUser(UserModel user) {
|
public void setUser(UserModel user) {
|
||||||
entity.setUser(((UserAdapter) user).getUser());
|
entity.setUserId(user.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -89,8 +89,8 @@ public class UserSessionAdapter implements UserSessionModel {
|
||||||
ClientUserSessionAssociationEntity association = new ClientUserSessionAssociationEntity();
|
ClientUserSessionAssociationEntity association = new ClientUserSessionAssociationEntity();
|
||||||
association.setClientId(client.getId());
|
association.setClientId(client.getId());
|
||||||
association.setSession(entity);
|
association.setSession(entity);
|
||||||
association.setUser(entity.getUser());
|
association.setUserId(entity.getUserId());
|
||||||
association.setRealm(((RealmAdapter)realm).getEntity());
|
association.setRealmId(realm.getId());
|
||||||
em.persist(association);
|
em.persist(association);
|
||||||
entity.getClients().add(association);
|
entity.getClients().add(association);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,22 +21,22 @@ import javax.persistence.NamedQuery;
|
||||||
@NamedQuery(name = "getClientUserSessionByClient", query = "select s from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
|
@NamedQuery(name = "getClientUserSessionByClient", query = "select s from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
|
||||||
@NamedQuery(name = "getActiveClientSessions", query = "select COUNT(s) from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
|
@NamedQuery(name = "getActiveClientSessions", query = "select COUNT(s) from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
|
||||||
@NamedQuery(name = "removeClientUserSessionByClient", query = "delete from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
|
@NamedQuery(name = "removeClientUserSessionByClient", query = "delete from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
|
||||||
@NamedQuery(name = "removeClientUserSessionByUser", query = "delete from ClientUserSessionAssociationEntity s where s.user = :user"),
|
@NamedQuery(name = "removeClientUserSessionByUser", query = "delete from ClientUserSessionAssociationEntity s where s.userId = :userId"),
|
||||||
@NamedQuery(name = "removeClientUserSessionByRealm", query = "delete from ClientUserSessionAssociationEntity s where s.realm = :realm")})
|
@NamedQuery(name = "removeClientUserSessionByRealm", query = "delete from ClientUserSessionAssociationEntity s where s.realmId = :realmId")})
|
||||||
public class ClientUserSessionAssociationEntity {
|
public class ClientUserSessionAssociationEntity {
|
||||||
@Id
|
@Id
|
||||||
@GenericGenerator(name="uuid_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
|
@GenericGenerator(name="uuid_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
|
||||||
@GeneratedValue(generator = "uuid_generator")
|
@GeneratedValue(generator = "uuid_generator")
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
|
// we use ids to avoid select for update contention
|
||||||
|
private String userId;
|
||||||
|
private String realmId;
|
||||||
|
|
||||||
@ManyToOne(fetch= FetchType.LAZY)
|
@ManyToOne(fetch= FetchType.LAZY)
|
||||||
private UserSessionEntity session;
|
private UserSessionEntity session;
|
||||||
|
|
||||||
@ManyToOne(fetch= FetchType.LAZY)
|
|
||||||
private UserEntity user;
|
|
||||||
|
|
||||||
@ManyToOne(fetch= FetchType.LAZY)
|
|
||||||
private RealmEntity realm;
|
|
||||||
|
|
||||||
private String clientId;
|
private String clientId;
|
||||||
|
|
||||||
|
@ -48,14 +48,6 @@ public class ClientUserSessionAssociationEntity {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserEntity getUser() {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUser(UserEntity user) {
|
|
||||||
this.user = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserSessionEntity getSession() {
|
public UserSessionEntity getSession() {
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
@ -64,14 +56,6 @@ public class ClientUserSessionAssociationEntity {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RealmEntity getRealm() {
|
|
||||||
return realm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRealm(RealmEntity realm) {
|
|
||||||
this.realm = realm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClientId() {
|
public String getClientId() {
|
||||||
return clientId;
|
return clientId;
|
||||||
}
|
}
|
||||||
|
@ -79,4 +63,20 @@ public class ClientUserSessionAssociationEntity {
|
||||||
public void setClientId(String clientId) {
|
public void setClientId(String clientId) {
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRealmId() {
|
||||||
|
return realmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealmId(String realmId) {
|
||||||
|
this.realmId = realmId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ public class RealmEntity {
|
||||||
@JoinTable(name="User_RequiredCreds")
|
@JoinTable(name="User_RequiredCreds")
|
||||||
Collection<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
|
Collection<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
|
||||||
|
|
||||||
|
|
||||||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
|
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
|
||||||
@JoinTable(name="AuthProviders")
|
@JoinTable(name="AuthProviders")
|
||||||
List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
|
List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
|
||||||
|
|
|
@ -20,9 +20,9 @@ import java.util.Collection;
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
@NamedQuery(name = "getUserSessionByUser", query = "select s from UserSessionEntity s where s.user = :user"),
|
@NamedQuery(name = "getUserSessionByUser", query = "select s from UserSessionEntity s where s.userId = :userId"),
|
||||||
@NamedQuery(name = "removeRealmUserSessions", query = "delete from UserSessionEntity s where s.realm = :realm"),
|
@NamedQuery(name = "removeRealmUserSessions", query = "delete from UserSessionEntity s where s.realmId = :realmId"),
|
||||||
@NamedQuery(name = "removeUserSessionByUser", query = "delete from UserSessionEntity s where s.user = :user"),
|
@NamedQuery(name = "removeUserSessionByUser", query = "delete from UserSessionEntity s where s.userId = :userId"),
|
||||||
@NamedQuery(name = "getUserSessionExpired", query = "select s from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime"),
|
@NamedQuery(name = "getUserSessionExpired", query = "select s from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime"),
|
||||||
@NamedQuery(name = "removeUserSessionExpired", query = "delete from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime")
|
@NamedQuery(name = "removeUserSessionExpired", query = "delete from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime")
|
||||||
})
|
})
|
||||||
|
@ -33,20 +33,18 @@ public class UserSessionEntity {
|
||||||
@GeneratedValue(generator = "uuid_generator")
|
@GeneratedValue(generator = "uuid_generator")
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@ManyToOne
|
// we use ids to avoid select for update contention
|
||||||
private UserEntity user;
|
private String userId;
|
||||||
|
private String realmId;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
private String ipAddress;
|
||||||
private RealmEntity realm;
|
|
||||||
|
|
||||||
String ipAddress;
|
private int started;
|
||||||
|
|
||||||
int started;
|
private int lastSessionRefresh;
|
||||||
|
|
||||||
int lastSessionRefresh;
|
|
||||||
|
|
||||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy="session")
|
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy="session")
|
||||||
Collection<ClientUserSessionAssociationEntity> clients = new ArrayList<ClientUserSessionAssociationEntity>();
|
private Collection<ClientUserSessionAssociationEntity> clients = new ArrayList<ClientUserSessionAssociationEntity>();
|
||||||
|
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -57,12 +55,21 @@ public class UserSessionEntity {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserEntity getUser() {
|
|
||||||
return user;
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUser(UserEntity user) {
|
public void setUserId(String userId) {
|
||||||
this.user = user;
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRealmId() {
|
||||||
|
return realmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealmId(String realmId) {
|
||||||
|
this.realmId = realmId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getIpAddress() {
|
public String getIpAddress() {
|
||||||
|
@ -97,11 +104,4 @@ public class UserSessionEntity {
|
||||||
this.clients = clients;
|
this.clients = clients;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RealmEntity getRealm() {
|
|
||||||
return realm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRealm(RealmEntity realm) {
|
|
||||||
this.realm = realm;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import javax.servlet.FilterConfig;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +30,7 @@ public class KeycloakSessionServletFilter implements Filter {
|
||||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||||
ProviderSessionFactory providerSessionFactory = (ProviderSessionFactory) servletRequest.getServletContext().getAttribute(ProviderSessionFactory.class.getName());
|
ProviderSessionFactory providerSessionFactory = (ProviderSessionFactory) servletRequest.getServletContext().getAttribute(ProviderSessionFactory.class.getName());
|
||||||
ProviderSession providerSession = providerSessionFactory.createSession();
|
ProviderSession providerSession = providerSessionFactory.createSession();
|
||||||
|
HttpServletRequest request = (HttpServletRequest)servletRequest;
|
||||||
ResteasyProviderFactory.pushContext(ProviderSession.class, providerSession);
|
ResteasyProviderFactory.pushContext(ProviderSession.class, providerSession);
|
||||||
|
|
||||||
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
|
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
|
||||||
|
@ -52,7 +53,7 @@ public class KeycloakSessionServletFilter implements Filter {
|
||||||
}
|
}
|
||||||
catch (RuntimeException ex) {
|
catch (RuntimeException ex) {
|
||||||
if (tx.isActive()) tx.rollback();
|
if (tx.isActive()) tx.rollback();
|
||||||
throw ex;
|
throw new RuntimeException("request path: " + request.getRequestURI(), ex);
|
||||||
} finally {
|
} finally {
|
||||||
providerSession.close();
|
providerSession.close();
|
||||||
ResteasyProviderFactory.clearContextData();
|
ResteasyProviderFactory.clearContextData();
|
||||||
|
|
0
services/src/main/java/org/keycloak/services/scheduled/ClearExpiredUserSessions.java
Normal file → Executable file
0
services/src/main/java/org/keycloak/services/scheduled/ClearExpiredUserSessions.java
Normal file → Executable file
0
testsuite/integration/src/main/resources/log4j.properties
Normal file → Executable file
0
testsuite/integration/src/main/resources/log4j.properties
Normal file → Executable file
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source.
|
||||||
|
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||||
|
* as indicated by the @author tags. See the copyright.txt file in the
|
||||||
|
* distribution for a full listing of individual contributors.
|
||||||
|
*
|
||||||
|
* This is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this software; if not, write to the Free
|
||||||
|
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||||
|
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||||
|
*/
|
||||||
|
package org.keycloak.testsuite.perf;
|
||||||
|
|
||||||
|
import org.apache.http.NameValuePair;
|
||||||
|
import org.apache.http.client.utils.URLEncodedUtils;
|
||||||
|
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
|
||||||
|
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.audit.Details;
|
||||||
|
import org.keycloak.audit.Errors;
|
||||||
|
import org.keycloak.audit.Event;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.keycloak.services.resources.TokenService;
|
||||||
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
|
import org.keycloak.testsuite.Constants;
|
||||||
|
import org.keycloak.testsuite.OAuthClient;
|
||||||
|
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
|
||||||
|
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.keycloak.util.BasicAuthHelper;
|
||||||
|
import org.openqa.selenium.WebDriver;
|
||||||
|
|
||||||
|
import javax.ws.rs.client.Entity;
|
||||||
|
import javax.ws.rs.core.Form;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class AccessTokenPerfTest {
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static KeycloakRule keycloakRule = new KeycloakRule();
|
||||||
|
|
||||||
|
public static class BrowserLogin implements Runnable
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
WebDriver driver = WebRule.createWebDriver();
|
||||||
|
OAuthClient oauth = new OAuthClient(driver);
|
||||||
|
oauth.doLogin("test-user@localhost", "password");
|
||||||
|
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||||
|
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
|
||||||
|
Assert.assertEquals(200, response.getStatusCode());
|
||||||
|
driver.close();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AtomicLong count = new AtomicLong(0);
|
||||||
|
|
||||||
|
public static class JaxrsClientLogin implements Runnable
|
||||||
|
{
|
||||||
|
ResteasyClient client;
|
||||||
|
|
||||||
|
private String baseUrl = Constants.AUTH_SERVER_ROOT;
|
||||||
|
|
||||||
|
private String realm = "test";
|
||||||
|
|
||||||
|
private String responseType = OAuth2Constants.CODE;
|
||||||
|
|
||||||
|
private String grantType = "authorization_code";
|
||||||
|
|
||||||
|
private String clientId = "test-app";
|
||||||
|
|
||||||
|
private String redirectUri = "http://localhost:8081/app/auth";
|
||||||
|
|
||||||
|
|
||||||
|
public JaxrsClientLogin() {
|
||||||
|
this.client = new ResteasyClientBuilder().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLoginFormUrl(String state) {
|
||||||
|
UriBuilder b = TokenService.loginPageUrl(UriBuilder.fromUri(baseUrl));
|
||||||
|
if (responseType != null) {
|
||||||
|
b.queryParam(OAuth2Constants.RESPONSE_TYPE, responseType);
|
||||||
|
}
|
||||||
|
if (clientId != null) {
|
||||||
|
b.queryParam(OAuth2Constants.CLIENT_ID, clientId);
|
||||||
|
}
|
||||||
|
if (redirectUri != null) {
|
||||||
|
b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
|
||||||
|
}
|
||||||
|
if (state != null) {
|
||||||
|
b.queryParam(OAuth2Constants.STATE, state);
|
||||||
|
}
|
||||||
|
return b.build(realm).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProcessLoginUrl(String state) {
|
||||||
|
UriBuilder b = TokenService.processLoginUrl(UriBuilder.fromUri(baseUrl));
|
||||||
|
if (clientId != null) {
|
||||||
|
b.queryParam(OAuth2Constants.CLIENT_ID, clientId);
|
||||||
|
}
|
||||||
|
if (redirectUri != null) {
|
||||||
|
b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
|
||||||
|
}
|
||||||
|
if (state != null) {
|
||||||
|
b.queryParam(OAuth2Constants.STATE, state);
|
||||||
|
}
|
||||||
|
return b.build(realm).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
this.client = new ResteasyClientBuilder().build();
|
||||||
|
String state = "42";
|
||||||
|
Response response = client.target(getLoginFormUrl(state)).request().get();
|
||||||
|
URI uri = null;
|
||||||
|
if (200 == response.getStatus()) {
|
||||||
|
response.close();
|
||||||
|
Form form = new Form();
|
||||||
|
form.param("username", "test-user@localhost");
|
||||||
|
form.param("password", "password");
|
||||||
|
response = client.target(getProcessLoginUrl(state)).request().post(Entity.form(form));
|
||||||
|
|
||||||
|
}
|
||||||
|
Assert.assertEquals(302, response.getStatus());
|
||||||
|
uri = response.getLocation();
|
||||||
|
response.close();
|
||||||
|
|
||||||
|
Assert.assertNotNull(uri);
|
||||||
|
String code = getCode(uri);
|
||||||
|
Assert.assertNotNull(code);
|
||||||
|
|
||||||
|
Form form = new Form();
|
||||||
|
form.param(OAuth2Constants.GRANT_TYPE, grantType)
|
||||||
|
.param(OAuth2Constants.CODE, code)
|
||||||
|
.param(OAuth2Constants.REDIRECT_URI, redirectUri);
|
||||||
|
|
||||||
|
String authorization = BasicAuthHelper.createHeader(clientId, "password");
|
||||||
|
|
||||||
|
String res = client.target(TokenService.accessCodeToTokenUrl(UriBuilder.fromUri(baseUrl)).build(realm)).request()
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, authorization)
|
||||||
|
.post(Entity.form(form), String.class);
|
||||||
|
count.incrementAndGet();
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode(URI uri) {
|
||||||
|
Map<String, String> m = new HashMap<String, String>();
|
||||||
|
List<NameValuePair> pairs = URLEncodedUtils.parse(uri, "UTF-8");
|
||||||
|
for (NameValuePair p : pairs) {
|
||||||
|
if (p.getName().equals("code")) return p.getValue();
|
||||||
|
m.put(p.getName(), p.getValue());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void perfJaxrsClientLogin()
|
||||||
|
{
|
||||||
|
long ITERATIONS = 1;
|
||||||
|
JaxrsClientLogin login = new JaxrsClientLogin();
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < ITERATIONS; i++) {
|
||||||
|
login.run();
|
||||||
|
}
|
||||||
|
long end = System.currentTimeMillis() - start;
|
||||||
|
System.out.println("took: " + end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void perfBrowserLogin() throws Exception
|
||||||
|
{
|
||||||
|
long ITERATIONS = 1;
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < ITERATIONS; i++) {
|
||||||
|
new BrowserLogin().run();
|
||||||
|
}
|
||||||
|
long end = System.currentTimeMillis() - start;
|
||||||
|
System.out.println("took: " + end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiThread() throws Exception {
|
||||||
|
int num_threads = 1;
|
||||||
|
Thread[] threads = new Thread[num_threads];
|
||||||
|
for (int i = 0; i < num_threads; i++) {
|
||||||
|
threads[i] = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
perfJaxrsClientLogin();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < num_threads; i++) {
|
||||||
|
threads[i].start();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < num_threads; i++) {
|
||||||
|
threads[i].join();
|
||||||
|
}
|
||||||
|
long end = System.currentTimeMillis() - start;
|
||||||
|
System.out.println(count.toString() + " took: " + end);
|
||||||
|
System.out.println(count.floatValue() / ((float)end) * 1000+ " logins/s");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue