final re-org work

This commit is contained in:
Bill Burke committed 2016-01-20 17:26:26 -05:00
1 parent d9487a8745
commit efd8317259
66 files changed
+53 -44

No files matched your search

@@ -0,0 +1,175 @@
package org.keycloak.authentication;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.common.ClientConnection;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.services.managers.BruteForceProtector;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface AbstractAuthenticationFlowContext {
/**
* Current event builder being used
*
* @return
*/
EventBuilder getEvent();
/**
* Create a refresh new EventBuilder to use within this context
*
* @return
*/
EventBuilder newEvent();
/**
* The current execution in the flow
*
* @return
*/
AuthenticationExecutionModel getExecution();
/**
* Current realm
*
* @return
*/
RealmModel getRealm();
/**
* Information about the IP address from the connecting HTTP client.
*
* @return
*/
ClientConnection getConnection();
/**
* UriInfo of the current request
*
* @return
*/
UriInfo getUriInfo();
/**
* Current session
*
* @return
*/
KeycloakSession getSession();
HttpRequest getHttpRequest();
BruteForceProtector getProtector();
/**
* Get any configuration associated with the current execution
*
* @return
*/
AuthenticatorConfigModel getAuthenticatorConfig();
/**
* This could be an error message forwarded from another authenticator that is restarting or continuing the flo. For example
* the brokering API sends this when the broker failed authentication
* and we want to continue authentication locally. forwardedErrorMessage can then be displayed by
* whatever form is challenging.
*/
FormMessage getForwardedErrorMessage();
/**
* This could be an success message forwarded from another authenticator that is restarting or continuing the flow. For example
* a reset password sends an email, then resets the flow with a success message. forwardedSuccessMessage can then be displayed by
* whatever form is challenging.
*/
FormMessage getForwardedSuccessMessage();
/**
* Generates access code and updates clientsession timestamp
* Access codes must be included in form action callbacks as a query parameter.
*
* @return
*/
String generateAccessCode();
AuthenticationExecutionModel.Requirement getCategoryRequirementFromCurrentFlow(String authenticatorCategory);
/**
* Mark the current execution as successful. The flow will then continue
*
*/
void success();
/**
* Aborts the current flow
*
* @param error
*/
void failure(AuthenticationFlowError error);
/**
* Aborts the current flow.
*
* @param error
* @param response Response that will be sent back to HTTP client
*/
void failure(AuthenticationFlowError error, Response response);
/**
* Sends a challenge response back to the HTTP client. If the current execution requirement is optional, this response will not be
* sent. If the current execution requirement is alternative, then this challenge will be sent if no other alternative
* execution was successful.
*
* @param challenge
*/
void challenge(Response challenge);
/**
* Sends the challenge back to the HTTP client irregardless of the current executionr equirement
*
* @param challenge
*/
void forceChallenge(Response challenge);
/**
* Same behavior as challenge(), but the error count in brute force attack detection will be incremented.
* For example, if a user enters in a bad password, the user is directed to try again, but Keycloak will keep track
* of how many failures have happened.
*
* @param error
* @param challenge
*/
void failureChallenge(AuthenticationFlowError error, Response challenge);
/**
* There was no failure or challenge. The authenticator was attempted, but not fulfilled. If the current execution
* requirement is alternative or optional, then this status is ignored by the flow.
*
*/
void attempted();
/**
* Get the current status of the current execution.
*
* @return may return null if not set yet.
*/
FlowStatus getStatus();
/**
* Get the error condition of a failed execution.
*
* @return may return null if there was no error
*/
AuthenticationFlowError getError();
}
@@ -0,0 +1,16 @@
package org.keycloak.authentication;
import javax.ws.rs.core.Response;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface AuthenticationFlow {
String BASIC_FLOW = "basic-flow";
String FORM_FLOW = "form-flow";
String CLIENT_FLOW = "client-flow";
Response processAction(String actionExecution);
Response processFlow();
}
@@ -0,0 +1,116 @@
package org.keycloak.authentication;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.FormMessage;
import java.net.URI;
/**
* This interface encapsulates information about an execution in an AuthenticationFlow. It is also used to set
* the status of the execution being performed.
*
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface AuthenticationFlowContext extends AbstractAuthenticationFlowContext {
/**
* Current user attached to this flow. It can return null if no user has been identified yet
*
* @return
*/
UserModel getUser();
/**
* Attach a specific user to this flow.
*
* @param user
*/
void setUser(UserModel user);
/**
* Clear the user from the flow.
*/
void clearUser();
void attachUserSession(UserSessionModel userSession);
/**
* ClientSessionModel attached to this flow
*
* @return
*/
ClientSessionModel getClientSession();
/**
* Create a Freemarker form builder that presets the user, action URI, and a generated access code
*
* @return
*/
LoginFormsProvider form();
/**
* Get the action URL for the required action.
*
* @param code client session access code
* @return
*/
URI getActionUrl(String code);
/**
* Get the action URL for the required action. This auto-generates the access code.
*
* @return
*/
URI getActionUrl();
/**
* End the flow and redirect browser based on protocol specific respones. This should only be executed
* in browser-based flows.
*
*/
void cancelLogin();
/**
* Reset the current flow to the beginning and restarts it.
*
*/
void resetFlow();
/**
* Fork the current flow. The client session will be cloned and set to point at the realm's browser login flow. The Response will be the result
* of this fork. The previous flow will still be set at the current execution. This is used by reset password when it sends an email.
* It sends an email linking to the current flow and redirects the browser to a new browser login flow.
*
*
*
* @return
*/
void fork();
/**
* Fork the current flow. The client session will be cloned and set to point at the realm's browser login flow. The Response will be the result
* of this fork. The previous flow will still be set at the current execution. This is used by reset password when it sends an email.
* It sends an email linking to the current flow and redirects the browser to a new browser login flow.
*
* This method will set up a success message that will be displayed in the first page of the new flow
*
* @param message Corresponds to raw text or a message property defined in a message bundle
*/
void forkWithSuccessMessage(FormMessage message);
/**
* Fork the current flow. The client session will be cloned and set to point at the realm's browser login flow. The Response will be the result
* of this fork. The previous flow will still be set at the current execution. This is used by reset password when it sends an email.
* It sends an email linking to the current flow and redirects the browser to a new browser login flow.
*
* This method will set up an error message that will be displayed in the first page of the new flow
*
* @param message Corresponds to raw text or a message property defined in a message bundle
*/
void forkWithErrorMessage(FormMessage message);
}
@@ -0,0 +1,30 @@
package org.keycloak.authentication;
/**
* Set of error codes that can be thrown by an Authenticator, FormAuthenticator, or FormAction
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public enum AuthenticationFlowError {
EXPIRED_CODE,
INVALID_CLIENT_SESSION,
INVALID_USER,
INVALID_CREDENTIALS,
CREDENTIAL_SETUP_REQUIRED,
USER_DISABLED,
USER_CONFLICT,
USER_TEMPORARILY_DISABLED,
INTERNAL_ERROR,
UNKNOWN_USER,
FORK_FLOW,
UNKNOWN_CLIENT,
CLIENT_NOT_FOUND,
CLIENT_DISABLED,
CLIENT_CREDENTIALS_SETUP_REQUIRED,
INVALID_CLIENT_CREDENTIALS,
IDENTITY_PROVIDER_NOT_FOUND,
IDENTITY_PROVIDER_DISABLED,
IDENTITY_PROVIDER_ERROR
}
@@ -0,0 +1,39 @@
package org.keycloak.authentication;
/**
* Throw this exception from an Authenticator, FormAuthenticator, or FormAction if you want to completely abort the flow.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthenticationFlowException extends RuntimeException {
private AuthenticationFlowError error;
public AuthenticationFlowException(AuthenticationFlowError error) {
this.error = error;
}
public AuthenticationFlowException(String message, AuthenticationFlowError error) {
super(message);
this.error = error;
}
public AuthenticationFlowException(String message, Throwable cause, AuthenticationFlowError error) {
super(message, cause);
this.error = error;
}
public AuthenticationFlowException(Throwable cause, AuthenticationFlowError error) {
super(cause);
this.error = error;
}
public AuthenticationFlowException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, AuthenticationFlowError error) {
super(message, cause, enableSuppression, writableStackTrace);
this.error = error;
}
public AuthenticationFlowError getError() {
return error;
}
}
@@ -0,0 +1,71 @@
package org.keycloak.authentication;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.Provider;
/**
* This interface is for users that want to add custom authenticators to an authentication flow.
* You must implement this interface as well as an AuthenticatorFactory.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface Authenticator extends Provider {
/**
* Initial call for the authenticator. This method should check the current HTTP request to determine if the request
* satifies the Authenticator's requirements. If it doesn't, it should send back a challenge response by calling
* the AuthenticationFlowContext.challenge(Response). If this challenge is a authentication, the action URL
* of the form must point to
*
* /realms/{realm}/login-actions/authenticate?code={session-code}&execution={executionId}
*
* or
*
* /realms/{realm}/login-actions/registration?code={session-code}&execution={executionId}
*
* {session-code} pertains to the code generated from AuthenticationFlowContext.generateAccessCode(). The {executionId}
* pertains to the AuthenticationExecutionModel.getId() value obtained from AuthenticationFlowContext.getExecution().
*
* The action URL will invoke the action() method described below.
*
* @param context
*/
void authenticate(AuthenticationFlowContext context);
/**
* Called from a form action invocation.
*
* @param context
*/
void action(AuthenticationFlowContext context);
/**
* Does this authenticator require that the user has already been identified? That AuthenticatorContext.getUser() is not null?
*
* @return
*/
boolean requiresUser();
/**
* Is this authenticator configured for this user.
*
* @param session
* @param realm
* @param user
* @return
*/
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
/**
* Set actions to configure authenticator
*
*/
void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
}
@@ -0,0 +1,16 @@
package org.keycloak.authentication;
import org.keycloak.provider.ProviderFactory;
/**
* Factory for creating Authenticator instances. This is a singleton and created when Keycloak boots.
*
* You must specify a file
* META-INF/services/org.keycloak.authentication.AuthenticatorFactory in the jar that this class is contained in
* This file must have the fully qualified class name of all your AuthenticatorFactory classes
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface AuthenticatorFactory extends ProviderFactory<Authenticator>, ConfigurableAuthenticatorFactory {
}
@@ -0,0 +1,32 @@
package org.keycloak.authentication;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AuthenticatorSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "authenticator";
}
@Override
public Class<? extends Provider> getProviderClass() {
return Authenticator.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return AuthenticatorFactory.class;
}
}
@@ -0,0 +1,39 @@
package org.keycloak.authentication;
import java.util.Map;
import org.keycloak.models.ClientModel;
/**
* Encapsulates information about the execution in ClientAuthenticationFlow
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface ClientAuthenticationFlowContext extends AbstractAuthenticationFlowContext {
/**
* Current client attached to this flow. It can return null if no client has been identified yet
*
* @return
*/
ClientModel getClient();
/**
* Attach a specific client to this flow.
*
* @param client
*/
void setClient(ClientModel client);
/**
* Return the map where the authenticators can put some additional state related to authenticated client and the context how was
* client authenticated (ie. attributes from client certificate etc). Map is writable, so you can add/remove items from it as needed.
*
* After successful authentication will be those state data put into UserSession notes. This allows you to configure
* UserSessionNote protocol mapper for your client, which will allow to map those state data into the access token available in the application
*
* @return
*/
Map<String, String> getClientAuthAttributes();
}
@@ -0,0 +1,28 @@
package org.keycloak.authentication;
import org.keycloak.provider.Provider;
/**
* This interface is for users that want to add custom client authenticators to an authentication flow.
* You must implement this interface as well as a ClientAuthenticatorFactory.
*
* This interface is for verifying client credentials from request. On the adapter side, you must also implement org.keycloak.adapters.authentication.ClientCredentialsProvider , which is supposed
* to add the client credentials to the request, which will ClientAuthenticator verify on server side
*
* @see org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator
* @see org.keycloak.authentication.authenticators.client.JWTClientAuthenticator
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface ClientAuthenticator extends Provider {
/**
* Initial call for the authenticator. This method should check the current HTTP request to determine if the request
* satisfies the ClientAuthenticator's requirements. If it doesn't, it should send back a challenge response by calling
* the ClientAuthenticationFlowContext.challenge(Response).
*
* @param context
*/
void authenticateClient(ClientAuthenticationFlowContext context);
}
@@ -0,0 +1,46 @@
package org.keycloak.authentication;
import java.util.List;
import java.util.Map;
import org.keycloak.models.ClientModel;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderFactory;
/**
* Factory for creating ClientAuthenticator instances. This is a singleton and created when Keycloak boots.
*
* You must specify a file
* META-INF/services/org.keycloak.authentication.ClientAuthenticatorFactory in the jar that this class is contained in
* This file must have the fully qualified class name of all your ClientAuthenticatorFactory classes
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface ClientAuthenticatorFactory extends ProviderFactory<ClientAuthenticator>, ConfigurableAuthenticatorFactory {
ClientAuthenticator create();
/**
* Is this authenticator configurable globally?
*
* @return
*/
@Override
boolean isConfigurable();
/**
* List of config properties for this client implementation. Those will be shown in admin console in clients credentials tab and can be configured per client.
* Applicable only if "isConfigurablePerClient" is true
*
* @return
*/
List<ProviderConfigProperty> getConfigPropertiesPerClient();
/**
* Get configuration, which needs to be used for adapter ( keycloak.json ) of particular client. Some implementations
* may return just template and user needs to edit the values according to his environment (For example fill the location of keystore file)
*
* @return
*/
Map<String, Object> getAdapterConfiguration(ClientModel client);
}
@@ -0,0 +1,31 @@
package org.keycloak.authentication;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ClientAuthenticatorSpi implements Spi {
@Override
public boolean isInternal() {
return false;
}
@Override
public String getName() {
return "client-authenticator";
}
@Override
public Class<? extends Provider> getProviderClass() {
return ClientAuthenticator.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return ClientAuthenticatorFactory.class;
}
}
@@ -0,0 +1,48 @@
package org.keycloak.authentication;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.provider.ConfiguredProvider;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface ConfigurableAuthenticatorFactory extends ConfiguredProvider {
/**
* Friendly name for the authenticator
*
* @return
*/
String getDisplayType();
/**
* General authenticator type, i.e. totp, password, cert.
*
* @return null if not a referencable category
*/
String getReferenceCategory();
/**
* Is this authenticator configurable?
*
* @return
*/
boolean isConfigurable();
/**
* What requirement settings are allowed.
*
* @return
*/
AuthenticationExecutionModel.Requirement[] getRequirementChoices();
/**
*
* Does this authenticator have required actions that can set if the user does not have
* this authenticator set up?
*
*
* @return
*/
boolean isUserSetupAllowed();
}
@@ -0,0 +1,60 @@
package org.keycloak.authentication;
/**
* Status of an execution/authenticator in a Authentication Flow
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public enum FlowStatus {
/**
* Successful execution
*/
SUCCESS,
/**
* Execution offered a challenge. Optional executions will ignore this challenge. Alternative executions may
* ignore the challenge depending on the status of other executions in the flow.
*
*/
CHALLENGE,
/**
* Irregardless of the execution's requirement, this challenge will be sent to the user.
*
*/
FORCE_CHALLENGE,
/**
* Flow will be aborted and a Response provided by the execution will be sent.
*
*/
FAILURE_CHALLENGE,
/**
* Flow will be aborted.
*
*/
FAILED,
/**
* This is not an error condition. Execution was attempted, but the authenticator is unable to process the request. An example of this is if
* a Kerberos authenticator did not see a negotiate header. There was no error, but the execution was attempted.
*
*/
ATTEMPTED,
/**
* This flow is being forked. The current client session is being cloned, reset, and redirected to browser login.
*
*/
FORK,
/**
* This flow was reset to the beginning. An example is hitting cancel on the OTP page which will bring you back to the
* username password page.
*
*/
FLOW_RESET
}
@@ -0,0 +1,29 @@
package org.keycloak.authentication;
import org.keycloak.models.utils.FormMessage;
/**
* Thrown internally when authenticator wants to fork the current flow.
*
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ForkFlowException extends AuthenticationFlowException {
protected FormMessage successMessage;
protected FormMessage errorMessage;
public FormMessage getSuccessMessage() {
return successMessage;
}
public FormMessage getErrorMessage() {
return errorMessage;
}
public ForkFlowException(FormMessage successMessage, FormMessage errorMessage) {
super(AuthenticationFlowError.FORK_FLOW);
this.successMessage = successMessage;
this.errorMessage = errorMessage;
}
}
@@ -0,0 +1,65 @@
package org.keycloak.authentication;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.Provider;
/**
* Fine grain processing of a form. Allows you to split up the processing of a form into smaller parts so that you can
* enable/disable them from the admin console. For example, Recaptcha is a FormAction. This allows you as the admin
* to turn Recaptcha on/off even though it is on the same form/page as other registration validation.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface FormAction extends Provider {
/**
* When a FormAuthenticator is rendering the challenge page, even FormAction.buildPage() method will be called
* This gives the FormAction the opportunity to add additional attributes to the form to be displayed.
*
* @param context
* @param form
*/
void buildPage(FormContext context, LoginFormsProvider form);
/**
* This is the first phase of form processing. Each FormAction.validate() method is called. This gives the
* FormAction a chance to validate and challenge if user input is invalid.
*
* @param context
*/
void validate(ValidationContext context);
/**
* Called after all validate() calls of all FormAction providers are successful.
*
* @param context
*/
void success(FormContext context);
/**
* Does this FormAction require that a user be set? For registration, this method will always return false.
*
* @return
*/
boolean requiresUser();
/**
* Is this FormAction configured for the current user?
*
* @param session
* @param realm
* @param user
* @return
*/
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
/**
* Set actions to configure authenticator
*
*/
void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
}
@@ -0,0 +1,16 @@
package org.keycloak.authentication;
import org.keycloak.provider.ProviderFactory;
/**
* Factory for instantiating FormAction objects. This is a singleton and created when Keycloak boots.
*
* You must specify a file
* META-INF/services/org.keycloak.authentication.FormActionFactory in the jar that this class is contained in
* This file must have the fully qualified class name of all your FormActionFactory classes
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface FormActionFactory extends ProviderFactory<FormAction>, ConfigurableAuthenticatorFactory {
}
@@ -0,0 +1,32 @@
package org.keycloak.authentication;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class FormActionSpi implements Spi {
@Override
public boolean isInternal() {
return false;
}
@Override
public String getName() {
return "form-action";
}
@Override
public Class<? extends Provider> getProviderClass() {
return FormAction.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return FormActionFactory.class;
}
}
@@ -0,0 +1,27 @@
package org.keycloak.authentication;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.provider.Provider;
import javax.ws.rs.core.Response;
/**
* This class is responsible for rendering a form. The way it works is that each FormAction that is a child of this
* FormAuthenticator, will have its buildPage() method call first, then the FormAuthenticator.render() method will be invoked.
*
* This gives each FormAction a chance to add information to the form in an independent manner.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface FormAuthenticator extends Provider {
/**
* Called to render the FormAuthenticator's challenge page. If null is returned, then success is assumed and the
* next authenticator in the flow will be invoked.
*
* @param context
* @param form
* @return
*/
Response render(FormContext context, LoginFormsProvider form);
}
@@ -0,0 +1,16 @@
package org.keycloak.authentication;
import org.keycloak.provider.ProviderFactory;
/**
* Factory for instantiating FormAuthenticators. This is a singleton and created when Keycloak boots.
*
* You must specify a file
* META-INF/services/org.keycloak.authentication.FormAuthenticatorFactory in the jar that this class is contained in
* This file must have the fully qualified class name of all your FormAuthenticatorFactory classes
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface FormAuthenticatorFactory extends ProviderFactory<FormAuthenticator>, ConfigurableAuthenticatorFactory {
}
@@ -0,0 +1,32 @@
package org.keycloak.authentication;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class FormAuthenticatorSpi implements Spi {
@Override
public boolean isInternal() {
return false;
}
@Override
public String getName() {
return "form-authenticator";
}
@Override
public Class<? extends Provider> getProviderClass() {
return FormAuthenticator.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return FormAuthenticatorFactory.class;
}
}
@@ -0,0 +1,100 @@
package org.keycloak.authentication;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.common.ClientConnection;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import javax.ws.rs.core.UriInfo;
/**
* Interface that encapsulates the current state of the current form being executed
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface FormContext {
/**
* Current event builder being used
*
* @return
*/
EventBuilder getEvent();
/**
* Create a refresh new EventBuilder to use within this context
*
* @return
*/
EventBuilder newEvent();
/**
* The current execution in the flow
*
* @return
*/
AuthenticationExecutionModel getExecution();
/**
* Current user attached to this flow. It can return null if no uesr has been identified yet
*
* @return
*/
UserModel getUser();
/**
* Attach a specific user to this flow.
*
* @param user
*/
void setUser(UserModel user);
/**
* Current realm
*
* @return
*/
RealmModel getRealm();
/**
* ClientSessionModel attached to this flow
*
* @return
*/
ClientSessionModel getClientSession();
/**
* Information about the IP address from the connecting HTTP client.
*
* @return
*/
ClientConnection getConnection();
/**
* UriInfo of the current request
*
* @return
*/
UriInfo getUriInfo();
/**
* Current session
*
* @return
*/
KeycloakSession getSession();
HttpRequest getHttpRequest();
/**
* Get any configuration associated with the current execution
*
* @return
*/
AuthenticatorConfigModel getAuthenticatorConfig();
}
@@ -0,0 +1,118 @@
package org.keycloak.authentication;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.common.ClientConnection;
import org.keycloak.events.EventBuilder;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
/**
* Interface that encapsulates current information about the current requred action
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface RequiredActionContext {
public static enum Status {
CHALLENGE,
SUCCESS,
IGNORE,
FAILURE
}
/**
* Get the action URL for the required action.
*
* @param code client sessino access code
* @return
*/
URI getActionUrl(String code);
/**
* Get the action URL for the required action. This auto-generates the access code.
*
* @return
*/
URI getActionUrl();
/**
* Create a Freemarker form builder that presets the user, action URI, and a generated access code
*
* @return
*/
LoginFormsProvider form();
/**
* If challenge has been sent this returns the JAX-RS Response
*
* @return
*/
Response getChallenge();
/**
* Current event builder being used
*
* @return
*/
EventBuilder getEvent();
/**
* Current user
*
* @return
*/
UserModel getUser();
RealmModel getRealm();
ClientSessionModel getClientSession();
UserSessionModel getUserSession();
ClientConnection getConnection();
UriInfo getUriInfo();
KeycloakSession getSession();
HttpRequest getHttpRequest();
/**
* Generates access code and updates clientsession timestamp
* Access codes must be included in form action callbacks as a query parameter.
*
* @return
*/
String generateCode();
Status getStatus();
/**
* Send a challenge Response back to user
*
* @param response
*/
void challenge(Response response);
/**
* Abort the authentication with an error
*
*/
void failure();
/**
* Mark this required action as successful. The required action will be removed from the UserModel
*
*/
void success();
/**
* Ignore this required action and go onto the next, or complete the flow.
*
*/
void ignore();
}
@@ -0,0 +1,21 @@
package org.keycloak.authentication;
import org.keycloak.provider.ProviderFactory;
/**
* You must specify a file
* META-INF/services/org.keycloak.authentication.RequiredActionFactory in the jar that this class is contained in
* This file must have the fully qualified class name of all your RequiredActionFactory classes
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface RequiredActionFactory extends ProviderFactory<RequiredActionProvider> {
/**
* Display text used in admin console to reference this required action
*
* @return
*/
String getDisplayText();
}
@@ -0,0 +1,37 @@
package org.keycloak.authentication;
import org.keycloak.provider.Provider;
/**
* RequiredAction provider. Required actions are one-time actions that a user must perform before they are logged in.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface RequiredActionProvider extends Provider {
/**
* Called every time a uesr authenticates. This checks to see if this required action should be triggered.
* The implementation of this method is responsible for setting the required action on the UserModel.
*
* For example, the UpdatePassword required actions checks the password policies to see if the password has expired.
*
* @param context
*/
void evaluateTriggers(RequiredActionContext context);
/**
* If the user has a required action set, this method will be the initial call to obtain what to display to the
* user's browser. Return null if no action should be done.
*
* @param context
* @return
*/
void requiredActionChallenge(RequiredActionContext context);
/**
* Called when a required action has form input you want to process.
*
* @param context
*/
void processAction(RequiredActionContext context);
}
@@ -0,0 +1,32 @@
package org.keycloak.authentication;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RequiredActionSpi implements Spi {
@Override
public boolean isInternal() {
return false;
}
@Override
public String getName() {
return "required-action";
}
@Override
public Class<? extends Provider> getProviderClass() {
return RequiredActionProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return RequiredActionFactory.class;
}
}
@@ -0,0 +1,31 @@
package org.keycloak.authentication;
import org.keycloak.models.utils.FormMessage;
import javax.ws.rs.core.MultivaluedMap;
import java.util.List;
/**
* Interface that encapsulates the current validation that is being performed. Calling success() or validationError()
* sets the status of this current validation.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface ValidationContext extends FormContext {
/**
* Mark this validation as having a validation error
*
* @param formData form data you want to display when the form is refreshed
* @param errors error messages to display on the form
*/
void validationError(MultivaluedMap<String, String> formData, List<FormMessage> errors);
void error(String error);
/**
* Mark this validation as sucessful
*
*/
void success();
}
@@ -27,4 +27,10 @@ org.keycloak.truststore.TruststoreSpi
org.keycloak.connections.httpclient.HttpClientSpi
org.keycloak.models.cache.CacheRealmProviderSpi
org.keycloak.models.cache.CacheUserProviderSpi
org.keycloak.authentication.AuthenticatorSpi
org.keycloak.authentication.ClientAuthenticatorSpi
org.keycloak.authentication.RequiredActionSpi
org.keycloak.authentication.FormAuthenticatorSpi
org.keycloak.authentication.FormActionSpi