SingleConnect

Single Connect is a PAM product that enables enterprises to remove static passwords stored in applications by instead keeping passwords in a secure password vault. Single Connect provides a token-based authentication for 3rd party applications when accessing the password vault. This authentication process verifies the application identity and gives secure access to the password associated with that identity.

Authentication & Identity Management · Single Connect

Details

IDSingleConnect
ProviderVectra AI
CategoryAuthentication & Identity Management
From Version6.2.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Single Connect is a PAM product that enables enterprises to remove static passwords stored in applications by instead keeping passwords in a secure password vault. Single Connect provides a token-based authentication for 3rd party applications when accessing the password vault. This authentication process verifies the application identity and gives secure access to the password associated with that identity.

What does this pack do?

This integration provides the following commands for Shared Account Password Management (SAPM):

  • List all SAPM accounts (single-connect-sapm-account-list-all)
  • Show password for an SAPM account (single-connect-sapm-account-show-password)
  • List SAPM accounts of a specific device (single-connect-device-list-sapm-accounts)
  • Search SAPM accounts by secret name (single-connect-sapm-account-search-with-secret-name)

Configuration parameters

  • url — Your server URL (required)
  • credentials — Credentials (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (4)

  • single-connect-device-list-sapm-accounts

    List all the SAPM accounts on a specific device.

  • single-connect-sapm-account-list-all

    List all the accounts in the SAPM.

  • single-connect-sapm-account-search-with-secret-name

    List the accounts with given secretName.

  • single-connect-sapm-account-show-password

    Get the password for a specific SAPM account.

import pytest
from CommonServerPython import CommandResults, DemistoException
from pytest import raises  # noqa: PT013
from SingleConnect import (
    Client,
    get_sapm_user_info_command,
    list_all_sapm_accounts_command,
    search_sapm_with_secret_name_command,
    show_password_command,
)
from test_data.http_responses import (
    EMPTY_SEARCH_SAPM_ACCOUNTS_RESPONSE,
    ERROR_MESSAGE_RESPONSE,
    GET_SAPM_USER_INFO_RESPONSE,
    SEARCH_SAPM_ACCOUNTS_RESPONSE,
    SHOW_PASSWORD_RESPONSE,
)

UNEXPECTED_RESPONSE_FORMAT = "Unexpected response format"

ERROR_SINGLE_CONNECT = "Error in Single Connect API call"

ARGS_SEARCH_SAPM_WITH_SECRET_NAME = {"secret_name": "account7"}

ARGS_GET_SAPM_USER_INFO = {"device_ip": "123141"}

ARGS_SHOW_PASSWORD = {"password_expiration_in_minute": 30, "sapm_db_id": 642365, "comment": "reason for password request"}

EMPTY_DICT = {}

EMPTY_LIST = []

AUTHENTICATION_ERROR_RESPONSE = "Error in API call [401]"

AUTHENTICATION_ERROR_MESSAGE = "Authentication Error: Make sure username and password are correctly set"

HEADER_XSRF_TOKEN = {"XSRF-TOKEN": "4b6794ebc982f8aa3786d745555d8dc3"}


@pytest.mark.parametrize(
    "search_command, args, invalid_response_type",
    [
        (list_all_sapm_accounts_command, {}, EMPTY_DICT),
        (search_sapm_with_secret_name_command, ARGS_SEARCH_SAPM_WITH_SECRET_NAME, EMPTY_DICT),
    ],
)
def test_search_sapm_account_commands(search_command, args, invalid_response_type, mocker):
    mocker.patch.object(Client, "_generate_token")

    mocker.patch.object(
        Client,
        "_http_request",
        side_effect=[
            SEARCH_SAPM_ACCOUNTS_RESPONSE,
            EMPTY_SEARCH_SAPM_ACCOUNTS_RESPONSE,
            invalid_response_type,
            ERROR_MESSAGE_RESPONSE,
        ],
    )

    client = Client(base_url="https://localhost", username="admin", password="admin", use_ssl=False, proxy=False)

    command_output = search_command(client, **args)
    assert type(command_output) is CommandResults
    assert command_output.outputs == SEARCH_SAPM_ACCOUNTS_RESPONSE.get("searchResults")

    command_output_empty_body = search_command(client, **args)
    assert type(command_output_empty_body) is CommandResults
    assert command_output_empty_body.outputs == EMPTY_SEARCH_SAPM_ACCOUNTS_RESPONSE.get("searchResults")

    with raises(Exception, match=UNEXPECTED_RESPONSE_FORMAT):
        search_command(client, **args)
    with raises(Exception, match=ERROR_SINGLE_CONNECT):
        search_command(client, **args)


@pytest.mark.parametrize(
    "command, args, http_response, invalid_response_type",
    [
        (get_sapm_user_info_command, ARGS_GET_SAPM_USER_INFO, GET_SAPM_USER_INFO_RESPONSE, EMPTY_DICT),
        (show_password_command, ARGS_SHOW_PASSWORD, SHOW_PASSWORD_RESPONSE, EMPTY_LIST),
    ],
)
def test_single_connect_commands(command, args, http_response, invalid_response_type, mocker):
    mocker.patch.object(Client, "_generate_token")

    mocker.patch.object(Client, "_http_request", side_effect=[http_response, invalid_response_type, ERROR_MESSAGE_RESPONSE])

    client = Client(base_url="https://localhost", username="admin", password="admin", use_ssl=False, proxy=False)

    command_output = command(client, **args)
    assert type(command_output) is CommandResults
    assert command_output.outputs == http_response

    with raises(Exception, match=UNEXPECTED_RESPONSE_FORMAT):
        command(client, **args)
    with raises(Exception, match=ERROR_SINGLE_CONNECT):
        command(client, **args)


def test_generate_token_authentication_fail(mocker):
    mocker.patch.object(Client, "_http_request", side_effect=[DemistoException(AUTHENTICATION_ERROR_RESPONSE)])

    with raises(DemistoException, match=AUTHENTICATION_ERROR_MESSAGE):
        Client(base_url="https://localhost", username="admin", password="admin", use_ssl=False, proxy=False)


def test_generate_token_success(requests_mock):
    requests_mock.post("https://localhost/aioc-rest-web/rest/login", headers=HEADER_XSRF_TOKEN)
    client = Client(base_url="https://localhost", username="admin", password="admin", use_ssl=False, proxy=False)
    assert HEADER_XSRF_TOKEN.get("XSRF-TOKEN") == client._token