unfinished working auth spi

This commit is contained in:
Bill Burke 2015-05-27 14:28:47 -04:00
parent 143d176dcd
commit a2718a889d
10 changed files with 172 additions and 5 deletions

View file

@ -45,6 +45,9 @@ public class UrlBean {
}
public String getLoginAction() {
if (this.actionuri != null) {
return this.actionuri.toString();
}
return Urls.realmLoginAction(baseURI, realm).toString();
}

View file

@ -279,6 +279,10 @@ public class AuthenticationProcessor {
super(message, cause, enableSuppression, writableStackTrace);
this.error = error;
}
public Error getError() {
return error;
}
}
public void logUserFailure() {
@ -389,6 +393,9 @@ public class AuthenticationProcessor {
if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) throw new AuthException(Error.INVALID_CREDENTIALS);
clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.ATTEMPTED);
continue;
} else {
logger.error("Unknown result status");
throw new AuthException(Error.INTERNAL_ERROR);
}
}
return null;
@ -398,7 +405,7 @@ public class AuthenticationProcessor {
public void validateUser(UserModel authenticatedUser) {
if (authenticatedUser != null) {
if (!clientSession.getAuthenticatedUser().isEnabled()) throw new AuthException(Error.USER_DISABLED);
if (!authenticatedUser.isEnabled()) throw new AuthException(Error.USER_DISABLED);
}
if (realm.isBruteForceProtected()) {
if (protector.isTemporarilyDisabled(session, realm, authenticatedUser.getUsername())) {

View file

@ -52,6 +52,7 @@ public class LoginFormOTPAuthenticator extends LoginFormUsernameAuthenticator {
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return;
}
context.success();
}
@Override

View file

@ -53,6 +53,7 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return;
}
context.success();
}
@Override
@ -62,7 +63,7 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
@Override
public boolean configuredFor(UserModel user) {
return false;
return user.configuredForCredentialType(UserCredentialModel.PASSWORD);
}
@Override

View file

@ -1,6 +1,7 @@
package org.keycloak.authentication.authenticators;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.keycloak.OAuth2Constants;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext;
@ -51,7 +52,9 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
protected boolean isActionUrl(AuthenticatorContext context) {
URI expected = LoginActionsService.authenticationFormProcessor(context.getUriInfo()).build(context.getRealm().getName());
return expected.getPath().equals(context.getUriInfo().getPath());
String current = context.getUriInfo().getAbsolutePath().getPath();
String expectedPath = expected.getPath();
return expectedPath.equals(current);
}
@ -71,7 +74,11 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
protected LoginFormsProvider loginForm(AuthenticatorContext context) {
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession());
code.setAction(ClientSessionModel.Action.AUTHENTICATE);
URI action = LoginActionsService.authenticationFormProcessor(context.getUriInfo())
.queryParam(OAuth2Constants.CODE, code.getCode())
.build(context.getRealm().getName());
return context.getSession().getProvider(LoginFormsProvider.class)
.setActionUri(action)
.setClientSessionCode(code.getCode());
}
@ -98,6 +105,7 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
if (invalidUser(context, user)) return;
context.setUser(user);
context.success();
}
public boolean invalidUser(AuthenticatorContext context, UserModel user) {

View file

@ -58,6 +58,7 @@ public class OTPFormAuthenticator implements Authenticator {
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return;
}
context.success();
}

View file

@ -5,12 +5,15 @@ import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
import org.keycloak.OAuth2Constants;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.authenticators.AuthenticationFlow;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.IdentityProviderModel;
@ -244,6 +247,32 @@ public class AuthorizationEndpoint {
return buildRedirectToIdentityProvider(idpHint, accessCode);
}
return oldBrowserAuthentication(accessCode);
}
protected Response newBrowserAuthentication(String accessCode) {
String flowId = null;
for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
if (flow.getAlias().equals("browser")) {
flowId = flow.getId();
break;
}
}
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)
.setFlowId(flowId)
.setConnection(clientConnection)
.setEventBuilder(event)
.setProtector(authManager.getProtector())
.setRealm(realm)
.setSession(session)
.setUriInfo(uriInfo)
.setRequest(request);
return processor.authenticate();
}
protected Response oldBrowserAuthentication(String accessCode) {
Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
if (response != null) return response;

View file

@ -78,6 +78,10 @@ public class AuthenticationManager {
this.protector = protector;
}
public BruteForceProtector getProtector() {
return protector;
}
public static boolean isSessionValid(RealmModel realm, UserSessionModel userSession) {
if (userSession == null) {
logger.debug("No user session");

View file

@ -24,6 +24,7 @@ package org.keycloak.services.resources;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.events.Details;
@ -32,6 +33,7 @@ import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.RoleModel;
@ -118,7 +120,7 @@ public class LoginActionsService {
}
public static UriBuilder authenticationFormProcessor(UriInfo uriInfo) {
return loginActionsBaseUrl(uriInfo).path("auth-form");
return loginActionsBaseUrl(uriInfo).path(LoginActionsService.class, "authForm");
}
public static UriBuilder loginActionsBaseUrl(UriBuilder baseUriBuilder) {
@ -274,6 +276,116 @@ public class LoginActionsService {
* URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
*
* @param code
* @return
*/
@Path("auth-form")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response authForm(@QueryParam("code") String code) {
event.event(EventType.LOGIN);
if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED);
return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
}
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
}
ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
if (clientCode == null) {
event.error(Errors.INVALID_CODE);
return ErrorPage.error(session, Messages.INVALID_CODE);
}
ClientSessionModel clientSession = clientCode.getClientSession();
event.detail(Details.CODE_ID, clientSession.getId());
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientSession.getUserSession() != null) {
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
return session.getProvider(LoginFormsProvider.class)
.setError(Messages.EXPIRED_CODE)
.setClientSessionCode(clientCode.getCode())
.createLogin();
}
ClientModel client = clientSession.getClient();
if (client == null) {
event.error(Errors.CLIENT_NOT_FOUND);
return ErrorPage.error(session, Messages.UNKNOWN_LOGIN_REQUESTER);
}
if (!client.isEnabled()) {
event.error(Errors.CLIENT_NOT_FOUND);
return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED);
}
String flowId = null;
for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
if (flow.getAlias().equals("browser")) {
flowId = flow.getId();
break;
}
}
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)
.setFlowId(flowId)
.setConnection(clientConnection)
.setEventBuilder(event)
.setProtector(authManager.getProtector())
.setRealm(realm)
.setSession(session)
.setUriInfo(uriInfo)
.setRequest(request);
try {
return processor.authenticate();
} catch (AuthenticationProcessor.AuthException e) {
return handleError(e, code);
} catch (Exception e) {
logger.error("failed authentication", e);
return ErrorPage.error(session, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE);
}
}
protected Response handleError(AuthenticationProcessor.AuthException e, String code) {
logger.error("failed authentication: " + e.getError().toString(), e);
if (e.getError() == AuthenticationProcessor.Error.INVALID_USER) {
event.error(Errors.USER_NOT_FOUND);
return session.getProvider(LoginFormsProvider.class)
.setError(Messages.INVALID_USER)
.setClientSessionCode(code)
.createLogin();
} else if (e.getError() == AuthenticationProcessor.Error.USER_DISABLED) {
event.error(Errors.USER_DISABLED);
return session.getProvider(LoginFormsProvider.class)
.setError(Messages.ACCOUNT_DISABLED)
.setClientSessionCode(code)
.createLogin();
} else if (e.getError() == AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED) {
event.error(Errors.USER_TEMPORARILY_DISABLED);
return session.getProvider(LoginFormsProvider.class)
.setError(Messages.ACCOUNT_TEMPORARILY_DISABLED)
.setClientSessionCode(code)
.createLogin();
} else {
event.error(Errors.INVALID_USER_CREDENTIALS);
return session.getProvider(LoginFormsProvider.class)
.setError(Messages.INVALID_USER)
.setClientSessionCode(code)
.createLogin();
}
}
/**
* URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
*
* @param code
* @param formData
* @return
*/

View file

@ -3,3 +3,4 @@ org.keycloak.protocol.ProtocolMapperSpi
org.keycloak.exportimport.ClientImportSpi
org.keycloak.wellknown.WellKnownSpi
org.keycloak.messages.MessagesSpi
org.keycloak.authentication.AuthenticatorSpi