final re-org work
This commit is contained in:
1 parent
d9487a8745
commit
efd8317259
66 files changed
+53
-44
No files matched your search
Executable
+175
@@ -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();
|
||||
}
|
||||
+16
@@ -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();
|
||||
}
|
||||
+116
@@ -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);
|
||||
}
|
||||
+30
@@ -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
|
||||
}
|
||||
+39
@@ -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);
|
||||
|
||||
|
||||
|
||||
}
|
||||
+16
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -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();
|
||||
|
||||
}
|
||||
+28
@@ -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;
|
||||
}
|
||||
}
|
||||
Executable
+48
@@ -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
|
||||
|
||||
}
|
||||
+29
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
+16
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -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);
|
||||
}
|
||||
+16
@@ -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 {
|
||||
}
|
||||
+32
@@ -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();
|
||||
}
|
||||
+118
@@ -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();
|
||||
|
||||
}
|
||||
+21
@@ -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();
|
||||
}
|
||||
+37
@@ -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);
|
||||
}
|
||||
+32
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user