PingOne

Integrates with the PingOne Management API to unlock, create, delete and update users.

Authentication & Identity Management · PingIdentity

Details

IDPingOne
ProviderThoma Bravo
CategoryAuthentication & Identity Management
From Version6.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix Cloud Runtime Security Cloud Posture Security XSIAM EDR Cortex Cloud

README

Integrates with the PingOne Management API to unlock, create, delete and update users.

Configure PingOne in Cortex

Parameter Description Required
Your PingOne Environment ID Environment ID is available under the PingOne Dashboard->Environment Properties. True
PingOne Region PingOne has 3 regions, US, EU and Asia. True
Client ID   True
Client Secret   True
Trust any certificate (not secure) Trust any certificate (not secure). False
Use system proxy settings Use system proxy settings. False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

pingone-unlock-user


Unlock a user’s account.

Base Command

pingone-unlock-user

Input

Argument Name Description Required
username Username to unlock. Required

Context Output

There is no context output for this command.

Command Example

!pingone-unlock-user username=emma.sharp

Human Readable Output

emma.sharp unlocked

pingone-deactivate-user


Deactivate a user’s account.

Base Command

pingone-deactivate-user

Input

Argument Name Description Required
username A username to deactivate. Required

Context Output

There is no context output for this command.

Command Example

!pingone-deactivate-user username=emma.sharp

Human Readable Output

User emma.sharp deactivated

pingone-activate-user


Activate a user’s account.

Base Command

pingone-activate-user

Input

Argument Name Description Required
username A username to activate. Required

Context Output

There is no context output for this command.

Command Example

!pingone-activate-user username=emma.sharp

Human Readable Output

emma.sharp is active now

pingone-set-password


Sets a user’s password. Further password details available here: https://apidocs.pingidentity.com/pingone/platform/v1/api/#put-update-password-self

Base Command

pingone-set-password

Input

Argument Name Description Required
username The username to change the password for. Required
password The new password. Required

Context Output

There is no context output for this command.

Command Example

!pingone-set-password username=emma.sharp password=OnePing123!

Human Readable Output

emma.sharp password was updated.

pingone-password-force-change


Force a user’s password change on next login.

Base Command

pingone-password-force-change

Input

Argument Name Description Required
username The username to force a password change for. Required

Context Output

There is no context output for this command.

Command Example

!pingone-password-force-change username=emma.sharp

Human Readable Output

emma.sharp password was set to force change.

pingone-read-password-state


Read a user’s password state.

Base Command

pingone-read-password-state

Input

Argument Name Description Required
username The username to read password state for. Required

Context Output

There is no context output for this command.

Command Example

!pingone-read-password-state username=emma.sharp

Human Readable Output

Password state for emma.sharp  
status MUST_CHANGE_PASSWORD

pingone-add-to-group


Add user to the group. One of the following has to be given username or userId and groupName or groupId.

Base Command

pingone-add-to-group

Input

Argument Name Description Required
username Username . Optional
groupName Group Name. Optional
groupId Group ID. Optional
userId User ID. Optional

Context Output

There is no context output for this command.

Command Example

!pingone-add-to-group username=richard.smith groupName=Sales

Human Readable Output

User: 9bc53389-f806-47b6-9480-ab1e3c6269b5 added to group: Sales successfully

pingone-remove-from-group


Remove user from the group. One of the following has to be given username or userId and groupName or groupId.

Base Command

pingone-remove-from-group

Input

Argument Name Description Required
username Username. Optional
groupName Group Name. Optional
userId User ID. Optional
groupId Group ID. Optional

Context Output

There is no context output for this command.

Command Example

!pingone-remove-from-group username=richard.smith groupName=Sales

Human Readable Output

User: 9bc53389-f806-47b6-9480-ab1e3c6269b5 was removed from group: Sales successfully

pingone-get-groups


Returns user’s group memberships.

Base Command

pingone-get-groups

Input

Argument Name Description Required
username Username. Required

Context Output

Path Type Description
PingOne.Account.Group string PingOne groups of the user.
PingOne.Account.ID string PingOne account ID.
PingOne.Account.Type string PingOne account type.
PingOne.Account.Group.ID string Group unique ID.
PingOne.Account.Group.Name string Group name.

Command Example

!pingone-get-groups username=emma.sharp

Context Example

{
    "Account": {
        "Group": [
            {
                "ID": "a5ec8d61-6291-4ebf-8630-135c2ecf1d10",
                "Name": "Marketing"
            },
            {
                "ID": "115458a4-5460-4514-802c-df153a6dcd47",
                "Name": "Standard"
            }
        ],
        "ID": "emma.sharp",
        "Type": "PingOne"
    }
}

Human Readable Output

PingOne groups for user: emma.sharp

Groups

ID Name
a5ec8d61-6291-4ebf-8630-135c2ecf1d10 Marketing
115458a4-5460-4514-802c-df153a6dcd47 Standard

pingone-get-user


Returns a PingOne user. One of the following has to be given username or userId.

Base Command

pingone-get-user

Input

Argument Name Description Required
username Username. Optional
userId User ID. Optional
detailed PingOne user details. Optional

Context Output

Path Type Description
PingOne.Account.ID string PingOne account ID.
PingOne.Account.Username string PingOne account username.
PingOne.Account.DisplayName string PingOne account display name.
PingOne.Account.Email string PingOne account email.
PingOne.Account.Enabled boolean PingOne account enabled status.
PingOne.Account.CreatedAt date PingOne account create date.
PingOne.Account.UpdatedAt date PingOne account updated date.

Command Example

!pingone-get-user username=emma.sharp

Context Example

{
    "Account": {
        "CreatedAt": "2021-09-03T18:04:03.916Z",
        "DisplayName": "Emma Testman",
        "Email": "emma.sharp@example.com",
        "Enabled": true,
        "ID": "a8890eb9-38ea-469a-bc00-b64be7903633",
        "UpdatedAt": "2021-09-23T17:18:11.747Z",
        "Username": "emma.sharp"
    }
}

Human Readable Output

User:emma.sharp

AccountStatus CreatedAt Email Enabled Environment First Name ID Last Name PopulationID UpdatedAt Username
OK 2021-09-03T18:04:03.916Z emma.sharp@example.com true b4f5e266-a946-4f77-9cc5-5dc91b046431 Emma a8890eb9-38ea-469a-bc00-b64be7903633 Sharp 4cd45bdb-0eb2-42fe-8475-4bcd908269f1 2021-09-23T17:18:11.747Z emma.sharp

pingone-create-user


Create a PingOne user.

Base Command

pingone-create-user

Input

Argument Name Description Required
username The username to create. Required
populationId PingOne population ID where the new user will be created. Required

Context Output

Path Type Description
PingOne.Account.ID string PingOne account ID.
PingOne.Account.Username string PingOne account username.
PingOne.Account.DisplayName string PingOne account display name.
PingOne.Account.Email string PingOne account email.
PingOne.Account.Enabled boolean PingOne account enabled status.
PingOne.Account.CreatedAt date PingOne account create date.
PingOne.Account.UpdatedAt date PingOne account updated date.

Command Example

!pingone-create-user username=richard.smith populationId=4cd45bdb-0eb2-42fe-8475-4bcd908269f1

Context Example

{
    "Account": {
        "CreatedAt": "2021-09-23T17:18:18.292Z",
        "DisplayName": null,
        "Email": null,
        "Enabled": true,
        "ID": "9bc53389-f806-47b6-9480-ab1e3c6269b5",
        "UpdatedAt": "2021-09-23T17:18:18.292Z",
        "Username": "richard.smith"
    }
}

Human Readable Output

PingOne user created: richard.smith

AccountStatus CreatedAt Email Enabled Environment First Name ID Last Name PopulationID UpdatedAt Username
OK 2021-09-23T17:18:18.292Z   true b4f5e266-a946-4f77-9cc5-5dc91b046431   9bc53389-f806-47b6-9480-ab1e3c6269b5   4cd45bdb-0eb2-42fe-8475-4bcd908269f1 2021-09-23T17:18:18.292Z richard.smith

pingone-update-user


Update a PingOne user.

Base Command

pingone-update-user

Input

Argument Name Description Required
username The username to be updated. Required
formatted Formatted PingOne name. Optional
given Given name. Optional
middle Middle name. Optional
family Family name. Optional
nickname Nickname. Optional
title Title. Optional
locale Locale. Optional
email Email. Optional
primaryPhone Primary phone number. Optional
mobilePhone Mobile phone number. Optional
streetAddress Street address. Optional
locality Locality. Optional
region Region. Optional
postalCode Zip code. Optional
countryCode Country code. Optional
Type Account type. Optional

Context Output

There is no context output for this command.

Command Example

!pingone-update-user username=richard.smith phoneNumber=604-998-7766

Human Readable Output

PingOne user updated: richard.smith

No entries.

pingone-delete-user


Delete a PingOne user. One of the following has to be given username or userId.

Base Command

pingone-delete-user

Input

Argument Name Description Required
username Username. Optional
userId User ID. Optional

Context Output

There is no context output for this command.

Command Example

!pingone-delete-user username=richard.smith

Human Readable Output

User: 9bc53389-f806-47b6-9480-ab1e3c6269b5 was Deleted successfully

Configuration parameters

  • environment_id — Your PingOne Environment ID (required)
  • region — PingOne Region (required)
  • credentials — Client ID (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (13)

  • pingone-activate-user

    Activate a user's account.

  • pingone-add-to-group

    Add user to the group. One of the following has to be given username or userId and groupName or groupId.

  • pingone-create-user

    Create a PingOne user.

  • pingone-deactivate-user

    Deactivate a user's account.

  • pingone-delete-user

    Delete a PingOne user. One of the following has to be given username or userId.

  • pingone-get-groups

    Returns user's group memberships.

  • pingone-get-user

    Returns a PingOne user. One of the following has to be given username or userId.

  • pingone-password-force-change

    Force a user's password change on next login.

  • pingone-read-password-state

    Read a user's password state.

  • pingone-remove-from-group

    Remove user from the group. One of the following has to be given username or userId and groupName or groupId.

  • pingone-set-password

    Sets a user's password. Further password details available here: https://apidocs.pingidentity.com/pingone/platform/v1/api/#put-update-password-self .

  • pingone-unlock-user

    Unlock a user's account.

  • pingone-update-user

    Update a PingOne user.

from PingOne import (
    Client,
    unlock_user_command,
    deactivate_user_command,
    activate_user_command,
    set_password_command,
    force_password_change_command,
    read_password_state_command,
    add_user_to_group_command,
    remove_from_group_command,
    get_groups_for_user_command,
    get_user_command,
    create_user_command,
    update_user_command,
    delete_user_command,
)
import pytest

TEST_USER_ID = "a8890eb9-38ea-469a-bc00-b64be7903633"
TEST_GROUP_ID = "8c33d93e-a614-457a-80ed-5e922fccd906"

user_data_by_username = {
    "_embedded": {"password": {"status": "MUST_CHANGE_PASSWORD"}},
    "id": "a8890eb9-38ea-469a-bc00-b64be7903633",
    "environment": {"id": "b4f5e266-a946-4f77-9cc5-5dc91b046431"},
    "account": {"canAuthenticate": True, "status": "OK"},
    "address": {
        "streetAddress": "9999 Marine Drive ",
        "locality": "calgary",
        "region": "BC",
        "postalCode": "12345",
        "countryCode": "CA",
    },
    "createdAt": "2021-09-03T18:04:03.916Z",
    "email": "emma.sharp@example.com",
    "enabled": True,
    "identityProvider": {"type": "PING_ONE"},
    "lifecycle": {"status": "ACCOUNT_OK"},
    "locale": "en-US",
    "mfaEnabled": False,
    "mobilePhone": "604-999-9999",
    "name": {"formatted": "Emma Testman", "given": "Emma", "family": "Sharp"},
    "nickname": "emma.sharp",
    "population": {"id": "4cd45bdb-0eb2-42fe-8475-4bcd908269f1"},
    "preferredLanguage": "en",
    "primaryPhone": "604-123-4567",
    "updatedAt": "2021-09-08T20:18:19.419Z",
    "username": "emma.sharp",
    "verifyStatus": "NOT_INITIATED",
}

user_data_by_id = {
    "_embedded": {"password": {"status": "MUST_CHANGE_PASSWORD"}},
    "id": "a8890eb9-38ea-469a-bc00-b64be7903633",
    "environment": {"id": "b4f5e266-a946-4f77-9cc5-5dc91b046431"},
    "account": {"canAuthenticate": True, "status": "OK"},
    "address": {
        "streetAddress": "9999 Marine Drive ",
        "locality": "calgary",
        "region": "BC",
        "postalCode": "12345",
        "countryCode": "CA",
    },
    "createdAt": "2021-09-03T18:04:03.916Z",
    "email": "emma.sharp@example.com",
    "enabled": True,
    "identityProvider": {"type": "PING_ONE"},
    "lifecycle": {"status": "ACCOUNT_OK"},
    "locale": "en-US",
    "mfaEnabled": False,
    "mobilePhone": "604-999-9999",
    "name": {"formatted": "Emma Testman", "given": "Emma", "family": "Sharp"},
    "nickname": "emma.sharp",
    "population": {"id": "4cd45bdb-0eb2-42fe-8475-4bcd908269f1"},
    "preferredLanguage": "en",
    "primaryPhone": "604-123-4567",
    "updatedAt": "2021-09-08T20:18:19.419Z",
    "username": "emma.sharp",
    "verifyStatus": "NOT_INITIATED",
}

create_user_response = {
    "id": "9e45580c-79f3-4499-83cc-006a20dcc50e",
    "environment": {"id": "b4f5e266-a946-4f77-9cc5-5dc91b046431"},
    "account": {"canAuthenticate": True, "status": "OK"},
    "createdAt": "2021-08-20T19:07:00.979Z",
    "enabled": True,
    "identityProvider": {"type": "PING_ONE"},
    "lifecycle": {"status": "ACCOUNT_OK"},
    "mfaEnabled": False,
    "population": {"id": "4cd45bdb-0eb2-42fe-8475-4bcd908269f1"},
    "updatedAt": "2021-08-20T19:07:00.979Z",
    "username": "marysample",
    "verifyStatus": "NOT_INITIATED",
}

single_group_data = {
    "_links": {
        "self": {
            "href": "https://api.pingone.com/v1/environments/b4f5e266-a946-4f77-9cc5-5dc91b046431/users/a8890eb9-38ea"
            "-469a-bc00-b64be7903633/memberOfGroups/8c33d93e-a614-457a-80ed-5e922fccd906"
        },
        "user": {
            "href": "https://api.pingone.com/v1/environments/b4f5e266-a946-4f77-9cc5-5dc91b046431/users/a8890eb9-38ea"
            "-469a-bc00-b64be7903633"
        },
        "environment": {"href": "https://api.pingone.com/v1/environments/b4f5e266-a946-4f77-9cc5-5dc91b046431"},
        "population": {
            "href": "https://api.pingone.com/v1/environments/b4f5e266-a946-4f77-9cc5-5dc91b046431/populations/4cd45bdb"
            "-0eb2-42fe-8475-4bcd908269f1"
        },
        "group": {
            "href": "https://api.pingone.com/v1/environments/b4f5e266-a946-4f77-9cc5-5dc91b046431/groups/8c33d93e-a614"
            "-457a-80ed-5e922fccd906"
        },
    },
    "id": "8c33d93e-a614-457a-80ed-5e922fccd906",
    "environment": {"id": "b4f5e266-a946-4f77-9cc5-5dc91b046431"},
    "name": "Sales",
    "population": {"id": "4cd45bdb-0eb2-42fe-8475-4bcd908269f1"},
    "type": "DIRECT",
}

group_data = {
    "_embedded": {
        "groupMemberships": [
            {
                "id": "dd95b574-cff5-485e-8460-c245ad8dab0f",
                "environment": {"id": "b4f5e266-a946-4f77-9cc5-5dc91b046431"},
                "name": "Sample Group",
                "type": "INDIRECT",
            },
            {
                "id": "8c33d93e-a614-457a-80ed-5e922fccd906",
                "environment": {"id": "b4f5e266-a946-4f77-9cc5-5dc91b046431"},
                "name": "Sales",
                "population": {"id": "4cd45bdb-0eb2-42fe-8475-4bcd908269f1"},
                "type": "DIRECT",
            },
        ]
    },
    "count": 2,
    "size": 2,
}

update_user_data = {
    "id": "3930766f-9e36-422d-ab4d-d8a2297f98f2",
    "environment": {"id": "b4f5e266-a946-4f77-9cc5-5dc91b046431"},
    "account": {"canAuthenticate": True, "status": "OK"},
    "createdAt": "2021-09-09T01:02:14.247Z",
    "enabled": True,
    "identityProvider": {"type": "PING_ONE"},
    "lifecycle": {"status": "ACCOUNT_OK"},
    "mfaEnabled": False,
    "nickname": "Freddie",
    "population": {"id": "4cd45bdb-0eb2-42fe-8475-4bcd908269f1"},
    "type": "",
    "updatedAt": "2021-09-09T01:05:28.536Z",
    "username": "fred.smith4",
    "verifyStatus": "NOT_INITIATED",
}


class ClientTestPing:
    """
    Test class to handle the client
    """

    def __init__(self, mocker):
        test_params = {
            "client_id": "12345",
            "client_secret": "clientsecret",
            "base_url": "https://api.pingone.com",
            "auth_url": "https://auth.pingone.com",
        }

        testing_auth_header = {"Authorization": "Bearer ACCESS_TOKEN"}
        mocker.patch.object(Client, "_request_token", return_value=testing_auth_header)

        self.client = Client(base_url="https://api.pingone.com", verify=False, proxy=False, auth_params=test_params)


@pytest.mark.parametrize("args, expected_context, expected_readable", [({"username": "emma.sharp"}, {}, "emma.sharp")])
def test_unlock_user(mocker, args, expected_context, expected_readable):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "get_user_id", return_value=TEST_USER_ID)
    mocker.patch.object(client, "unlock_user", return_value=user_data_by_id)

    readable, outputs, _ = unlock_user_command(client, args)
    assert outputs == expected_context
    assert expected_readable in readable


@pytest.mark.parametrize("args, expected_context, expected_readable", [({"username": "emma.sharp"}, {}, "emma.sharp")])
def test_deactivate_user(mocker, args, expected_context, expected_readable):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "get_user_id", return_value=TEST_USER_ID)
    mocker.patch.object(client, "deactivate_user", return_value=user_data_by_id)

    readable, outputs, _ = deactivate_user_command(client, args)
    assert outputs == expected_context
    assert expected_readable in readable


@pytest.mark.parametrize("args, expected_context, expected_readable", [({"username": "emma.sharp"}, {}, "emma.sharp")])
def test_activate_user(mocker, args, expected_context, expected_readable):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "get_user_id", return_value=TEST_USER_ID)
    mocker.patch.object(client, "activate_user", return_value=user_data_by_id)

    readable, outputs, _ = activate_user_command(client, args)
    assert outputs == expected_context
    assert expected_readable in readable


@pytest.mark.parametrize(
    "args, expected_context, expected_readable", [({"username": "emma.sharp", "password": "2FederateM0re"}, {}, "emma.sharp")]
)
def test_set_password(mocker, args, expected_context, expected_readable):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "get_user_id", return_value=TEST_USER_ID)
    mocker.patch.object(client, "set_password", return_value=user_data_by_id)

    readable, outputs, _ = set_password_command(client, args)
    assert outputs == expected_context
    assert expected_readable in readable


@pytest.mark.parametrize("args, expected_context, expected_readable", [({"username": "emma.sharp"}, {}, "emma.sharp")])
def test_force_password_change(mocker, args, expected_context, expected_readable):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "get_user_id", return_value=TEST_USER_ID)
    mocker.patch.object(client, "force_password_change", return_value=None)

    readable, outputs, _ = force_password_change_command(client, args)
    assert outputs == expected_context
    assert expected_readable in readable


@pytest.mark.parametrize("args, expected_context, expected_readable", [({"username": "emma.sharp"}, {}, "Password state")])
def test_read_password_state(mocker, args, expected_context, expected_readable):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "get_user_id", return_value=TEST_USER_ID)
    mocker.patch.object(client, "read_password_state", return_value={"status": "MUST_CHANGE_PASSWORD"})

    readable, outputs, _ = read_password_state_command(client, args)
    assert outputs == expected_context
    assert expected_readable in readable


@pytest.mark.parametrize(
    "args, expected_context, expected_readable",
    [
        ({"username": "emma.sharp", "groupName": "Sales"}, {}, "Sales"),
        (
            {"username": "emma.sharp", "groupId": "8c33d93e-a614-457a-80ed-5e922fccd906"},
            {},
            "8c33d93e-a614-457a-80ed-5e922fccd906",
        ),
        (
            {"userId": "a8890eb9-38ea-469a-bc00-b64be7903633", "groupId": "8c33d93e-a614-457a-80ed-5e922fccd906"},
            {},
            "a8890eb9-38ea-469a-bc00-b64be7903633",
        ),
    ],
)
def test_add_user_to_group(mocker, args, expected_context, expected_readable):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "get_user_id", return_value=TEST_USER_ID)
    mocker.patch.object(client, "get_group_id", return_value=TEST_GROUP_ID)
    mocker.patch.object(client, "add_user_to_group", return_value=single_group_data)

    readable, outputs, _ = add_user_to_group_command(client, args)

    assert outputs == expected_context
    assert expected_readable in readable


@pytest.mark.parametrize(
    "args, expected_context, expected_readable", [({"username": "emma.sharp", "groupName": "Sales"}, {}, "")]
)
def test_remove_user_to_group(mocker, args, expected_context, expected_readable):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "get_user_id", return_value=TEST_USER_ID)
    mocker.patch.object(client, "get_group_id", return_value=TEST_GROUP_ID)
    mocker.patch.object(client, "remove_user_from_group", return_value=single_group_data)

    readable, outputs, _ = remove_from_group_command(client, args)

    assert outputs == expected_context
    assert expected_readable in readable


@pytest.mark.parametrize(
    "args, expected_context, expected_readable",
    [
        (
            {"username": "emma.sharp"},
            {
                "ID": "a8890eb9-38ea-469a-bc00-b64be7903633",
                "Username": "emma.sharp",
                "Email": "emma.sharp@example.com",
                "DisplayName": "Emma Testman",
                "Enabled": True,
                "CreatedAt": "2021-09-03T18:04:03.916Z",
                "UpdatedAt": "2021-09-08T20:18:19.419Z",
            },
            "emma.sharp@example.com",
        ),
        (
            {"userId": "a8890eb9-38ea-469a-bc00-b64be7903633"},
            {
                "ID": "a8890eb9-38ea-469a-bc00-b64be7903633",
                "Username": "emma.sharp",
                "Email": "emma.sharp@example.com",
                "DisplayName": "Emma Testman",
                "Enabled": True,
                "CreatedAt": "2021-09-03T18:04:03.916Z",
                "UpdatedAt": "2021-09-08T20:18:19.419Z",
            },
            "emma.sharp@example.com",
        ),
    ],
)
def test_get_user_cmd(mocker, args, expected_context, expected_readable):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "get_user_by_id", return_value=user_data_by_id)
    mocker.patch.object(client, "get_user_by_username", return_value=user_data_by_username)
    readable, outputs, _ = get_user_command(client, args)
    assert outputs.get("PingOne.Account(val.ID && val.ID === obj.ID)")[0] == expected_context
    assert expected_readable in readable


@pytest.mark.parametrize("args", [{"username": "emma.sharp"}])
def test_get_groups_for_user_command(mocker, args):
    client = ClientTestPing(mocker).client

    expected_context = [{"ID": "8c33d93e-a614-457a-80ed-5e922fccd906", "Name": "Sales"}]

    mocker.patch.object(client, "get_user_id", return_value=TEST_USER_ID)
    mocker.patch.object(client, "get_groups_for_user", return_value=group_data)

    _, outputs, _ = get_groups_for_user_command(client, args)

    assert outputs.get("PingOne.Account(val.ID && val.ID === obj.ID)").get("Group") == expected_context
    assert outputs.get("PingOne.Account(val.ID && val.ID === obj.ID)").get("ID") == "emma.sharp"


@pytest.mark.parametrize("args", [({"username": "marysample", "populationId": "4cd45bdb-0eb2-42fe-8475-4bcd908269f1"})])
def test_create_user_command(mocker, args):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "create_user", return_value=create_user_response)
    readable, outputs, _ = create_user_command(client, args)

    assert "9e45580c-79f3-4499-83cc-006a20dcc50e" in readable
    assert outputs.get("PingOne.Account(val.ID && val.ID === obj.ID)").get("Enabled")


@pytest.mark.parametrize(
    "args, expected_context, expected_readable",
    [
        (
            {"username": "fred.smith4", "populationId": "4cd45bdb-0eb2-42fe-8475-4bcd908269f1", "nickname": "Freddie"},
            {
                "id": "3930766f-9e36-422d-ab4d-d8a2297f98f2",
                "environment": {"id": "b4f5e266-a946-4f77-9cc5-5dc91b046431"},
                "account": {"canAuthenticate": True, "status": "OK"},
                "createdAt": "2021-09-09T01:02:14.247Z",
                "enabled": True,
                "identityProvider": {"type": "PING_ONE"},
                "lifecycle": {"status": "ACCOUNT_OK"},
                "mfaEnabled": False,
                "nickname": "Freddie",
                "population": {"id": "4cd45bdb-0eb2-42fe-8475-4bcd908269f1"},
                "type": "",
                "updatedAt": "2021-09-09T01:05:28.536Z",
                "username": "fred.smith4",
                "verifyStatus": "NOT_INITIATED",
            },
            "fred.smith4",
        )
    ],
)
def test_update_user_command(mocker, args, expected_context, expected_readable):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "get_user_id", return_value="3930766f-9e36-422d-ab4d-d8a2297f98f2")
    mocker.patch.object(client, "update_user", return_value=update_user_data)
    readable, outputs, raw_response = update_user_command(client, args)

    assert expected_readable in readable
    assert raw_response == expected_context


@pytest.mark.parametrize(
    "args, expected_context, expected_readable",
    [({"userId": "3930766f-9e36-422d-ab4d-d8a2297f98f2"}, {}, "3930766f-9e36-422d-ab4d-d8a2297f98f2")],
)
def test_delete_user(mocker, args, expected_context, expected_readable):
    client = ClientTestPing(mocker).client

    mocker.patch.object(client, "get_user_by_username", return_value="3930766f-9e36-422d-ab4d-d8a2297f98f2")
    mocker.patch.object(client, "delete_user", return_value=None)

    readable, _, _ = delete_user_command(client, args)
    assert "3930766f-9e36-422d-ab4d-d8a2297f98f2" in readable