Search in sources :

Example 6 with AuthenticationExecutionModel

use of org.keycloak.models.AuthenticationExecutionModel in project keycloak by keycloak.

the class FormAuthenticationFlow method renderForm.

public Response renderForm(MultivaluedMap<String, String> formData, List<FormMessage> errors) {
    String executionId = formExecution.getId();
    processor.getAuthenticationSession().setAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, executionId);
    String code = processor.generateCode();
    URI actionUrl = getActionUrl(executionId, code);
    LoginFormsProvider form = processor.getSession().getProvider(LoginFormsProvider.class).setAuthenticationSession(processor.getAuthenticationSession()).setActionUri(actionUrl).setExecution(executionId).setClientSessionCode(code).setFormData(formData).setErrors(errors);
    for (AuthenticationExecutionModel formActionExecution : formActionExecutions) {
        if (!formActionExecution.isEnabled())
            continue;
        FormAction action = processor.getSession().getProvider(FormAction.class, formActionExecution.getAuthenticator());
        FormContext result = new FormContextImpl(formActionExecution);
        action.buildPage(result, form);
    }
    FormContext context = new FormContextImpl(formExecution);
    return formAuthenticator.render(context, form);
}
Also used : LoginFormsProvider(org.keycloak.forms.login.LoginFormsProvider) AuthenticationExecutionModel(org.keycloak.models.AuthenticationExecutionModel) URI(java.net.URI)

Example 7 with AuthenticationExecutionModel

use of org.keycloak.models.AuthenticationExecutionModel in project keycloak by keycloak.

the class FormAuthenticationFlow method processAction.

@Override
public Response processAction(String actionExecution) {
    if (!actionExecution.equals(formExecution.getId())) {
        throw new AuthenticationFlowException("action is not current execution", AuthenticationFlowError.INTERNAL_ERROR);
    }
    Map<String, AuthenticationSessionModel.ExecutionStatus> executionStatus = new HashMap<>();
    List<FormAction> requiredActions = new LinkedList<>();
    List<ValidationContextImpl> successes = new LinkedList<>();
    List<ValidationContextImpl> errors = new LinkedList<>();
    for (AuthenticationExecutionModel formActionExecution : formActionExecutions) {
        if (!formActionExecution.isEnabled()) {
            executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SKIPPED);
            continue;
        }
        FormActionFactory factory = (FormActionFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(FormAction.class, formActionExecution.getAuthenticator());
        FormAction action = factory.create(processor.getSession());
        UserModel authUser = processor.getAuthenticationSession().getAuthenticatedUser();
        if (action.requiresUser() && authUser == null) {
            throw new AuthenticationFlowException("form action: " + formExecution.getAuthenticator() + " requires user", AuthenticationFlowError.UNKNOWN_USER);
        }
        boolean configuredFor = false;
        if (action.requiresUser() && authUser != null) {
            configuredFor = action.configuredFor(processor.getSession(), processor.getRealm(), authUser);
            if (!configuredFor) {
                if (formActionExecution.isRequired()) {
                    if (factory.isUserSetupAllowed()) {
                        AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", formExecution.getAuthenticator());
                        executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SETUP_REQUIRED);
                        requiredActions.add(action);
                        continue;
                    } else {
                        throw new AuthenticationFlowException(AuthenticationFlowError.CREDENTIAL_SETUP_REQUIRED);
                    }
                } else if (formActionExecution.isConditional()) {
                    executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SKIPPED);
                    continue;
                }
            }
        }
        ValidationContextImpl result = new ValidationContextImpl(formActionExecution, action);
        action.validate(result);
        if (result.success) {
            executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SUCCESS);
            successes.add(result);
        } else {
            executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.CHALLENGED);
            errors.add(result);
        }
    }
    if (!errors.isEmpty()) {
        processor.logFailure();
        List<FormMessage> messages = new LinkedList<>();
        Set<String> fields = new HashSet<>();
        for (ValidationContextImpl v : errors) {
            for (FormMessage m : v.errors) {
                if (!fields.contains(m.getField())) {
                    if (v.excludeOthers) {
                        fields.clear();
                        messages.clear();
                    }
                    fields.add(m.getField());
                    messages.add(m);
                    if (v.excludeOthers) {
                        break;
                    }
                }
            }
        }
        ValidationContextImpl first = errors.get(0);
        first.getEvent().error(first.error);
        return renderForm(first.formData, messages);
    }
    for (ValidationContextImpl context : successes) {
        context.action.success(context);
    }
    // set status and required actions only if form is fully successful
    for (Map.Entry<String, AuthenticationSessionModel.ExecutionStatus> entry : executionStatus.entrySet()) {
        processor.getAuthenticationSession().setExecutionStatus(entry.getKey(), entry.getValue());
    }
    for (FormAction action : requiredActions) {
        action.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser());
    }
    processor.getAuthenticationSession().setExecutionStatus(actionExecution, AuthenticationSessionModel.ExecutionStatus.SUCCESS);
    processor.getAuthenticationSession().removeAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
    return null;
}
Also used : HashMap(java.util.HashMap) AuthenticationExecutionModel(org.keycloak.models.AuthenticationExecutionModel) LinkedList(java.util.LinkedList) UserModel(org.keycloak.models.UserModel) FormMessage(org.keycloak.models.utils.FormMessage) HashMap(java.util.HashMap) Map(java.util.Map) MultivaluedMap(javax.ws.rs.core.MultivaluedMap) HashSet(java.util.HashSet)

Example 8 with AuthenticationExecutionModel

use of org.keycloak.models.AuthenticationExecutionModel in project keycloak by keycloak.

the class AuthenticationSelectionResolver method createAuthenticationSelectionList.

/**
 * This method creates the list of authenticators that is presented to the user. For a required execution, this is
 * only the credentials associated to the authenticator, and for an alternative execution, this is all other alternative
 * executions in the flow, including the credentials.
 * <p>
 * In both cases, the credentials take precedence, with the order selected by the user (or his administrator).
 * <p>
 * The implementation needs to take various subflows into account.
 *
 * For example during configuration of the authentication flow like this:
 * - WebAuthn:                 ALTERNATIVE
 * - Password-and-OTP subflow:  ALTERNATIVE
 *   - Password REQUIRED
 *   - OTP      REQUIRED
 * The user can authenticate with: WebAuthn OR (Password AND OTP). In this case, the user should be able to choose between WebAuthn and Password
 * even if those mechanisms are in different subflows
 *
 * @param model The current execution model
 * @return an ordered list of the authentication selection options to present the user.
 */
static List<AuthenticationSelectionOption> createAuthenticationSelectionList(AuthenticationProcessor processor, AuthenticationExecutionModel model) {
    List<AuthenticationSelectionOption> authenticationSelectionList = new ArrayList<>();
    if (processor.getAuthenticationSession() != null) {
        Map<String, AuthenticationExecutionModel> typeAuthExecMap = new HashMap<>();
        List<AuthenticationExecutionModel> nonCredentialExecutions = new ArrayList<>();
        String topFlowId = getFlowIdOfTheHighestUsefulFlow(processor, model);
        if (topFlowId == null) {
            addSimpleAuthenticationExecution(processor, model, typeAuthExecMap, nonCredentialExecutions);
        } else {
            addAllExecutionsFromSubflow(processor, topFlowId, typeAuthExecMap, nonCredentialExecutions);
        }
        // add credential authenticators in order
        if (processor.getAuthenticationSession().getAuthenticatedUser() != null) {
            authenticationSelectionList = Stream.concat(processor.getSession().userCredentialManager().getStoredCredentialsStream(processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser()).map(CredentialModel::getType), processor.getSession().userCredentialManager().getConfiguredUserStorageCredentialTypesStream(processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser())).distinct().filter(typeAuthExecMap::containsKey).map(credentialType -> new AuthenticationSelectionOption(processor.getSession(), typeAuthExecMap.get(credentialType))).collect(Collectors.toList());
        }
        // add all other authenticators
        for (AuthenticationExecutionModel exec : nonCredentialExecutions) {
            authenticationSelectionList.add(new AuthenticationSelectionOption(processor.getSession(), exec));
        }
    }
    logger.debugf("Selections when trying execution '%s' : %s", model.getAuthenticator(), authenticationSelectionList);
    return authenticationSelectionList;
}
Also used : CredentialModel(org.keycloak.credential.CredentialModel) HashMap(java.util.HashMap) AuthenticationExecutionModel(org.keycloak.models.AuthenticationExecutionModel) ArrayList(java.util.ArrayList)

Example 9 with AuthenticationExecutionModel

use of org.keycloak.models.AuthenticationExecutionModel in project keycloak by keycloak.

the class AuthenticationSelectionResolver method addAllExecutionsFromSubflow.

/**
 * Fill the typeAuthExecMap and nonCredentialExecutions collections with all available authentication mechanisms for the particular subflow with
 * given flowId
 *
 * Return true if at least something was added to any of the list
 */
private static boolean addAllExecutionsFromSubflow(AuthenticationProcessor processor, String flowId, Map<String, AuthenticationExecutionModel> typeAuthExecMap, List<AuthenticationExecutionModel> nonCredentialExecutions) {
    AuthenticationFlowModel flowModel = processor.getRealm().getAuthenticationFlowById(flowId);
    if (flowModel == null) {
        throw new AuthenticationFlowException("Flow not found", AuthenticationFlowError.INTERNAL_ERROR);
    }
    DefaultAuthenticationFlow flow = new DefaultAuthenticationFlow(processor, flowModel);
    logger.debugf("Going through the flow '%s' for adding executions", flowModel.getAlias());
    List<AuthenticationExecutionModel> requiredList = new ArrayList<>();
    List<AuthenticationExecutionModel> alternativeList = new ArrayList<>();
    flow.fillListsOfExecutions(processor.getRealm().getAuthenticationExecutionsStream(flowId), requiredList, alternativeList);
    // If requiredList is not empty, we're going to collect just very first execution from the flow
    if (!requiredList.isEmpty()) {
        AuthenticationExecutionModel requiredExecution = requiredList.stream().filter(ex -> {
            if (ex.isRequired())
                return true;
            // requiredExecution in the list
            return !flow.isConditionalSubflowDisabled(ex);
        }).findFirst().orElse(null);
        // Not requiredExecution found. Returning false as we did not add any authenticator
        if (requiredExecution == null)
            return false;
        // Don't add already processed executions
        if (flow.isProcessed(requiredExecution)) {
            return false;
        }
        FormAuthenticatorFactory factory = (FormAuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(FormAuthenticator.class, requiredExecution.getAuthenticator());
        // Recursively add credentials from required execution
        if (requiredExecution.isAuthenticatorFlow() && factory == null) {
            return addAllExecutionsFromSubflow(processor, requiredExecution.getFlowId(), typeAuthExecMap, nonCredentialExecutions);
        } else {
            addSimpleAuthenticationExecution(processor, requiredExecution, typeAuthExecMap, nonCredentialExecutions);
            return true;
        }
    } else {
        // We're going through all the alternatives
        boolean anyAdded = false;
        for (AuthenticationExecutionModel execution : alternativeList) {
            // Don't add already processed executions
            if (flow.isProcessed(execution)) {
                continue;
            }
            if (!execution.isAuthenticatorFlow()) {
                addSimpleAuthenticationExecution(processor, execution, typeAuthExecMap, nonCredentialExecutions);
                anyAdded = true;
            } else {
                anyAdded |= addAllExecutionsFromSubflow(processor, execution.getFlowId(), typeAuthExecMap, nonCredentialExecutions);
            }
        }
        return anyAdded;
    }
}
Also used : AuthenticationExecutionModel(org.keycloak.models.AuthenticationExecutionModel) ArrayList(java.util.ArrayList) AuthenticationFlowModel(org.keycloak.models.AuthenticationFlowModel)

Example 10 with AuthenticationExecutionModel

use of org.keycloak.models.AuthenticationExecutionModel in project keycloak by keycloak.

the class ClientAuthenticationFlow method processResult.

protected Response processResult(AuthenticationProcessor.Result result) {
    AuthenticationExecutionModel execution = result.getExecution();
    FlowStatus status = result.getStatus();
    logger.debugv("client authenticator {0}: {1}", status.toString(), execution.getAuthenticator());
    if (status == FlowStatus.SUCCESS) {
        return null;
    }
    if (status == FlowStatus.FAILED) {
        if (result.getChallenge() != null) {
            return sendChallenge(result, execution);
        } else {
            throw new AuthenticationFlowException(result.getError());
        }
    } else if (status == FlowStatus.FORCE_CHALLENGE) {
        return sendChallenge(result, execution);
    } else if (status == FlowStatus.CHALLENGE) {
        // Make sure the first priority alternative challenge is used
        if (alternativeChallenge == null) {
            alternativeChallenge = result.getChallenge();
        }
        return sendChallenge(result, execution);
    } else if (status == FlowStatus.FAILURE_CHALLENGE) {
        return sendChallenge(result, execution);
    } else {
        ServicesLogger.LOGGER.unknownResultStatus();
        throw new AuthenticationFlowException(AuthenticationFlowError.INTERNAL_ERROR);
    }
}
Also used : AuthenticationExecutionModel(org.keycloak.models.AuthenticationExecutionModel)

Aggregations

AuthenticationExecutionModel (org.keycloak.models.AuthenticationExecutionModel)51 AuthenticationFlowModel (org.keycloak.models.AuthenticationFlowModel)32 AuthenticatorConfigModel (org.keycloak.models.AuthenticatorConfigModel)11 Path (javax.ws.rs.Path)8 NoCache (org.jboss.resteasy.annotations.cache.NoCache)8 HashMap (java.util.HashMap)7 Response (javax.ws.rs.core.Response)7 RealmModel (org.keycloak.models.RealmModel)7 BadRequestException (javax.ws.rs.BadRequestException)6 NotFoundException (javax.ws.rs.NotFoundException)6 POST (javax.ws.rs.POST)6 ArrayList (java.util.ArrayList)5 LinkedList (java.util.LinkedList)5 Consumes (javax.ws.rs.Consumes)5 Before (org.junit.Before)5 ClientModel (org.keycloak.models.ClientModel)4 List (java.util.List)3 UserModel (org.keycloak.models.UserModel)3 MultivaluedMap (javax.ws.rs.core.MultivaluedMap)2 Logger (org.jboss.logging.Logger)2