Skip to content

Commit

Permalink
KEYCLOAK-2129
Browse files Browse the repository at this point in the history
  • Loading branch information
stianst committed Nov 26, 2015
1 parent 30ec60b commit ee363a4
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 41 deletions.
Expand Up @@ -754,7 +754,7 @@ public void checkClientSession() {
if (!code.isValidAction(action)) { if (!code.isValidAction(action)) {
throw new AuthenticationFlowException(AuthenticationFlowError.INVALID_CLIENT_SESSION); throw new AuthenticationFlowException(AuthenticationFlowError.INVALID_CLIENT_SESSION);
} }
if (!code.isActionActive(action)) { if (!code.isActionActive(ClientSessionCode.ActionType.LOGIN)) {
throw new AuthenticationFlowException(AuthenticationFlowError.EXPIRED_CODE); throw new AuthenticationFlowException(AuthenticationFlowError.EXPIRED_CODE);
} }
clientSession.setTimestamp(Time.currentTime()); clientSession.setTimestamp(Time.currentTime());
Expand Down
Expand Up @@ -202,7 +202,7 @@ public Response buildAuthorizationCodeAccessTokenResponse() {


ClientSessionModel clientSession = accessCode.getClientSession(); ClientSessionModel clientSession = accessCode.getClientSession();
event.detail(Details.CODE_ID, clientSession.getId()); event.detail(Details.CODE_ID, clientSession.getId());
if (!accessCode.isValid(ClientSessionModel.Action.CODE_TO_TOKEN.name())) { if (!accessCode.isValid(ClientSessionModel.Action.CODE_TO_TOKEN.name(), ClientSessionCode.ActionType.CLIENT)) {
event.error(Errors.INVALID_CODE); event.error(Errors.INVALID_CODE);
throw new ErrorResponseException("invalid_grant", "Code is expired", Response.Status.BAD_REQUEST); throw new ErrorResponseException("invalid_grant", "Code is expired", Response.Status.BAD_REQUEST);
} }
Expand Down
Expand Up @@ -27,6 +27,12 @@ public class ClientSessionCode {
private final RealmModel realm; private final RealmModel realm;
private final ClientSessionModel clientSession; private final ClientSessionModel clientSession;


public enum ActionType {
CLIENT,
LOGIN,
USER
}

public ClientSessionCode(RealmModel realm, ClientSessionModel clientSession) { public ClientSessionCode(RealmModel realm, ClientSessionModel clientSession) {
this.realm = realm; this.realm = realm;
this.clientSession = clientSession; this.clientSession = clientSession;
Expand Down Expand Up @@ -128,23 +134,29 @@ public ClientSessionModel getClientSession() {
return clientSession; return clientSession;
} }


public boolean isValid(String requestedAction) { public boolean isValid(String requestedAction, ActionType actionType) {
if (!isValidAction(requestedAction)) return false; if (!isValidAction(requestedAction)) return false;
return isActionActive(requestedAction); return isActionActive(actionType);
} }


public boolean isActionActive(String requestedAction) { public boolean isActionActive(ActionType actionType) {
int timestamp = clientSession.getTimestamp(); int timestamp = clientSession.getTimestamp();


int lifespan; int lifespan;
if (requestedAction.equals(ClientSessionModel.Action.CODE_TO_TOKEN.name())) { switch (actionType) {
lifespan = realm.getAccessCodeLifespan(); case CLIENT:

lifespan = realm.getAccessCodeLifespan();
} else if (requestedAction.equals(ClientSessionModel.Action.AUTHENTICATE.name())) { break;
lifespan = realm.getAccessCodeLifespanLogin() > 0 ? realm.getAccessCodeLifespanLogin() : realm.getAccessCodeLifespanUserAction(); case LOGIN:
} else { lifespan = realm.getAccessCodeLifespanLogin() > 0 ? realm.getAccessCodeLifespanLogin() : realm.getAccessCodeLifespanUserAction();
lifespan = realm.getAccessCodeLifespanUserAction(); break;
case USER:
lifespan = realm.getAccessCodeLifespanUserAction();
break;
default:
throw new IllegalArgumentException();
} }

return timestamp + lifespan > Time.currentTime(); return timestamp + lifespan > Time.currentTime();
} }


Expand Down
Expand Up @@ -446,7 +446,7 @@ private Response finishBrokerAuthentication(BrokeredIdentityContext context, Use
@Override @Override
public Response cancelled(String code) { public Response cancelled(String code) {
ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel); ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
if (clientCode.getClientSession() == null || !clientCode.isValid(AUTHENTICATE.name())) { if (clientCode.getClientSession() == null || !clientCode.isValid(AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
return redirectToErrorPage(Messages.INVALID_CODE); return redirectToErrorPage(Messages.INVALID_CODE);
} }


Expand All @@ -456,7 +456,7 @@ public Response cancelled(String code) {
@Override @Override
public Response error(String code, String message) { public Response error(String code, String message) {
ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel); ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
if (clientCode.getClientSession() == null || !clientCode.isValid(AUTHENTICATE.name())) { if (clientCode.getClientSession() == null || !clientCode.isValid(AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
return redirectToErrorPage(Messages.INVALID_CODE); return redirectToErrorPage(Messages.INVALID_CODE);
} }
return browserAuthentication(clientCode.getClientSession(), message); return browserAuthentication(clientCode.getClientSession(), message);
Expand Down Expand Up @@ -522,7 +522,7 @@ private void updateFederatedIdentity(BrokeredIdentityContext context, UserModel
private ClientSessionCode parseClientSessionCode(String code) { private ClientSessionCode parseClientSessionCode(String code) {
ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel); ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);


if (clientCode != null && clientCode.isValid(AUTHENTICATE.name())) { if (clientCode != null && clientCode.isValid(AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
ClientSessionModel clientSession = clientCode.getClientSession(); ClientSessionModel clientSession = clientCode.getClientSession();


if (clientSession != null) { if (clientSession != null) {
Expand Down
Expand Up @@ -165,25 +165,25 @@ private class Checks {
ClientSessionCode clientCode; ClientSessionCode clientCode;
Response response; Response response;


boolean verifyCode(String code, String requiredAction) { boolean verifyCode(String code, String requiredAction, ClientSessionCode.ActionType actionType) {
if (!verifyCode(code)) { if (!verifyCode(code)) {
return false; return false;
} }
if (!verifyAction(requiredAction)) { if (!verifyAction(requiredAction, actionType)) {
return false; return false;
} else { } else {
return true; return true;
} }
} }


public boolean verifyAction(String requiredAction) { public boolean verifyAction(String requiredAction, ClientSessionCode.ActionType actionType) {
if (!clientCode.isValidAction(requiredAction)) { if (!clientCode.isValidAction(requiredAction)) {
event.client(clientCode.getClientSession().getClient()); event.client(clientCode.getClientSession().getClient());
event.error(Errors.INVALID_CODE); event.error(Errors.INVALID_CODE);
response = ErrorPage.error(session, Messages.INVALID_CODE); response = ErrorPage.error(session, Messages.INVALID_CODE);
return false; return false;
} }
if (!clientCode.isActionActive(requiredAction)) { if (!clientCode.isActionActive(actionType)) {
event.client(clientCode.getClientSession().getClient()); event.client(clientCode.getClientSession().getClient());
event.clone().error(Errors.EXPIRED_CODE); event.clone().error(Errors.EXPIRED_CODE);
if (clientCode.getClientSession().getAction().equals(ClientSessionModel.Action.AUTHENTICATE.name())) { if (clientCode.getClientSession().getAction().equals(ClientSessionModel.Action.AUTHENTICATE.name())) {
Expand Down Expand Up @@ -264,7 +264,7 @@ public Response authenticate(@QueryParam("code") String code,
@QueryParam("execution") String execution) { @QueryParam("execution") String execution) {
event.event(EventType.LOGIN); event.event(EventType.LOGIN);
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name())) { if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
return checks.response; return checks.response;
} }
event.detail(Details.CODE_ID, code); event.detail(Details.CODE_ID, code);
Expand Down Expand Up @@ -315,7 +315,7 @@ public Response authenticateForm(@QueryParam("code") String code,
@QueryParam("execution") String execution) { @QueryParam("execution") String execution) {
event.event(EventType.LOGIN); event.event(EventType.LOGIN);
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name())) { if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
return checks.response; return checks.response;
} }
final ClientSessionCode clientCode = checks.clientCode; final ClientSessionCode clientCode = checks.clientCode;
Expand Down Expand Up @@ -374,7 +374,7 @@ public Response resetCredentialsGET(@QueryParam("code") String code,
protected Response resetCredentials(String code, String execution) { protected Response resetCredentials(String code, String execution) {
event.event(EventType.RESET_PASSWORD); event.event(EventType.RESET_PASSWORD);
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name())) { if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.USER)) {
return checks.response; return checks.response;
} }
final ClientSessionCode clientCode = checks.clientCode; final ClientSessionCode clientCode = checks.clientCode;
Expand Down Expand Up @@ -438,7 +438,7 @@ public Response registerPage(@QueryParam("code") String code,
} }


Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name())) { if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
return checks.response; return checks.response;
} }
event.detail(Details.CODE_ID, code); event.detail(Details.CODE_ID, code);
Expand Down Expand Up @@ -468,7 +468,7 @@ public Response processRegister(@QueryParam("code") String code,
return ErrorPage.error(session, Messages.REGISTRATION_NOT_ALLOWED); return ErrorPage.error(session, Messages.REGISTRATION_NOT_ALLOWED);
} }
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name())) { if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
return checks.response; return checks.response;
} }


Expand Down Expand Up @@ -496,7 +496,7 @@ protected Response firstBrokerLogin(String code, String execution) {
event.event(EventType.IDENTITY_PROVIDER_FIRST_LOGIN); event.event(EventType.IDENTITY_PROVIDER_FIRST_LOGIN);


Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name())) { if (!checks.verifyCode(code, ClientSessionModel.Action.AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
return checks.response; return checks.response;
} }
event.detail(Details.CODE_ID, code); event.detail(Details.CODE_ID, code);
Expand Down Expand Up @@ -556,7 +556,7 @@ public Response processConsent(final MultivaluedMap<String, String> formData) {
String code = formData.getFirst("code"); String code = formData.getFirst("code");


ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm); ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
if (accessCode == null || !accessCode.isValid(ClientSessionModel.Action.OAUTH_GRANT.name())) { if (accessCode == null || !accessCode.isValid(ClientSessionModel.Action.OAUTH_GRANT.name(), ClientSessionCode.ActionType.LOGIN)) {
event.error(Errors.INVALID_CODE); event.error(Errors.INVALID_CODE);
return ErrorPage.error(session, Messages.INVALID_ACCESS_CODE); return ErrorPage.error(session, Messages.INVALID_ACCESS_CODE);
} }
Expand Down Expand Up @@ -622,7 +622,7 @@ public Response emailVerification(@QueryParam("code") String code, @QueryParam("
event.event(EventType.VERIFY_EMAIL); event.event(EventType.VERIFY_EMAIL);
if (key != null) { if (key != null) {
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.verifyCode(code, ClientSessionModel.Action.REQUIRED_ACTIONS.name())) { if (!checks.verifyCode(code, ClientSessionModel.Action.REQUIRED_ACTIONS.name(), ClientSessionCode.ActionType.USER)) {
return checks.response; return checks.response;
} }
ClientSessionCode accessCode = checks.clientCode; ClientSessionCode accessCode = checks.clientCode;
Expand Down Expand Up @@ -665,7 +665,7 @@ public Response emailVerification(@QueryParam("code") String code, @QueryParam("
return AuthenticationProcessor.createRequiredActionRedirect(realm, clientSession, uriInfo); return AuthenticationProcessor.createRequiredActionRedirect(realm, clientSession, uriInfo);
} else { } else {
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.verifyCode(code, ClientSessionModel.Action.REQUIRED_ACTIONS.name())) { if (!checks.verifyCode(code, ClientSessionModel.Action.REQUIRED_ACTIONS.name(), ClientSessionCode.ActionType.USER)) {
return checks.response; return checks.response;
} }
ClientSessionCode accessCode = checks.clientCode; ClientSessionCode accessCode = checks.clientCode;
Expand Down Expand Up @@ -697,7 +697,7 @@ public Response executeActions(@QueryParam("key") String key) {
event.event(EventType.EXECUTE_ACTIONS); event.event(EventType.EXECUTE_ACTIONS);
if (key != null) { if (key != null) {
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.verifyCode(key, ClientSessionModel.Action.EXECUTE_ACTIONS.name())) { if (!checks.verifyCode(key, ClientSessionModel.Action.EXECUTE_ACTIONS.name(), ClientSessionCode.ActionType.USER)) {
return checks.response; return checks.response;
} }
ClientSessionModel clientSession = checks.clientCode.getClientSession(); ClientSessionModel clientSession = checks.clientCode.getClientSession();
Expand Down Expand Up @@ -767,7 +767,7 @@ public Response processRequireAction(final String code, String action) {
event.event(EventType.CUSTOM_REQUIRED_ACTION); event.event(EventType.CUSTOM_REQUIRED_ACTION);
event.detail(Details.CUSTOM_REQUIRED_ACTION, action); event.detail(Details.CUSTOM_REQUIRED_ACTION, action);
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.verifyCode(code, ClientSessionModel.Action.REQUIRED_ACTIONS.name())) { if (!checks.verifyCode(code, ClientSessionModel.Action.REQUIRED_ACTIONS.name(), ClientSessionCode.ActionType.USER)) {
return checks.response; return checks.response;
} }
final ClientSessionCode clientCode = checks.clientCode; final ClientSessionCode clientCode = checks.clientCode;
Expand Down
Expand Up @@ -166,7 +166,7 @@ public Response authResponse(@QueryParam("state") String state,
private ClientSessionCode parseClientSessionCode(String code) { private ClientSessionCode parseClientSessionCode(String code) {
ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realm); ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realm);


if (clientCode != null && clientCode.isValid(AUTHENTICATE.name())) { if (clientCode != null && clientCode.isValid(AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) {
ClientSessionModel clientSession = clientCode.getClientSession(); ClientSessionModel clientSession = clientCode.getClientSession();


if (clientSession != null) { if (clientSession != null) {
Expand Down
Expand Up @@ -21,10 +21,7 @@
*/ */
package org.keycloak.testsuite.forms; package org.keycloak.testsuite.forms;


import org.junit.Before; import org.junit.*;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors; import org.keycloak.events.Errors;
import org.keycloak.events.Event; import org.keycloak.events.Event;
Expand All @@ -39,13 +36,8 @@
import org.keycloak.testsuite.Constants; import org.keycloak.testsuite.Constants;
import org.keycloak.testsuite.MailUtil; import org.keycloak.testsuite.MailUtil;
import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.*;
import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.ErrorPage;
import org.keycloak.testsuite.pages.InfoPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.LoginPasswordResetPage;
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
import org.keycloak.testsuite.rule.GreenMailRule; import org.keycloak.testsuite.rule.GreenMailRule;
import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebResource;
Expand All @@ -59,6 +51,7 @@


import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger;


import static org.junit.Assert.*; import static org.junit.Assert.*;


Expand Down Expand Up @@ -114,6 +107,9 @@ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmMod
@WebResource @WebResource
protected InfoPage infoPage; protected InfoPage infoPage;


@WebResource
protected VerifyEmailPage verifyEmailPage;

@WebResource @WebResource
protected LoginPasswordResetPage resetPasswordPage; protected LoginPasswordResetPage resetPasswordPage;


Expand Down Expand Up @@ -422,6 +418,59 @@ public void resetPasswordExpiredCode() throws IOException, MessagingException, I
} }
} }


@Test
public void resetPasswordExpiredCodeShort() throws IOException, MessagingException, InterruptedException {
final AtomicInteger originalValue = new AtomicInteger();
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
originalValue.set(appRealm.getAccessCodeLifespan());
appRealm.setAccessCodeLifespanUserAction(60);
}
});

try {
loginPage.open();
loginPage.resetPassword();

resetPasswordPage.assertCurrent();

resetPasswordPage.changePassword("login-test");

loginPage.assertCurrent();
assertEquals("You should receive an email shortly with further instructions.", loginPage.getSuccessMessage());

events.expectRequiredAction(EventType.SEND_RESET_PASSWORD)
.session((String)null)
.user(userId).detail(Details.USERNAME, "login-test").detail(Details.EMAIL, "login@test.com").assertEvent();

assertEquals(1, greenMail.getReceivedMessages().length);

MimeMessage message = greenMail.getReceivedMessages()[0];

String changePasswordUrl = getPasswordResetEmailLink(message);

Time.setOffset(70);

driver.navigate().to(changePasswordUrl.trim());

loginPage.assertCurrent();

assertEquals("You took too long to login. Login process starting from beginning.", loginPage.getError());

events.expectRequiredAction(EventType.RESET_PASSWORD).error("expired_code").client("test-app").user((String) null).session((String) null).clearDetails().assertEvent();
} finally {
Time.setOffset(0);

keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.setAccessCodeLifespanUserAction(originalValue.get());
}
});
}
}

@Test @Test
public void resetPasswordDisabledUser() throws IOException, MessagingException, InterruptedException { public void resetPasswordDisabledUser() throws IOException, MessagingException, InterruptedException {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() { keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
Expand Down

0 comments on commit ee363a4

Please sign in to comment.