示例#1
0
def test_select_preferred_mfa_index_output(email, capsys, mocker, sample_json_response):
    """Test whether the function gives correct output."""
    from tokendito.helpers import select_preferred_mfa_index

    primary_auth = sample_json_response(email=email)
    mfa_options = primary_auth.get("_embedded").get("factors")

    correct_output = (
        "\nSelect your preferred MFA method and press Enter:\n"
        "[0]  OKTA    push                Redmi 6 Pro                 Id: opfrar9yi4bKJNH2WEWQ0x8\n"
        "[1]  GOOGLE  token:software:totp {0} Id: FfdskljfdsS1ljUT0r8\n"
        "[2]  OKTA    token:software:totp {0} Id: fdsfsd6ewREr8\n".format(email)
    )

    mocker.patch("tokendito.helpers.collect_integer", return_value=1)
    select_preferred_mfa_index(mfa_options)
    captured = capsys.readouterr()
    assert captured.out == correct_output
示例#2
0
def test_select_preferred_mfa_index(mocker, sample_json_response):
    """Test whether the function returns index entered by user."""
    from tokendito.helpers import select_preferred_mfa_index

    primary_auth = sample_json_response()
    mfa_options = primary_auth.get("_embedded").get("factors")
    for output in mfa_options:
        mocker.patch("tokendito.helpers.collect_integer", return_value=output)
        assert select_preferred_mfa_index(mfa_options) == output
示例#3
0
def user_mfa_index(preset_mfa, available_mfas, mfa_options):
    """Get mfa method index in request.

    :param preset_mfa: preset mfa method from settings
    :param available_mfas: available mfa method ids
    :param mfa_options: available mfa methods
    """
    logging.debug("Get mfa method index in request.")
    if preset_mfa is not None and preset_mfa in available_mfas:
        mfa_index = available_mfas.index(preset_mfa)
    else:
        mfa_index = helpers.select_preferred_mfa_index(mfa_options)

    return mfa_index
示例#4
0
def authenticate_duo(selected_okta_factor):
    """Accomplish MFA via Duo.

    This is the main function that coordinates the Duo
    multifactor fetching, presentation, selection, challenge,
    and verification until making an Okta callback.

    :param selected_okta_factor: Duo factor information retrieved from Okta.
    :return payload: required payload for Okta callback
    :return headers: required headers for Okta callback
    """
    try:
        duo_info = prepare_duo_info(selected_okta_factor)
    except KeyError as missing_key:
        logging.error(
            "There was an issue parsing the Okta factor."
            " Please try again. \n{}".format(missing_key)
        )
        sys.exit(1)

    # Collect devices, factors, auth params for Duo
    duo_info, duo_auth_response = get_duo_sid(duo_info)
    factor_options = get_duo_devices(duo_auth_response)
    mfa_index = helpers.select_preferred_mfa_index(
        factor_options, factor_key="factor", subfactor_key="device"
    )

    mfa_option = factor_options[mfa_index]
    logging.debug("Selected MFA is [{}]".format(mfa_option))
    passcode = set_passcode(mfa_option)

    txid = duo_mfa_challenge(duo_info, mfa_option, passcode)
    verify_mfa = duo_mfa_verify(duo_info, txid)

    # Make factor callback to Duo
    sig_response = duo_factor_callback(duo_info, verify_mfa)

    # Prepare for Okta callback
    payload = helpers.prepare_payload(
        id=duo_info["factor_id"],
        sig_response=sig_response,
        stateToken=duo_info["state_token"],
    )
    headers = {}
    headers["content-type"] = "application/json"
    headers["accept"] = "application/json"

    return payload, headers, duo_info["okta_callback_url"]
示例#5
0
def user_mfa_challenge(headers, primary_auth):
    """Handle user mfa challenges.

    :param headers: headers what needs to be sent to api
    :param primary_auth: primary authentication
    :return: Okta MFA Session token after the successful entry of the code

    """
    logging.debug("Handle user MFA challenges")
    try:
        mfa_options = primary_auth["_embedded"]["factors"]
    except KeyError:
        logging.error("Okta auth failed: "
                      "Could not retrieve list of MFA methods")
        logging.debug("Error parsing response: {}".format(
            json.dumps(primary_auth)))
        sys.exit(1)

    mfa_setup_statuses = [
        d["status"] for d in mfa_options
        if "status" in d and d["status"] != "ACTIVE"
    ]

    if len(mfa_setup_statuses) == len(mfa_options):
        logging.error("MFA not configured. "
                      "Please enable MFA on your account and try again.")
        sys.exit(2)

    preset_mfa = settings.mfa_method
    available_mfas = [d["factorType"] for d in mfa_options]
    if preset_mfa is not None and preset_mfa in available_mfas:
        mfa_index = available_mfas.index(settings.mfa_method)
    else:
        logging.warning(
            "No MFA provided or provided MFA does not exist. [{}]".format(
                settings.mfa_method))
        mfa_index = helpers.select_preferred_mfa_index(mfa_options)

    # time to challenge the mfa option
    selected_mfa_option = mfa_options[mfa_index]
    logging.debug("Selected MFA is [{}]".format(selected_mfa_option))

    mfa_challenge_url = selected_mfa_option["_links"]["verify"]["href"]

    payload = helpers.prepare_payload(
        stateToken=primary_auth["stateToken"],
        factorType=selected_mfa_option["factorType"],
        provider=selected_mfa_option["provider"],
        profile=selected_mfa_option["profile"],
    )
    selected_factor = okta_verify_api_method(mfa_challenge_url, payload,
                                             headers)

    mfa_provider = selected_factor["_embedded"]["factor"]["provider"].lower()
    logging.debug("MFA Challenge URL: [{}] headers: {}".format(
        mfa_challenge_url, headers))

    if mfa_provider == "duo":
        payload, headers, callback_url = duo_helpers.authenticate_duo(
            selected_factor)
        okta_verify_api_method(callback_url, payload)
        payload.pop("id", "sig_response")
        mfa_verify = okta_verify_api_method(mfa_challenge_url, payload,
                                            headers)
    elif mfa_provider == "okta" or mfa_provider == "google":
        mfa_verify = user_mfa_options(selected_mfa_option, headers,
                                      mfa_challenge_url, payload, primary_auth)
    else:
        logging.error(
            "Sorry, the MFA provider '{}' is not yet supported."
            " Please retry with another option.".format(mfa_provider))
        exit(1)
    return mfa_verify["sessionToken"]