2016-02-03 10:20:22 +00:00
/ *
* Copyright 2016 Red Hat , Inc . and / or its affiliates
* and other contributors as indicated by the @author tags .
*
* Licensed under the Apache License , Version 2 . 0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
2015-07-17 11:45:43 +00:00
package org.keycloak.authentication ;
2016-10-17 13:19:54 +00:00
import org.jboss.logging.Logger ;
2015-07-17 11:45:43 +00:00
import org.keycloak.models.AuthenticationExecutionModel ;
import org.keycloak.models.AuthenticationFlowModel ;
import org.keycloak.models.ClientSessionModel ;
import org.keycloak.models.UserModel ;
2016-01-18 17:18:15 +00:00
import org.keycloak.services.ServicesLogger ;
2015-10-14 15:49:36 +00:00
2015-07-17 11:45:43 +00:00
import javax.ws.rs.core.Response ;
import java.util.Iterator ;
import java.util.List ;
/ * *
2016-01-13 21:18:24 +00:00
* @author < a href = " mailto:bill@burkecentral.com " > Bill Burke < / a >
* @version $Revision : 1 $
* /
2015-07-17 11:45:43 +00:00
public class DefaultAuthenticationFlow implements AuthenticationFlow {
2016-10-17 13:19:54 +00:00
private static final Logger logger = Logger . getLogger ( DefaultAuthenticationFlow . class ) ;
2015-07-17 11:45:43 +00:00
Response alternativeChallenge = null ;
AuthenticationExecutionModel challengedAlternativeExecution = null ;
boolean alternativeSuccessful = false ;
List < AuthenticationExecutionModel > executions ;
Iterator < AuthenticationExecutionModel > executionIterator ;
AuthenticationProcessor processor ;
AuthenticationFlowModel flow ;
public DefaultAuthenticationFlow ( AuthenticationProcessor processor , AuthenticationFlowModel flow ) {
this . processor = processor ;
this . flow = flow ;
this . executions = processor . getRealm ( ) . getAuthenticationExecutions ( flow . getId ( ) ) ;
this . executionIterator = executions . iterator ( ) ;
}
protected boolean isProcessed ( AuthenticationExecutionModel model ) {
if ( model . isDisabled ( ) ) return true ;
ClientSessionModel . ExecutionStatus status = processor . getClientSession ( ) . getExecutionStatus ( ) . get ( model . getId ( ) ) ;
if ( status = = null ) return false ;
return status = = ClientSessionModel . ExecutionStatus . SUCCESS | | status = = ClientSessionModel . ExecutionStatus . SKIPPED
| | status = = ClientSessionModel . ExecutionStatus . ATTEMPTED
| | status = = ClientSessionModel . ExecutionStatus . SETUP_REQUIRED ;
}
@Override
public Response processAction ( String actionExecution ) {
2015-12-16 17:23:41 +00:00
logger . debugv ( " processAction: {0} " , actionExecution ) ;
2015-07-17 11:45:43 +00:00
while ( executionIterator . hasNext ( ) ) {
AuthenticationExecutionModel model = executionIterator . next ( ) ;
2015-12-16 17:23:41 +00:00
logger . debugv ( " check: {0} requirement: {1} " , model . getAuthenticator ( ) , model . getRequirement ( ) . toString ( ) ) ;
2015-07-17 11:45:43 +00:00
if ( isProcessed ( model ) ) {
2015-12-16 17:23:41 +00:00
logger . debug ( " execution is processed " ) ;
2015-07-17 11:45:43 +00:00
if ( ! alternativeSuccessful & & model . isAlternative ( ) & & processor . isSuccessful ( model ) )
alternativeSuccessful = true ;
continue ;
}
2015-08-02 23:03:33 +00:00
if ( model . isAuthenticatorFlow ( ) ) {
2015-07-17 11:45:43 +00:00
AuthenticationFlow authenticationFlow = processor . createFlowExecution ( model . getFlowId ( ) , model ) ;
return authenticationFlow . processAction ( actionExecution ) ;
} else if ( model . getId ( ) . equals ( actionExecution ) ) {
AuthenticatorFactory factory = ( AuthenticatorFactory ) processor . getSession ( ) . getKeycloakSessionFactory ( ) . getProviderFactory ( Authenticator . class , model . getAuthenticator ( ) ) ;
2015-08-14 16:03:37 +00:00
if ( factory = = null ) {
throw new RuntimeException ( " Unable to find factory for AuthenticatorFactory: " + model . getAuthenticator ( ) + " did you forget to declare it in a META-INF/services file? " ) ;
}
2015-08-13 19:32:58 +00:00
Authenticator authenticator = factory . create ( processor . getSession ( ) ) ;
2015-08-09 19:06:24 +00:00
AuthenticationProcessor . Result result = processor . createAuthenticatorContext ( model , authenticator , executions ) ;
2015-12-16 17:23:41 +00:00
logger . debugv ( " action: {0} " , model . getAuthenticator ( ) ) ;
2015-07-17 11:45:43 +00:00
authenticator . action ( result ) ;
Response response = processResult ( result ) ;
2015-10-15 20:30:21 +00:00
if ( response = = null ) {
2016-01-13 21:18:24 +00:00
processor . getClientSession ( ) . removeNote ( AuthenticationProcessor . CURRENT_AUTHENTICATION_EXECUTION ) ;
2016-01-22 23:39:55 +00:00
if ( result . status = = FlowStatus . SUCCESS ) {
// we do this so that flow can redirect to a non-action URL
processor . setActionSuccessful ( ) ;
}
2016-01-13 21:18:24 +00:00
return processFlow ( ) ;
} else return response ;
2015-07-17 11:45:43 +00:00
}
}
2015-08-09 19:06:24 +00:00
throw new AuthenticationFlowException ( " action is not in current execution " , AuthenticationFlowError . INTERNAL_ERROR ) ;
2015-07-17 11:45:43 +00:00
}
@Override
public Response processFlow ( ) {
2015-12-16 17:23:41 +00:00
logger . debug ( " processFlow " ) ;
2015-07-17 11:45:43 +00:00
while ( executionIterator . hasNext ( ) ) {
AuthenticationExecutionModel model = executionIterator . next ( ) ;
2015-12-16 17:23:41 +00:00
logger . debugv ( " check execution: {0} requirement: {1} " , model . getAuthenticator ( ) , model . getRequirement ( ) . toString ( ) ) ;
2015-07-17 11:45:43 +00:00
if ( isProcessed ( model ) ) {
2015-12-16 17:23:41 +00:00
logger . debug ( " execution is processed " ) ;
2015-07-17 11:45:43 +00:00
if ( ! alternativeSuccessful & & model . isAlternative ( ) & & processor . isSuccessful ( model ) )
alternativeSuccessful = true ;
continue ;
}
if ( model . isAlternative ( ) & & alternativeSuccessful ) {
2015-12-16 17:23:41 +00:00
logger . debug ( " Skip alternative execution " ) ;
2015-07-17 11:45:43 +00:00
processor . getClientSession ( ) . setExecutionStatus ( model . getId ( ) , ClientSessionModel . ExecutionStatus . SKIPPED ) ;
continue ;
}
2015-08-02 23:03:33 +00:00
if ( model . isAuthenticatorFlow ( ) ) {
2015-12-16 17:23:41 +00:00
logger . debug ( " execution is flow " ) ;
2015-07-17 11:45:43 +00:00
AuthenticationFlow authenticationFlow = processor . createFlowExecution ( model . getFlowId ( ) , model ) ;
Response flowChallenge = authenticationFlow . processFlow ( ) ;
if ( flowChallenge = = null ) {
processor . getClientSession ( ) . setExecutionStatus ( model . getId ( ) , ClientSessionModel . ExecutionStatus . SUCCESS ) ;
if ( model . isAlternative ( ) ) alternativeSuccessful = true ;
continue ;
} else {
if ( model . isAlternative ( ) ) {
alternativeChallenge = flowChallenge ;
challengedAlternativeExecution = model ;
} else if ( model . isRequired ( ) ) {
processor . getClientSession ( ) . setExecutionStatus ( model . getId ( ) , ClientSessionModel . ExecutionStatus . CHALLENGED ) ;
return flowChallenge ;
} else if ( model . isOptional ( ) ) {
processor . getClientSession ( ) . setExecutionStatus ( model . getId ( ) , ClientSessionModel . ExecutionStatus . SKIPPED ) ;
continue ;
} else {
processor . getClientSession ( ) . setExecutionStatus ( model . getId ( ) , ClientSessionModel . ExecutionStatus . SKIPPED ) ;
continue ;
}
return flowChallenge ;
}
}
AuthenticatorFactory factory = ( AuthenticatorFactory ) processor . getSession ( ) . getKeycloakSessionFactory ( ) . getProviderFactory ( Authenticator . class , model . getAuthenticator ( ) ) ;
2015-07-22 00:56:05 +00:00
if ( factory = = null ) {
2015-08-14 16:03:37 +00:00
throw new RuntimeException ( " Unable to find factory for AuthenticatorFactory: " + model . getAuthenticator ( ) + " did you forget to declare it in a META-INF/services file? " ) ;
2015-07-22 00:56:05 +00:00
}
2015-08-13 19:32:58 +00:00
Authenticator authenticator = factory . create ( processor . getSession ( ) ) ;
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator: {0} " , factory . getId ( ) ) ;
2015-07-17 11:45:43 +00:00
UserModel authUser = processor . getClientSession ( ) . getAuthenticatedUser ( ) ;
if ( authenticator . requiresUser ( ) & & authUser = = null ) {
if ( alternativeChallenge ! = null ) {
processor . getClientSession ( ) . setExecutionStatus ( challengedAlternativeExecution . getId ( ) , ClientSessionModel . ExecutionStatus . CHALLENGED ) ;
return alternativeChallenge ;
}
2015-08-09 19:06:24 +00:00
throw new AuthenticationFlowException ( " authenticator: " + factory . getId ( ) , AuthenticationFlowError . UNKNOWN_USER ) ;
2015-07-17 11:45:43 +00:00
}
boolean configuredFor = false ;
if ( authenticator . requiresUser ( ) & & authUser ! = null ) {
configuredFor = authenticator . configuredFor ( processor . getSession ( ) , processor . getRealm ( ) , authUser ) ;
if ( ! configuredFor ) {
if ( model . isRequired ( ) ) {
2015-08-03 13:52:56 +00:00
if ( factory . isUserSetupAllowed ( ) ) {
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator SETUP_REQUIRED: {0} " , factory . getId ( ) ) ;
2015-07-17 11:45:43 +00:00
processor . getClientSession ( ) . setExecutionStatus ( model . getId ( ) , ClientSessionModel . ExecutionStatus . SETUP_REQUIRED ) ;
authenticator . setRequiredActions ( processor . getSession ( ) , processor . getRealm ( ) , processor . getClientSession ( ) . getAuthenticatedUser ( ) ) ;
continue ;
} else {
2015-08-09 19:06:24 +00:00
throw new AuthenticationFlowException ( AuthenticationFlowError . CREDENTIAL_SETUP_REQUIRED ) ;
2015-07-17 11:45:43 +00:00
}
} else if ( model . isOptional ( ) ) {
processor . getClientSession ( ) . setExecutionStatus ( model . getId ( ) , ClientSessionModel . ExecutionStatus . SKIPPED ) ;
continue ;
}
}
}
2016-01-22 23:39:55 +00:00
// skip if action as successful already
Response redirect = processor . checkWasSuccessfulBrowserAction ( ) ;
if ( redirect ! = null ) return redirect ;
2015-08-09 19:06:24 +00:00
AuthenticationProcessor . Result context = processor . createAuthenticatorContext ( model , authenticator , executions ) ;
2015-12-16 17:23:41 +00:00
logger . debug ( " invoke authenticator.authenticate " ) ;
2015-07-17 11:45:43 +00:00
authenticator . authenticate ( context ) ;
Response response = processResult ( context ) ;
if ( response ! = null ) return response ;
}
return null ;
}
2015-08-09 19:06:24 +00:00
public Response processResult ( AuthenticationProcessor . Result result ) {
2015-07-17 11:45:43 +00:00
AuthenticationExecutionModel execution = result . getExecution ( ) ;
2015-08-11 17:04:40 +00:00
FlowStatus status = result . getStatus ( ) ;
2015-10-14 15:49:36 +00:00
switch ( status ) {
case SUCCESS :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator SUCCESS: {0} " , execution . getAuthenticator ( ) ) ;
2015-10-14 15:49:36 +00:00
processor . getClientSession ( ) . setExecutionStatus ( execution . getId ( ) , ClientSessionModel . ExecutionStatus . SUCCESS ) ;
if ( execution . isAlternative ( ) ) alternativeSuccessful = true ;
return null ;
case FAILED :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator FAILED: {0} " , execution . getAuthenticator ( ) ) ;
2015-10-14 15:49:36 +00:00
processor . logFailure ( ) ;
processor . getClientSession ( ) . setExecutionStatus ( execution . getId ( ) , ClientSessionModel . ExecutionStatus . FAILED ) ;
if ( result . getChallenge ( ) ! = null ) {
return sendChallenge ( result , execution ) ;
}
throw new AuthenticationFlowException ( result . getError ( ) ) ;
case FORK :
2015-12-16 17:23:41 +00:00
logger . debugv ( " reset browser login from authenticator: {0} " , execution . getAuthenticator ( ) ) ;
2015-10-14 15:49:36 +00:00
processor . getClientSession ( ) . setNote ( AuthenticationProcessor . CURRENT_AUTHENTICATION_EXECUTION , execution . getId ( ) ) ;
throw new ForkFlowException ( result . getSuccessMessage ( ) , result . getErrorMessage ( ) ) ;
case FORCE_CHALLENGE :
2015-07-17 11:45:43 +00:00
processor . getClientSession ( ) . setExecutionStatus ( execution . getId ( ) , ClientSessionModel . ExecutionStatus . CHALLENGED ) ;
return sendChallenge ( result , execution ) ;
2015-10-14 15:49:36 +00:00
case CHALLENGE :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator CHALLENGE: {0} " , execution . getAuthenticator ( ) ) ;
2015-10-14 15:49:36 +00:00
if ( execution . isRequired ( ) ) {
processor . getClientSession ( ) . setExecutionStatus ( execution . getId ( ) , ClientSessionModel . ExecutionStatus . CHALLENGED ) ;
return sendChallenge ( result , execution ) ;
}
UserModel authenticatedUser = processor . getClientSession ( ) . getAuthenticatedUser ( ) ;
if ( execution . isOptional ( ) & & authenticatedUser ! = null & & result . getAuthenticator ( ) . configuredFor ( processor . getSession ( ) , processor . getRealm ( ) , authenticatedUser ) ) {
processor . getClientSession ( ) . setExecutionStatus ( execution . getId ( ) , ClientSessionModel . ExecutionStatus . CHALLENGED ) ;
return sendChallenge ( result , execution ) ;
}
if ( execution . isAlternative ( ) ) {
alternativeChallenge = result . getChallenge ( ) ;
challengedAlternativeExecution = execution ;
} else {
processor . getClientSession ( ) . setExecutionStatus ( execution . getId ( ) , ClientSessionModel . ExecutionStatus . SKIPPED ) ;
}
return null ;
case FAILURE_CHALLENGE :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator FAILURE_CHALLENGE: {0} " , execution . getAuthenticator ( ) ) ;
2015-10-14 15:49:36 +00:00
processor . logFailure ( ) ;
2015-07-17 11:45:43 +00:00
processor . getClientSession ( ) . setExecutionStatus ( execution . getId ( ) , ClientSessionModel . ExecutionStatus . CHALLENGED ) ;
return sendChallenge ( result , execution ) ;
2015-10-14 15:49:36 +00:00
case ATTEMPTED :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator ATTEMPTED: {0} " , execution . getAuthenticator ( ) ) ;
2015-10-14 15:49:36 +00:00
if ( execution . getRequirement ( ) = = AuthenticationExecutionModel . Requirement . REQUIRED ) {
throw new AuthenticationFlowException ( AuthenticationFlowError . INVALID_CREDENTIALS ) ;
}
processor . getClientSession ( ) . setExecutionStatus ( execution . getId ( ) , ClientSessionModel . ExecutionStatus . ATTEMPTED ) ;
return null ;
case FLOW_RESET :
AuthenticationProcessor . resetFlow ( processor . getClientSession ( ) ) ;
return processor . authenticate ( ) ;
default :
2015-12-16 17:23:41 +00:00
logger . debugv ( " authenticator INTERNAL_ERROR: {0} " , execution . getAuthenticator ( ) ) ;
2016-10-17 13:19:54 +00:00
ServicesLogger . LOGGER . unknownResultStatus ( ) ;
2015-10-14 15:49:36 +00:00
throw new AuthenticationFlowException ( AuthenticationFlowError . INTERNAL_ERROR ) ;
2015-07-17 11:45:43 +00:00
}
}
2015-08-09 19:06:24 +00:00
public Response sendChallenge ( AuthenticationProcessor . Result result , AuthenticationExecutionModel execution ) {
2015-07-17 11:45:43 +00:00
processor . getClientSession ( ) . setNote ( AuthenticationProcessor . CURRENT_AUTHENTICATION_EXECUTION , execution . getId ( ) ) ;
return result . getChallenge ( ) ;
}
}