access code checks

This commit is contained in:
Bill Burke 2015-06-16 07:59:53 -04:00
parent 9638c0dd83
commit cd84e78b27
13 changed files with 89 additions and 42 deletions

View file

@ -11,7 +11,7 @@ public interface MigrationModel {
/** /**
* Must have the form of major.minor.micro as the version is parsed and numbers are compared * Must have the form of major.minor.micro as the version is parsed and numbers are compared
*/ */
public static final String LATEST_VERSION = "1.3.0.Beta1"; public static final String LATEST_VERSION = "1.4.0";
String getStoredVersion(); String getStoredVersion();
void setStoredVersion(String version); void setStoredVersion(String version);

View file

@ -2,6 +2,7 @@ package org.keycloak.migration;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.migration.migrators.MigrateTo1_3_0; import org.keycloak.migration.migrators.MigrateTo1_3_0;
import org.keycloak.migration.migrators.MigrateTo1_4_0;
import org.keycloak.migration.migrators.MigrationTo1_2_0_CR1; import org.keycloak.migration.migrators.MigrationTo1_2_0_CR1;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -33,6 +34,12 @@ public class MigrationModelManager {
} }
new MigrateTo1_3_0().migrate(session); new MigrateTo1_3_0().migrate(session);
} }
if (stored == null || stored.lessThan(MigrateTo1_4_0.VERSION)) {
if (stored != null) {
logger.debug("Migrating older model to 1.4.0 updates");
}
new MigrateTo1_4_0().migrate(session);
}
model.setStoredVersion(MigrationModel.LATEST_VERSION); model.setStoredVersion(MigrationModel.LATEST_VERSION);
} }

View file

@ -0,0 +1,28 @@
package org.keycloak.migration.migrators;
import org.keycloak.migration.ModelVersion;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class MigrateTo1_4_0 {
public static final ModelVersion VERSION = new ModelVersion("1.4.0");
public void migrate(KeycloakSession session) {
List<RealmModel> realms = session.realms().getRealms();
for (RealmModel realm : realms) {
if (realm.getAuthenticationFlows().size() == 0) {
DefaultAuthenticationFlows.addFlows(realm);
}
}
}
}

View file

@ -19,6 +19,7 @@ import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.services.ErrorPage; import org.keycloak.services.ErrorPage;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.BruteForceProtector; import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -317,6 +318,13 @@ public class AuthenticationProcessor {
public String getForwardedErrorMessage() { public String getForwardedErrorMessage() {
return AuthenticationProcessor.this.forwardedErrorMessage; return AuthenticationProcessor.this.forwardedErrorMessage;
} }
@Override
public String generateAccessCode() {
ClientSessionCode accessCode = new ClientSessionCode(getRealm(), getClientSession());
accessCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
return accessCode.getCode();
}
} }
public static class AuthException extends RuntimeException { public static class AuthException extends RuntimeException {
@ -518,10 +526,7 @@ public class AuthenticationProcessor {
if (model.isUserSetupAllowed()) { if (model.isUserSetupAllowed()) {
logger.debugv("authenticator SETUP_REQUIRED: {0}", authenticatorModel.getProviderId()); logger.debugv("authenticator SETUP_REQUIRED: {0}", authenticatorModel.getProviderId());
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED); clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED);
String requiredAction = authenticator.getRequiredAction(); authenticator.setRequiredActions(session, realm, clientSession.getAuthenticatedUser());
if (!authUser.getRequiredActions().contains(requiredAction)) {
authUser.addRequiredAction(requiredAction);
}
continue; continue;
} else { } else {
throw new AuthException(Error.CREDENTIAL_SETUP_REQUIRED); throw new AuthException(Error.CREDENTIAL_SETUP_REQUIRED);

View file

@ -13,7 +13,12 @@ public interface Authenticator extends Provider {
boolean requiresUser(); boolean requiresUser();
void authenticate(AuthenticatorContext context); void authenticate(AuthenticatorContext context);
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user); boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
String getRequiredAction();
/**
* Set actions to configure authenticator
*
*/
void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
} }

View file

@ -73,4 +73,11 @@ public interface AuthenticatorContext {
* whatever form is challenging. * whatever form is challenging.
*/ */
String getForwardedErrorMessage(); String getForwardedErrorMessage();
/**
* Generates access code and updates clientsession timestamp
*
* @return
*/
String generateAccessCode();
} }

View file

@ -5,9 +5,7 @@ import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.AuthenticatorContext; import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.events.Errors; import org.keycloak.events.Errors;
import org.keycloak.login.LoginFormsProvider; import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.LoginActionsService; import org.keycloak.services.resources.LoginActionsService;
@ -29,22 +27,21 @@ public class AbstractFormAuthenticator {
} }
protected LoginFormsProvider loginForm(AuthenticatorContext context) { protected LoginFormsProvider loginForm(AuthenticatorContext context) {
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession()); String accessCode = context.generateAccessCode();
code.setAction(ClientSessionModel.Action.AUTHENTICATE.name()); URI action = getActionUrl(context, accessCode, LOGIN_FORM_ACTION);
URI action = getActionUrl(context, code, LOGIN_FORM_ACTION);
LoginFormsProvider provider = context.getSession().getProvider(LoginFormsProvider.class) LoginFormsProvider provider = context.getSession().getProvider(LoginFormsProvider.class)
.setUser(context.getUser()) .setUser(context.getUser())
.setActionUri(action) .setActionUri(action)
.setClientSessionCode(code.getCode()); .setClientSessionCode(accessCode);
if (context.getForwardedErrorMessage() != null) { if (context.getForwardedErrorMessage() != null) {
provider.setError(context.getForwardedErrorMessage()); provider.setError(context.getForwardedErrorMessage());
} }
return provider; return provider;
} }
public static URI getActionUrl(AuthenticatorContext context, ClientSessionCode code, String action) { public static URI getActionUrl(AuthenticatorContext context, String code, String action) {
return LoginActionsService.authenticationFormProcessor(context.getUriInfo()) return LoginActionsService.authenticationFormProcessor(context.getUriInfo())
.queryParam(OAuth2Constants.CODE, code.getCode()) .queryParam(OAuth2Constants.CODE, code)
.queryParam(ACTION, action) .queryParam(ACTION, action)
.build(context.getRealm().getName()); .build(context.getRealm().getName());
} }
@ -52,25 +49,21 @@ public class AbstractFormAuthenticator {
protected Response invalidUser(AuthenticatorContext context) { protected Response invalidUser(AuthenticatorContext context) {
return loginForm(context) return loginForm(context)
.setError(Messages.INVALID_USER) .setError(Messages.INVALID_USER)
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
.createLogin(); .createLogin();
} }
protected Response disabledUser(AuthenticatorContext context) { protected Response disabledUser(AuthenticatorContext context) {
return loginForm(context) return loginForm(context)
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
.setError(Messages.ACCOUNT_DISABLED).createLogin(); .setError(Messages.ACCOUNT_DISABLED).createLogin();
} }
protected Response temporarilyDisabledUser(AuthenticatorContext context) { protected Response temporarilyDisabledUser(AuthenticatorContext context) {
return loginForm(context) return loginForm(context)
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
.setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).createLogin(); .setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).createLogin();
} }
protected Response invalidCredentials(AuthenticatorContext context) { protected Response invalidCredentials(AuthenticatorContext context) {
return loginForm(context) return loginForm(context)
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
.setError(Messages.INVALID_USER).createLogin(); .setError(Messages.INVALID_USER).createLogin();
} }

View file

@ -38,8 +38,7 @@ public class CookieAuthenticator implements Authenticator {
} }
@Override @Override
public String getRequiredAction() { public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
return null;
} }
@Override @Override

View file

@ -68,8 +68,11 @@ public class LoginFormOTPAuthenticator extends LoginFormUsernameAuthenticator {
} }
@Override @Override
public String getRequiredAction() { public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
return UserModel.RequiredAction.CONFIGURE_TOTP.name(); if (!user.getRequiredActions().contains(UserModel.RequiredAction.CONFIGURE_TOTP.name())) {
user.addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP.name());
}
} }
@Override @Override

View file

@ -70,8 +70,11 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
} }
@Override @Override
public String getRequiredAction() { public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
return UserModel.RequiredAction.UPDATE_PASSWORD.name(); if (!user.getRequiredActions().contains(UserModel.RequiredAction.UPDATE_PASSWORD.name())) {
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.name());
}
} }
@Override @Override

View file

@ -111,8 +111,7 @@ public class LoginFormUsernameAuthenticator extends AbstractFormAuthenticator im
} }
@Override @Override
public String getRequiredAction() { public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
return null;
} }
@Override @Override

View file

@ -11,7 +11,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
@ -69,11 +68,11 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
} }
protected Response challenge(AuthenticatorContext context, String error) { protected Response challenge(AuthenticatorContext context, String error) {
ClientSessionCode clientSessionCode = new ClientSessionCode(context.getRealm(), context.getClientSession()); String accessCode = context.generateAccessCode();
URI action = AbstractFormAuthenticator.getActionUrl(context, clientSessionCode, TOTP_FORM_ACTION); URI action = AbstractFormAuthenticator.getActionUrl(context, accessCode, TOTP_FORM_ACTION);
LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class) LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
.setActionUri(action) .setActionUri(action)
.setClientSessionCode(clientSessionCode.getCode()); .setClientSessionCode(accessCode);
if (error != null) forms.setError(error); if (error != null) forms.setError(error);
return forms.createLoginTotp(); return forms.createLoginTotp();
@ -85,8 +84,11 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
} }
@Override @Override
public String getRequiredAction() { public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
return UserModel.RequiredAction.CONFIGURE_TOTP.name(); if (!user.getRequiredActions().contains(UserModel.RequiredAction.CONFIGURE_TOTP.name())) {
user.addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP.name());
}
} }
@Override @Override

View file

@ -15,7 +15,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
@ -131,9 +130,8 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
* @return * @return
*/ */
protected Response optionalChallengeRedirect(AuthenticatorContext context, String negotiateHeader) { protected Response optionalChallengeRedirect(AuthenticatorContext context, String negotiateHeader) {
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession()); String accessCode = context.generateAccessCode();
code.setAction(ClientSessionModel.Action.AUTHENTICATE.name()); URI action = getActionUrl(context, accessCode, KERBEROS_DISABLED);
URI action = getActionUrl(context, code, KERBEROS_DISABLED);
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
@ -162,11 +160,10 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
} }
protected Response formChallenge(AuthenticatorContext context, String negotiateHeader) { protected Response formChallenge(AuthenticatorContext context, String negotiateHeader) {
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession()); String accessCode = context.generateAccessCode();
code.setAction(ClientSessionModel.Action.AUTHENTICATE.name()); URI action = getActionUrl(context, accessCode, KERBEROS_DISABLED);
URI action = getActionUrl(context, code, KERBEROS_DISABLED);
return context.getSession().getProvider(LoginFormsProvider.class) return context.getSession().getProvider(LoginFormsProvider.class)
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode()) .setClientSessionCode(accessCode)
.setActionUri(action) .setActionUri(action)
.setStatus(Response.Status.UNAUTHORIZED) .setStatus(Response.Status.UNAUTHORIZED)
.setResponseHeader(HttpHeaders.WWW_AUTHENTICATE, negotiateHeader) .setResponseHeader(HttpHeaders.WWW_AUTHENTICATE, negotiateHeader)
@ -181,8 +178,7 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
} }
@Override @Override
public String getRequiredAction() { public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
return null;
} }
@Override @Override