access code checks
This commit is contained in:
parent
9638c0dd83
commit
cd84e78b27
13 changed files with 89 additions and 42 deletions
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
28
model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java
Executable file
28
model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java
Executable 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue