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
|
||||
*/
|
||||
public static final String LATEST_VERSION = "1.3.0.Beta1";
|
||||
public static final String LATEST_VERSION = "1.4.0";
|
||||
|
||||
String getStoredVersion();
|
||||
void setStoredVersion(String version);
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.migration;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
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.models.KeycloakSession;
|
||||
|
||||
|
@ -33,6 +34,12 @@ public class MigrationModelManager {
|
|||
}
|
||||
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);
|
||||
}
|
||||
|
|
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.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.BruteForceProtector;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
@ -317,6 +318,13 @@ public class AuthenticationProcessor {
|
|||
public String getForwardedErrorMessage() {
|
||||
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 {
|
||||
|
@ -518,10 +526,7 @@ public class AuthenticationProcessor {
|
|||
if (model.isUserSetupAllowed()) {
|
||||
logger.debugv("authenticator SETUP_REQUIRED: {0}", authenticatorModel.getProviderId());
|
||||
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED);
|
||||
String requiredAction = authenticator.getRequiredAction();
|
||||
if (!authUser.getRequiredActions().contains(requiredAction)) {
|
||||
authUser.addRequiredAction(requiredAction);
|
||||
}
|
||||
authenticator.setRequiredActions(session, realm, clientSession.getAuthenticatedUser());
|
||||
continue;
|
||||
} else {
|
||||
throw new AuthException(Error.CREDENTIAL_SETUP_REQUIRED);
|
||||
|
|
|
@ -13,7 +13,12 @@ public interface Authenticator extends Provider {
|
|||
boolean requiresUser();
|
||||
void authenticate(AuthenticatorContext context);
|
||||
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.
|
||||
*/
|
||||
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.events.Errors;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.LoginActionsService;
|
||||
|
||||
|
@ -29,22 +27,21 @@ public class AbstractFormAuthenticator {
|
|||
}
|
||||
|
||||
protected LoginFormsProvider loginForm(AuthenticatorContext context) {
|
||||
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession());
|
||||
code.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
URI action = getActionUrl(context, code, LOGIN_FORM_ACTION);
|
||||
String accessCode = context.generateAccessCode();
|
||||
URI action = getActionUrl(context, accessCode, LOGIN_FORM_ACTION);
|
||||
LoginFormsProvider provider = context.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setUser(context.getUser())
|
||||
.setActionUri(action)
|
||||
.setClientSessionCode(code.getCode());
|
||||
.setClientSessionCode(accessCode);
|
||||
if (context.getForwardedErrorMessage() != null) {
|
||||
provider.setError(context.getForwardedErrorMessage());
|
||||
}
|
||||
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())
|
||||
.queryParam(OAuth2Constants.CODE, code.getCode())
|
||||
.queryParam(OAuth2Constants.CODE, code)
|
||||
.queryParam(ACTION, action)
|
||||
.build(context.getRealm().getName());
|
||||
}
|
||||
|
@ -52,25 +49,21 @@ public class AbstractFormAuthenticator {
|
|||
protected Response invalidUser(AuthenticatorContext context) {
|
||||
return loginForm(context)
|
||||
.setError(Messages.INVALID_USER)
|
||||
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
|
||||
.createLogin();
|
||||
}
|
||||
|
||||
protected Response disabledUser(AuthenticatorContext context) {
|
||||
return loginForm(context)
|
||||
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
|
||||
.setError(Messages.ACCOUNT_DISABLED).createLogin();
|
||||
}
|
||||
|
||||
protected Response temporarilyDisabledUser(AuthenticatorContext context) {
|
||||
return loginForm(context)
|
||||
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
|
||||
.setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).createLogin();
|
||||
}
|
||||
|
||||
protected Response invalidCredentials(AuthenticatorContext context) {
|
||||
return loginForm(context)
|
||||
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
|
||||
.setError(Messages.INVALID_USER).createLogin();
|
||||
}
|
||||
|
||||
|
|
|
@ -38,8 +38,7 @@ public class CookieAuthenticator implements Authenticator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getRequiredAction() {
|
||||
return null;
|
||||
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -68,8 +68,11 @@ public class LoginFormOTPAuthenticator extends LoginFormUsernameAuthenticator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getRequiredAction() {
|
||||
return UserModel.RequiredAction.CONFIGURE_TOTP.name();
|
||||
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
if (!user.getRequiredActions().contains(UserModel.RequiredAction.CONFIGURE_TOTP.name())) {
|
||||
user.addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP.name());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -70,8 +70,11 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getRequiredAction() {
|
||||
return UserModel.RequiredAction.UPDATE_PASSWORD.name();
|
||||
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
if (!user.getRequiredActions().contains(UserModel.RequiredAction.UPDATE_PASSWORD.name())) {
|
||||
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.name());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -111,8 +111,7 @@ public class LoginFormUsernameAuthenticator extends AbstractFormAuthenticator im
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getRequiredAction() {
|
||||
return null;
|
||||
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -11,7 +11,6 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
|
@ -69,11 +68,11 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
|
|||
}
|
||||
|
||||
protected Response challenge(AuthenticatorContext context, String error) {
|
||||
ClientSessionCode clientSessionCode = new ClientSessionCode(context.getRealm(), context.getClientSession());
|
||||
URI action = AbstractFormAuthenticator.getActionUrl(context, clientSessionCode, TOTP_FORM_ACTION);
|
||||
String accessCode = context.generateAccessCode();
|
||||
URI action = AbstractFormAuthenticator.getActionUrl(context, accessCode, TOTP_FORM_ACTION);
|
||||
LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setActionUri(action)
|
||||
.setClientSessionCode(clientSessionCode.getCode());
|
||||
.setClientSessionCode(accessCode);
|
||||
if (error != null) forms.setError(error);
|
||||
|
||||
return forms.createLoginTotp();
|
||||
|
@ -85,8 +84,11 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getRequiredAction() {
|
||||
return UserModel.RequiredAction.CONFIGURE_TOTP.name();
|
||||
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
if (!user.getRequiredActions().contains(UserModel.RequiredAction.CONFIGURE_TOTP.name())) {
|
||||
user.addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP.name());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,7 +15,6 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
|
@ -131,9 +130,8 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
|
|||
* @return
|
||||
*/
|
||||
protected Response optionalChallengeRedirect(AuthenticatorContext context, String negotiateHeader) {
|
||||
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession());
|
||||
code.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
URI action = getActionUrl(context, code, KERBEROS_DISABLED);
|
||||
String accessCode = context.generateAccessCode();
|
||||
URI action = getActionUrl(context, accessCode, KERBEROS_DISABLED);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
|
@ -162,11 +160,10 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
|
|||
}
|
||||
|
||||
protected Response formChallenge(AuthenticatorContext context, String negotiateHeader) {
|
||||
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession());
|
||||
code.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
URI action = getActionUrl(context, code, KERBEROS_DISABLED);
|
||||
String accessCode = context.generateAccessCode();
|
||||
URI action = getActionUrl(context, accessCode, KERBEROS_DISABLED);
|
||||
return context.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
|
||||
.setClientSessionCode(accessCode)
|
||||
.setActionUri(action)
|
||||
.setStatus(Response.Status.UNAUTHORIZED)
|
||||
.setResponseHeader(HttpHeaders.WWW_AUTHENTICATE, negotiateHeader)
|
||||
|
@ -181,8 +178,7 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getRequiredAction() {
|
||||
return null;
|
||||
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in a new issue