KEYCLOAK-983 Fix login after reset-password

This commit is contained in:
Stian Thorgersen 2015-01-21 10:33:33 +01:00
parent 715482e371
commit e7a792f922
9 changed files with 117 additions and 28 deletions

View file

@ -53,17 +53,24 @@ public class ClientSessionAdapter implements ClientSessionModel {
@Override
public void setUserSession(UserSessionModel userSession) {
if (entity.getUserSession() != null) {
if (entity.getUserSession().equals(userSession.getId())) {
return;
} else {
provider.dettachSession(userSession, this);
if (userSession == null) {
if (entity.getUserSession() != null) {
provider.dettachSession(getUserSession(), this);
}
entity.setUserSession(null);
} else {
provider.attachSession(userSession, this);
}
if (entity.getUserSession() != null) {
if (entity.getUserSession().equals(userSession.getId())) {
return;
} else {
provider.dettachSession(userSession, this);
}
} else {
provider.attachSession(userSession, this);
}
entity.setUserSession(userSession.getId());
entity.setUserSession(userSession.getId());
}
update();
}

View file

@ -87,10 +87,17 @@ public class ClientSessionAdapter implements ClientSessionModel {
@Override
public void setUserSession(UserSessionModel userSession) {
UserSessionAdapter adapter = (UserSessionAdapter)userSession;
UserSessionEntity userSessionEntity = adapter.getEntity();
entity.setSession(userSessionEntity);
userSessionEntity.getClientSessions().add(entity);
if (userSession == null) {
if (entity.getSession() != null) {
entity.getSession().getClientSessions().remove(entity);
}
entity.setSession(null);
} else {
UserSessionAdapter adapter = (UserSessionAdapter) userSession;
UserSessionEntity userSessionEntity = adapter.getEntity();
entity.setSession(userSessionEntity);
userSessionEntity.getClientSessions().add(entity);
}
}
@Override
@ -109,6 +116,13 @@ public class ClientSessionAdapter implements ClientSessionModel {
entity.getRoles().add(roleEntity);
}
} else {
if (entity.getRoles() != null) {
for (ClientSessionRoleEntity r : entity.getRoles()) {
em.remove(r);
}
entity.getRoles().clear();
}
}
}

View file

@ -155,6 +155,9 @@ public class JpaUserSessionProvider implements UserSessionProvider {
public void removeUserSession(RealmModel realm, UserSessionModel session) {
UserSessionEntity entity = em.find(UserSessionEntity.class, session.getId());
if (entity != null) {
for (ClientSessionEntity c : entity.getClientSessions()) {
em.remove(c);
}
em.remove(entity);
}
}

View file

@ -59,7 +59,7 @@ public class UserSessionEntity {
@Column(name="USER_SESSION_STATE")
protected UserSessionModel.State state;
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="session")
@OneToMany(mappedBy="session")
protected Collection<ClientSessionEntity> clientSessions = new ArrayList<ClientSessionEntity>();
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="userSession")

View file

@ -52,10 +52,17 @@ public class ClientSessionAdapter implements ClientSessionModel {
@Override
public void setUserSession(UserSessionModel userSession) {
UserSessionAdapter adapter = (UserSessionAdapter)userSession;
UserSessionEntity userSessionEntity = adapter.getEntity();
entity.setSession(userSessionEntity);
userSessionEntity.getClientSessions().add(entity);
if (userSession == null) {
if (entity.getSession() != null) {
entity.getSession().getClientSessions().remove(entity);
}
entity.setSession(null);
} else {
UserSessionAdapter adapter = (UserSessionAdapter) userSession;
UserSessionEntity userSessionEntity = adapter.getEntity();
entity.setSession(userSessionEntity);
userSessionEntity.getClientSessions().add(entity);
}
}
@Override

View file

@ -55,11 +55,19 @@ public class ClientSessionAdapter extends AbstractMongoAdapter<MongoClientSessio
@Override
public void setUserSession(UserSessionModel userSession) {
MongoUserSessionEntity userSessionEntity = provider.getUserSessionEntity(realm, userSession.getId());
entity.setSessionId(userSessionEntity.getId());
updateMongoEntity();
if (userSession == null) {
if (entity.getSessionId() != null) {
MongoUserSessionEntity userSessionEntity = provider.getUserSessionEntity(realm, entity.getSessionId());
provider.getMongoStore().pullItemFromList(userSessionEntity, "clientSessions", entity.getSessionId(), invocationContext);
}
entity.setSessionId(null);
} else {
MongoUserSessionEntity userSessionEntity = provider.getUserSessionEntity(realm, userSession.getId());
entity.setSessionId(userSessionEntity.getId());
updateMongoEntity();
provider.getMongoStore().pushItemToList(userSessionEntity, "clientSessions", entity.getId(), true, invocationContext);
provider.getMongoStore().pushItemToList(userSessionEntity, "clientSessions", entity.getId(), true, invocationContext);
}
}
@Override
@ -70,9 +78,13 @@ public class ClientSessionAdapter extends AbstractMongoAdapter<MongoClientSessio
@Override
public void setRoles(Set<String> roles) {
List<String> list = new LinkedList<String>();
list.addAll(roles);
entity.setRoles(list);
if (roles == null) {
entity.setRoles(null);
} else {
List<String> list = new LinkedList<String>();
list.addAll(roles);
entity.setRoles(list);
}
updateMongoEntity();
}

View file

@ -17,6 +17,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
@ -137,10 +138,21 @@ public class TokenManager {
requestedRoles.add(r.getId());
}
clientSession.setRoles(requestedRoles);
}
public static void dettachClientSession(UserSessionProvider sessions, RealmModel realm, ClientSessionModel clientSession) {
UserSessionModel userSession = clientSession.getUserSession();
if (userSession == null) {
return;
}
clientSession.setUserSession(null);
clientSession.setRoles(null);
if (userSession.getClientSessions().isEmpty()) {
sessions.removeUserSession(realm, userSession);
}
}
public static Set<RoleModel> getAccess(String scopeParam, ClientModel client, UserModel user) {
// todo scopeParam is ignored until we figure out a scheme that fits with openid connect

View file

@ -200,7 +200,10 @@ public class LoginActionsService {
ClientSessionCode clientSessionCode = checks.clientCode;
ClientSessionModel clientSession = clientSessionCode.getClientSession();
if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD)) {
TokenManager.dettachClientSession(session.sessions(), realm, clientSession);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
}
LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo)
.setClientSessionCode(clientSessionCode.getCode());
@ -267,7 +270,7 @@ public class LoginActionsService {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.");
}
ClientSessionModel clientSession = clientCode.getClientSession();
if (!(clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientCode.isValid(ClientSessionModel.Action.RECOVER_PASSWORD))) {
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientSession.getUserSession() != null) {
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
event.client(clientSession.getClient()).error(Errors.INVALID_CODE);
return Flows.forms(this.session, realm, clientSession.getClient(), uriInfo).setError(Messages.INVALID_USER)

View file

@ -27,6 +27,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.Event;
import org.keycloak.events.EventType;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
@ -151,6 +152,36 @@ public class ResetPasswordTest {
Assert.assertEquals("Unknown code, please login again through your application.", errorPage.getError());
}
@Test
public void resetPasswordCancelChangeUser() throws IOException, MessagingException {
loginPage.open();
loginPage.resetPassword();
resetPasswordPage.assertCurrent();
resetPasswordPage.changePassword("test-user@localhost");
resetPasswordPage.assertCurrent();
events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).detail(Details.USERNAME, "test-user@localhost").detail(Details.EMAIL, "test-user@localhost").assertEvent().getSessionId();
resetPasswordPage.backToLogin();
Assert.assertTrue(loginPage.isCurrent());
loginPage.login("login@test.com", "password");
Event loginEvent = events.expectLogin().user(userId).detail(Details.USERNAME, "login@test.com").assertEvent();
String code = oauth.getCurrentQuery().get("code");
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, tokenResponse.getStatusCode());
Assert.assertEquals(userId, oauth.verifyToken(tokenResponse.getAccessToken()).getSubject());
events.expectCodeToToken(loginEvent.getDetails().get(Details.CODE_ID), loginEvent.getSessionId()).user(userId).assertEvent();
}
@Test
public void resetPasswordByEmail() throws IOException, MessagingException {
resetPassword("login@test.com");