예제 #1
0
def execute(ctx):
    """Execute this module with the configured options"""

    if click.confirm(
            "[*] Do you want to attempt to harvest information on all network zones?",
            default=True):
        msg = "Attempting to harvest all network zones"
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.echo(f"[*] {msg}")

        list_zones(ctx)
예제 #2
0
def delete_configuration_profile(ctx, config):
    msg = f'[*] Do you want to delete the configuration profile for {config["description"]} ({config["okta_url"]})?'

    if click.confirm(msg, default=True):
        file = Path(ctx.obj.config_dir / (config["id"] + ".json"))
        file.unlink()
        msg = f"Configuration file deleted ({file})"
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.secho(f"[*] {msg}", fg="green")

        delete_saved_data(ctx, config["id"])
예제 #3
0
def execute(ctx):
    """Execute this module with the configured options"""

    error = check_module_options(MODULE_OPTIONS)

    if error:
        return

    policy_id = MODULE_OPTIONS["policy_id"]["value"]
    rule_id = MODULE_OPTIONS["rule_id"]["value"]

    rule = get_policy_rule(ctx, policy_id, rule_id)

    if rule:
        if rule["status"] == "ACTIVE":
            click.echo("[*] Rule is ACTIVE")
            if click.confirm(
                    f'[*] Do you want to deactivate rule {rule_id} ({rule["name"]})?',
                    default=True):
                msg = f'Attempting to deactivate rule {rule_id} ({rule["name"]}) in policy {policy_id}'
                LOGGER.info(msg)
                index_event(ctx.obj.es,
                            module=__name__,
                            event_type="INFO",
                            event=msg)
                click.echo(f"[*] {msg}")
                set_policy_rule_state(ctx,
                                      policy_id,
                                      rule_id,
                                      operation="DEACTIVATE")

        elif rule["status"] == "INACTIVE":
            click.echo("[*] Rule is INACTIVE")
            if click.confirm(
                    f'[*] Do you want to activate rule {rule_id} ({rule["name"]})?',
                    default=True):
                msg = f'Attempting to activate rule {rule_id} ({rule["name"]}) in policy {policy_id}'
                LOGGER.info(msg)
                index_event(ctx.obj.es,
                            module=__name__,
                            event_type="INFO",
                            event=msg)
                click.echo(f"[*] {msg}")
                set_policy_rule_state(ctx,
                                      policy_id,
                                      rule_id,
                                      operation="ACTIVATE")

        else:
            click.echo(f'[*] Rule status is {rule["status"]}')
예제 #4
0
파일: get_groups.py 프로젝트: dd-m/dorothy
def execute(ctx):
    """Execute this module with the configured options"""

    if click.confirm(
            "[*] Do you want to attempt to harvest information on all groups? This may take a while to avoid "
            "exceeding API rate limits",
            default=True,
    ):
        msg = "Attempting to harvest all Okta groups"
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.echo(f"[*] {msg}")

        list_groups(ctx)
예제 #5
0
파일: get_user.py 프로젝트: dd-m/dorothy
def execute(ctx):
    """Execute this module with the configured options"""

    error = check_module_options(MODULE_OPTIONS)

    if error:
        return

    msg = f'Attempting to get profile and group memberships for user ID {MODULE_OPTIONS["id"]["value"]}'
    LOGGER.info(msg)
    index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
    click.echo(f"[*] {msg}")

    get_user_object(ctx, MODULE_OPTIONS["id"]["value"])
    get_user_groups(ctx, MODULE_OPTIONS["id"]["value"])
예제 #6
0
def execute(ctx):
    """Execute this module with the configured options"""

    error = check_module_options(MODULE_OPTIONS)

    if error:
        return

    msg = f'Attempting to reset MFA factors for user ID {MODULE_OPTIONS["id"]["value"]}'
    LOGGER.info(msg)
    index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
    click.echo(f"[*] {msg}")

    url = f'{ctx.obj.base_url}/users/{MODULE_OPTIONS["id"]["value"]}/lifecycle/reset_factors'

    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": f"SSWS {ctx.obj.api_token}",
    }

    params = {}
    payload = {}

    try:
        response = ctx.obj.session.post(url,
                                        headers=headers,
                                        params=params,
                                        json=payload,
                                        timeout=7)
    except Exception as e:
        LOGGER.error(e, exc_info=True)
        index_event(ctx.obj.es, module=__name__, event_type="ERROR", event=e)
        click.secho(f"[!] {URL_OR_API_TOKEN_ERROR}", fg="red")
        response = None

    if response.ok:
        msg = f'MFA factors reset for user {MODULE_OPTIONS["id"]["value"]}'
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.secho(f"[*] {msg}", fg="green")

        get_user_object(ctx, MODULE_OPTIONS["id"]["value"])

    else:
        msg = (
            f"Error resetting MFA factors for Okta user\n"
            f"    Response Code: {response.status_code} | Response Reason: {response.reason}\n"
            f'    Error Code: {response.json().get("errorCode")} | Error Summary: {response.json().get("errorSummary")}'
        )
        LOGGER.error(msg)
        index_event(ctx.obj.es, module=__name__, event_type="ERROR", event=msg)
        click.secho(f"[!] {msg}", fg="red")
        click.echo(
            "Check that the user's status is ACTIVE and that they have at least one factor enrolled"
        )

        return
예제 #7
0
def execute(ctx):
    """Execute this module with the configured options"""

    if click.confirm(
            "[*] Do you want to attempt to harvest information for all Okta policies and policy rules?",
            default=True):

        harvested_policies = []

        policy_types = ctx.obj.policy_types

        # Get a list of all policies by policy type
        for policy_type in policy_types:
            policies = list_policies_by_type(ctx, policy_type)
            if policies:
                harvested_policies.extend(policies)

        policies_and_rules = []

        # Get all policies again including their rules
        if harvested_policies:
            for policy in harvested_policies:
                policy_and_rules = get_policy_object(ctx,
                                                     policy["id"],
                                                     rules=True)
                if not policy_and_rules:
                    msg = f'Issue retrieving policy {policy["id"]} ({policy["name"]}) with rules'
                    LOGGER.error(msg)
                    index_event(ctx.obj.es,
                                module=__name__,
                                event_type="ERROR",
                                event=msg)
                    click.secho(f"[!] {msg}", fg="red")
                else:
                    policies_and_rules.append(policy_and_rules)

        if policies_and_rules:
            if click.confirm(
                    "[*] Do you want to print harvested policy information?",
                    default=True):
                for policy in policies_and_rules:
                    print_policy_object(policy)

            if click.confirm(
                    f"[*] Do you want to save {len(policies_and_rules)} harvested policies to a file?",
                    default=True):
                file_path = f"{ctx.obj.data_dir}/{ctx.obj.profile_id}_harvested_policies"
                write_json_file(file_path, policies_and_rules)
예제 #8
0
def delete_saved_data(ctx, profile_id):
    # Get a list of files associated with the configuration profile that was deleted
    files = list(ctx.obj.data_dir.rglob(f"{profile_id}*"))
    if not files:
        click.echo("[*] No associated saved data found for configuration profile")
    else:
        if click.confirm(
            f"[*] Do you want to delete the {len(files)} saved files associated with the " f"configuration profile?",
            default=True,
        ):
            for file in files:
                file.unlink()

                msg = f"File deleted ({file})"
                LOGGER.info(msg)
                index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
                click.secho(f"[*] {msg}", fg="green")
예제 #9
0
def check_assigned_roles(ctx, users):
    """Check if any users have admin roles assigned"""

    admin_users = []

    msg = (
        f"Checking assigned roles for {len(users)} users. This may take a while to avoid exceeding API " f"rate limits"
    )
    LOGGER.info(msg)
    index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
    click.echo(f"[*] {msg}")

    # Don't put print statements under click.progressbar otherwise the progress bar will be interrupted
    with click.progressbar(users, label="[*] Checking users for admin roles") as users:
        for user in users:
            assigned_roles, error = list_assigned_roles(ctx, user.get("id"), object_type="user", mute=True)
            # Stop trying to check roles if the current API token doesn't have that permission
            if error:
                return

            if assigned_roles:
                admin_user = {"user": user, "roles": assigned_roles}
                admin_users.append(admin_user)

                for role in assigned_roles:
                    if role["type"] in ctx.obj.admin_roles:
                        msg = f'User ID {user["id"]} has admin role {role["type"]} assigned'
                        LOGGER.info(msg)
                        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)

            # Sleep for 1s to avoid exceeding API rate limits
            time.sleep(1)

    if admin_users:
        for user in admin_users:
            print_role_info(user["user"]["id"], user["roles"], object_type="user")

        msg = f"Found {len(admin_users)} users with admin roles assigned"
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.secho(f"[*] {msg}", fg="green")

        file_path = f"{ctx.obj.data_dir}/{ctx.obj.profile_id}_admin_users"

        if click.confirm("[*] Do you want to save harvested admin user information to a file?", default=True):
            write_json_file(file_path, admin_users)

    else:
        msg = "No users found with admin roles assigned"
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.echo(f"[*] {msg}")

    return admin_users
예제 #10
0
def execute(ctx):
    """Execute this module with the configured options"""

    error = check_module_options(MODULE_OPTIONS)

    if error:
        return

    zone_id = MODULE_OPTIONS["id"]["value"]

    zone = get_zone_object(ctx, zone_id)

    if zone:
        if zone["status"] == "ACTIVE":
            click.echo("[*] Zone is ACTIVE")
            if click.confirm(
                    f'[*] Do you want to deactivate zone {zone_id} ({zone["name"]})?',
                    default=True):
                msg = f'Attempting to deactivate zone {zone_id} ({zone["name"]})'
                LOGGER.info(msg)
                index_event(ctx.obj.es,
                            module=__name__,
                            event_type="INFO",
                            event=msg)
                click.echo(f"[*] {msg}")
                set_zone_state(ctx, zone["id"], operation="DEACTIVATE")

        elif zone["status"] == "INACTIVE":
            click.echo("[*] Zone is INACTIVE")
            if click.confirm(
                    f'[*] Do you want to activate zone {zone_id} ({zone["name"]})?',
                    default=True):
                msg = f'Attempting to activate zone {zone_id} ({zone["name"]})'
                LOGGER.info(msg)
                index_event(ctx.obj.es,
                            module=__name__,
                            event_type="INFO",
                            event=msg)
                click.echo(f"[*] {msg}")
                set_zone_state(ctx, zone["id"], operation="ACTIVATE")

        else:
            click.echo(f'[*] Policy status is {zone["status"]}')
예제 #11
0
파일: modify_zone.py 프로젝트: dd-m/dorothy
def rename_zone(ctx, zone, original_name, new_name):
    """Update an existing network zone with a new name"""

    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": f"SSWS {ctx.obj.api_token}",
    }

    params = {}
    # Values for "type" and "name" and "gateways" OR "proxies are required when updating a network zone object
    payload = {
        "type": zone["type"],
        "name": new_name,
        "gateways": zone.get("gateways"),
        "proxies": zone.get("proxies")
    }

    url = f'{ctx.obj.base_url}/zones/{zone["id"]}'

    try:
        msg = f'Attempting to rename network zone "{original_name}" ({zone["id"]}) to "{new_name}"'
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.echo(f"[*] {msg}")
        response = ctx.obj.session.put(url,
                                       headers=headers,
                                       params=params,
                                       json=payload,
                                       timeout=7)
    except Exception as e:
        LOGGER.error(e, exc_info=True)
        index_event(ctx.obj.es, module=__name__, event_type="ERROR", event=e)
        click.secho(f"[!] {URL_OR_API_TOKEN_ERROR}", fg="red")
        response = None

    if response.ok:
        msg = f'Network zone "{original_name}" ({zone["id"]}) changed to "{new_name}"'
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.secho(f"[*] {msg}", fg="green")
        get_zone_object(ctx, zone["id"])
        time.sleep(1)

    else:
        msg = (
            f'Error modifying network zone {zone["id"]}\n'
            f"    Response Code: {response.status_code} | Response Reason: {response.reason}\n"
            f'    Error Code: {response.json().get("errorCode")} | Error Summary: {response.json().get("errorSummary")}'
        )
        LOGGER.error(msg)
        index_event(ctx.obj.es, module=__name__, event_type="ERROR", event=msg)
        click.secho(f"[!] {msg}", fg="red")
예제 #12
0
def rename_policy_rule(ctx, policy_id, rule, original_name, new_name):
    """Update an existing policy rule with a new name"""

    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": f"SSWS {ctx.obj.api_token}",
    }

    params = {}
    payload = {
        # Values for "type", "name", and "actions" are required when updating a policy rule
        "type": rule["type"],
        "name": new_name,
        "actions": rule["actions"],
    }

    url = f'{ctx.obj.base_url}/policies/{policy_id}/rules/{rule["id"]}'

    try:
        msg = f'Attempting to rename rule "{original_name}" ({rule["id"]}) to "{new_name}" in policy {policy_id}'
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.echo(f"[*] {msg}")
        response = ctx.obj.session.put(url,
                                       headers=headers,
                                       params=params,
                                       json=payload,
                                       timeout=7)
    except Exception as e:
        LOGGER.error(e, exc_info=True)
        index_event(ctx.obj.es, module=__name__, event_type="ERROR", event=e)
        click.secho(f"[!] {URL_OR_API_TOKEN_ERROR}", fg="red")
        response = None

    if response.ok:
        msg = f'Rule "{original_name}" ({rule["id"]}) changed to "{new_name}" in policy {policy_id}'
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.secho(f"[*] {msg}", fg="green")
        get_policy_object(ctx, policy_id)
        time.sleep(1)

    else:
        msg = (
            f'Error modifying rule "{original_name}" {rule["id"]} in policy {policy_id}\n'
            f"    Response Code: {response.status_code} | Response Reason: {response.reason}\n"
            f'    Error Code: {response.json().get("errorCode")} | Error Summary: {response.json().get("errorSummary")}'
        )
        LOGGER.error(msg)
        index_event(ctx.obj.es, module=__name__, event_type="ERROR", event=msg)
        click.secho(f"[!] {msg}", fg="red")
예제 #13
0
def execute(ctx):
    """Execute this module with the configured options"""

    error = check_module_options(MODULE_OPTIONS)

    if error:
        return

    user_id = MODULE_OPTIONS["id"]["value"]

    click.echo("""[*] Attempting to retrieve user's current state""")
    error = get_user_object(ctx, user_id)
    if error:
        return

    click.echo("[*] Available lifecycle operations:")
    for index, operation in enumerate(LIFECYCLE_OPERATIONS):
        click.echo(
            f'{index + 1}. {operation["operation"]} - {operation["description"]}'
        )

    while True:
        choice = click.prompt(
            "[*] Which state do you want to transition the user to?", type=int)

        if (choice > 0) and (choice <= len(LIFECYCLE_OPERATIONS)):
            lifecycle_operation = LIFECYCLE_OPERATIONS[choice - 1]["operation"]

            msg = f"Attempting to {lifecycle_operation} user ID {user_id}"
            LOGGER.info(msg)
            index_event(ctx.obj.es,
                        module=__name__,
                        event_type="INFO",
                        event=msg)
            click.echo(f"[*] {msg}")

            execute_lifecycle_operation(ctx, user_id, lifecycle_operation)

            return
        else:
            click.secho("[!] Invalid option selected", fg="red")
예제 #14
0
def execute(ctx):
    """Execute this module with the configured options"""

    error = check_module_options(MODULE_OPTIONS)

    if error:
        return

    msg = f'Attempting to get policy object for policy ID {MODULE_OPTIONS["id"]["value"]}'
    LOGGER.info(msg)
    index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
    click.echo(f"[*] {msg}")

    policy = get_policy_object(ctx, MODULE_OPTIONS["id"]["value"], rules=True)

    if policy:
        print_policy_object(policy)
        if click.confirm(
                f"[*] Do you want to save policy {policy['id']} ({policy['name']}) to a file?",
                default=True):
            file_path = f'{ctx.obj.data_dir}/{ctx.obj.profile_id}_policy_{policy["id"]}'
            write_json_file(file_path, policy)
예제 #15
0
def execute(ctx):
    """Execute this module with the configured options"""

    error = check_module_options(MODULE_OPTIONS)

    if error:
        return

    admin_roles = ctx.obj.admin_roles
    user_id = MODULE_OPTIONS["id"]["value"]

    click.echo("[*] Available admin roles:")
    for index, role in enumerate(admin_roles):
        click.echo(f"{index + 1}. {role}")

    while True:
        choice = click.prompt(
            "[*] Which admin role do you want to assign to the user?",
            type=int)

        if (choice > 0) and (choice <= len(admin_roles)):
            role_type = admin_roles[choice - 1]

            msg = f"Attempting to assign admin role, {role_type} to user ID, {user_id}"
            LOGGER.info(msg)
            index_event(ctx.obj.es,
                        module=__name__,
                        event_type="INFO",
                        event=msg)
            click.echo(f"[*] {msg}")

            assign_admin_role(ctx, user_id, role_type, target="user")

            return

        else:
            click.secho("[!] Invalid option selected", fg="red")
예제 #16
0
파일: create_user.py 프로젝트: dd-m/dorothy
def execute(ctx):
    """Execute this module with the configured options"""

    error = check_module_options(MODULE_OPTIONS)

    if error:
        return

    password = click.prompt(
        "[*] Enter a password for the new user. The input for this value is hidden",
        hide_input=True)

    msg = f'Attempting to create new Okta user {MODULE_OPTIONS["login"]["value"]}'
    LOGGER.info(msg)
    index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
    click.echo(f"[*] {msg}")

    url = f"{ctx.obj.base_url}/users"

    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": f"SSWS {ctx.obj.api_token}",
    }
    # Activate the new user when it's created
    params = {"activate": "true"}
    payload = {
        "profile": {
            "firstName": MODULE_OPTIONS["first_name"]["value"],
            "lastName": MODULE_OPTIONS["last_name"]["value"],
            "email": MODULE_OPTIONS["email"]["value"],
            "login": MODULE_OPTIONS["login"]["value"],
        },
        "groupIds": MODULE_OPTIONS["group_ids"]["value"],
        "credentials": {
            "password": {
                "value": password
            }
        },
    }

    try:
        response = ctx.obj.session.post(url,
                                        headers=headers,
                                        params=params,
                                        json=payload,
                                        timeout=7)
    except Exception as e:
        LOGGER.error(e, exc_info=True)
        index_event(ctx.obj.es, module=__name__, event_type="ERROR", event=e)
        click.secho(f"[!] {URL_OR_API_TOKEN_ERROR}", fg="red")
        response = None

    if response.ok:
        msg = f'Created new Okta user {MODULE_OPTIONS["login"]["value"]}'
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.secho(f"[*] {msg}", fg="green")
    else:
        msg = (
            f"Error creating new Okta user\n"
            f"    Response Code: {response.status_code} | Response Reason: {response.reason}\n"
            f'    Error Code: {response.json().get("errorCode")} | Error Summary: {response.json().get("errorSummary")}'
        )
        LOGGER.error(msg)
        index_event(ctx.obj.es, module=__name__, event_type="ERROR", event=msg)
        click.secho(f"[!] {msg}", fg="red")
        click.echo(
            'Did you try and add the new user to a built-in group? E.g. "Everyone"'
        )

        return
예제 #17
0
def execute(ctx):
    """Execute this module with the configured options"""

    error = check_module_options(MODULE_OPTIONS)

    if error:
        return

    user_id = MODULE_OPTIONS["id"]["value"]

    enrolled_factors, error = list_enrolled_factors(ctx, user_id)

    if error:
        return

    if not enrolled_factors:
        msg = f"No enrolled MFA factors found for user {user_id}"
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.echo(f"[*] {msg}")
        return

    else:
        msg = f"Found {len(enrolled_factors)} enrolled MFA factors for user {user_id}"
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.secho(f"[*] {msg}", fg="green")

        # Print the user's enrolled factors
        factors = []
        for index, factor in enumerate(enrolled_factors):
            factors.append((
                index + 1,
                factor["id"],
                factor.get("factorType", "-"),
                factor.get("provider", "-"),
                factor.get("vendorName", "-"),
                factor.get("status", "-"),
            ))

        headers = [
            "#", "Factor ID", "Type", "Provider", "Vendor Name", "Status"
        ]
        click.echo(tabulate(factors, headers=headers, tablefmt="pretty"))

        # Prompt to delete a factor
        while True:
            if click.confirm(
                    "[*] Do you want to delete a MFA factor from the user's profile?",
                    default=True):
                choice = click.prompt(
                    "[*] Enter the number (#) of the MFA factor to delete",
                    type=int)

                if (choice > 0) and (choice <= len(factors)):
                    factor_id = enrolled_factors[choice - 1]["id"]
                    reset_factor(ctx, user_id, factor_id)
                    return
                else:
                    click.secho("[!] Invalid choice", fg="red")
                    return
            else:
                return
예제 #18
0
def execute(ctx):
    """Execute this module with the configured options"""

    """
    Can't I just make an API call to get all users that have a specific role assigned? Good question. There is no
    Okta API yet to get all users that have a specific role assigned to them. It is necessary to enumerate through
    all Okta users and then get the roles assigned to each user
    """

    options = (
        "[*] Available options\n"
        "[1] Load harvested users from json file and check their assigned roles for administrator permissions\n"
        "[2] Harvest all users and check their assigned roles for administrator permissions\n"
        "[0] Exit this menu\n"
        "[*] Choose from the above options"
    )

    while True:
        value = click.prompt(options, type=int)

        if value == 1:
            file_path = Path(
                click.prompt(
                    "[*] Enter full path of file containing harvested Okta users",
                )
            )

            if file_path.exists():
                msg = f"Attempting to check roles for users in file, {file_path}"
                LOGGER.info(msg)
                index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
                click.echo(f"[*] {msg}")
                users = load_json_file(file_path)
                check_assigned_roles(ctx, users)
                return

            else:
                msg = f"File not found, {file_path}"
                LOGGER.error(msg)
                index_event(ctx.obj.es, module=__name__, event_type="ERROR", event=msg)
                click.secho(f"[!] {msg}", fg="red")

        elif value == 2:
            if click.confirm(
                "[*] Do you want to attempt to harvest information for all users? This may take a while "
                "to avoid exceeding API rate limits",
                default=True,
            ):
                msg = "Attempting to harvest all Okta users"
                LOGGER.info(msg)
                index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
                click.echo(f"[*] {msg}")
                users = list_users(ctx)
                check_assigned_roles(ctx, users)
                return

        elif value == 0:
            return

        else:
            click.secho("[!] Invalid option selected", fg="red")
예제 #19
0
def execute(ctx):
    """Execute this module with the configured options"""

    options = (
        "[*] Available options\n"
        "[1] Load harvested groups from json file and check their assigned roles for administrator "
        "permissions\n"
        "[2] Harvest all groups and check their assigned roles for administrator permissions\n"
        "[0] Exit this menu\n"
        "[*] Choose from the above options")

    while True:
        value = click.prompt(options, type=int)

        if value == 1:
            file_path = Path(
                click.prompt(
                    "[*] Enter full path of file containing harvested Okta groups",
                ))

            if file_path.exists():
                msg = f"Attempting to check roles for groups in file, {file_path}"
                LOGGER.info(msg)
                index_event(ctx.obj.es,
                            module=__name__,
                            event_type="INFO",
                            event=msg)
                click.echo(f"[*] {msg}")
                groups = load_json_file(file_path)
                check_assigned_roles(ctx, groups)
                return

            else:
                msg = f"File not found, {file_path}"
                LOGGER.error(msg)
                index_event(ctx.obj.es,
                            module=__name__,
                            event_type="ERROR",
                            event=msg)
                click.secho(f"[!] {msg}", fg="red")

        elif value == 2:
            if click.confirm(
                    "[*] Do you want to attempt to harvest information for all groups?",
                    default=True):
                msg = "Attempting to harvest all Okta groups"
                LOGGER.info(msg)
                index_event(ctx.obj.es,
                            module=__name__,
                            event_type="INFO",
                            event=msg)
                click.echo(f"[*] {msg}")
                groups = list_groups(ctx)
                check_assigned_roles(ctx, groups)
                return

        elif value == 0:
            return

        else:
            click.secho("[!] Invalid option selected", fg="red")
예제 #20
0
def execute(ctx):
    """Execute this module with the configured options"""

    error = check_module_options(MODULE_OPTIONS)

    if error:
        return

    msg = f'Attempting to generate a one-time token to reset the password for user ID {MODULE_OPTIONS["id"]["value"]}'
    LOGGER.info(msg)
    index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
    click.echo(f"[*] {msg}")

    url = f'{ctx.obj.base_url}/users/{MODULE_OPTIONS["id"]["value"]}/lifecycle/reset_password'

    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": f"SSWS {ctx.obj.api_token}",
    }

    # Set sendEmail to False. The default value for sendEmail is True, which will send the one-time token to the
    # target user
    params = {"sendEmail": "False"}
    payload = {}

    try:
        response = ctx.obj.session.post(url,
                                        headers=headers,
                                        params=params,
                                        json=payload,
                                        timeout=7)
    except Exception as e:
        LOGGER.error(e, exc_info=True)
        index_event(ctx.obj.es, module=__name__, event_type="ERROR", event=e)
        click.secho(f"[!] {URL_OR_API_TOKEN_ERROR}", fg="red")
        response = None

    if response.ok:
        msg = f'One-time password reset token generated for user {MODULE_OPTIONS["id"]["value"]}'
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.secho(f"[*] {msg}", fg="green")
        click.echo(
            "[*] The user will have the status of RECOVERY and will not be able to login or initiate the "
            "forgot password flow until the password is reset")

        response = response.json()
        click.echo(f'Reset password URL: {response["resetPasswordUrl"]}')

    else:
        msg = (
            f"Error resetting password for user\n"
            f"    Response Code: {response.status_code} | Response Reason: {response.reason}\n"
            f'    Error Code: {response.json().get("errorCode")} | Error Summary: {response.json().get("errorSummary")}'
        )
        LOGGER.error(msg)
        index_event(ctx.obj.es, module=__name__, event_type="ERROR", event=msg)
        click.secho(f"[!] {msg}", fg="red")
        click.echo(
            "Check the status of the user. The user's status must be ACTIVE")

        return
예제 #21
0
def execute(ctx):
    """Execute this module with the configured options"""

    options = (
        "[*] Available options\n"
        "[1] Load harvested users from a json file and check their enrolled MFA factors\n"
        "[2] Harvest all users and check their enrolled MFA factors\n"
        "[0] Exit this menu\n"
        "[*] Choose from the above options")

    while True:
        value = click.prompt(options, type=int)

        if value == 1:
            file_path = Path(
                click.prompt(
                    "[*] Enter full path of file containing harvested Okta users",
                ))

            if file_path.exists():
                msg = f"Attempting to check MFA factors for users in file, {file_path}"
                LOGGER.info(msg)
                index_event(ctx.obj.es,
                            module=__name__,
                            event_type="INFO",
                            event=msg)
                click.echo(f"[*] {msg}")
                users = load_json_file(file_path)
                check_enrolled_factors(ctx, users)
                return

            else:
                msg = f"File not found, {file_path}"
                LOGGER.error(msg)
                index_event(ctx.obj.es,
                            module=__name__,
                            event_type="ERROR",
                            event=msg)
                click.secho(f"[!] {msg}", fg="red")

        elif value == 2:
            if click.confirm(
                    "[*] Do you want to attempt to harvest information for all users? This may take a while "
                    "to avoid exceeding API rate limits",
                    default=True,
            ):
                msg = "Attempting to harvest all Okta users"
                LOGGER.info(msg)
                index_event(ctx.obj.es,
                            module=__name__,
                            event_type="INFO",
                            event=msg)
                click.echo(f"[*] {msg}")
                users = list_users(ctx)
                check_enrolled_factors(ctx, users)
                return

        elif value == 0:
            return

        else:
            click.secho("[!] Invalid option selected", fg="red")
예제 #22
0
def check_enrolled_factors(ctx, users):
    """Check for users that have no MFA factors enrolled"""

    users_without_mfa = []

    msg = (
        f"Checking enrolled MFA factors for {len(users)} users. This may take a while to avoid exceeding API "
        f"rate limits")
    LOGGER.info(msg)
    index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
    click.echo(f"[*] {msg}")

    # Don't put print statements under click.progressbar otherwise the progress bar will be interrupted
    with click.progressbar(
            users,
            label="[*] Checking for users without MFA enrolled") as users:
        for user in users:
            factors, error = list_enrolled_factors(ctx,
                                                   user.get("id"),
                                                   mute=True)
            # Stop trying to check enrolled MFA factors if the current API token doesn't have that permission
            if error:
                return

            if not factors:
                users_without_mfa.append(user)

                msg = f'User {user["id"]} does not have any MFA factors enrolled'
                LOGGER.info(msg)
                index_event(ctx.obj.es,
                            module=__name__,
                            event_type="INFO",
                            event=msg)

            # Sleep for 1s to avoid exceeding API rate limits
            time.sleep(1)

    if users_without_mfa:
        msg = f"Found {len(users_without_mfa)} users without any MFA factors enrolled"
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.secho(f"[*] {msg}", fg="green")

        if click.confirm(
                "[*] Do you want to print information for users without MFA?",
                default=True):
            for user in users_without_mfa:
                print_user_info(user)

        if click.confirm(
                "[*] Do you want to save users without any MFA factors enrolled to a file?",
                default=True):
            file_path = f"{ctx.obj.data_dir}/{ctx.obj.profile_id}_users_without_mfa"
            write_json_file(file_path, users_without_mfa)

    else:
        msg = "No users found without any MFA factors enrolled"
        LOGGER.info(msg)
        index_event(ctx.obj.es, module=__name__, event_type="INFO", event=msg)
        click.echo(f"[*] {msg}")

    return users_without_mfa