From 2b245b34cba7045b53c015601a1eb86d95e364db Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 14 Jul 2022 13:10:24 -0400 Subject: [PATCH] Island: Simplify Credentials Storing a sequence of identities and secrets in Credentials objects added a lot of complication. From now on, a Credentials object consists of one identity and one secret. See #2072. --- monkey/common/credentials/credentials.py | 40 ++------- .../data_for_tests/propagation_credentials.py | 21 +---- .../common/credentials/test_credentials.py | 89 +++++++++++-------- 3 files changed, 67 insertions(+), 83 deletions(-) diff --git a/monkey/common/credentials/credentials.py b/monkey/common/credentials/credentials.py index 4916a44e4..5483e9ff8 100644 --- a/monkey/common/credentials/credentials.py +++ b/monkey/common/credentials/credentials.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any, Mapping, MutableMapping, Sequence, Tuple +from typing import Any, Mapping, MutableMapping, Sequence from marshmallow import Schema, fields, post_load, pre_dump from marshmallow.exceptions import MarshmallowError @@ -42,27 +42,15 @@ CREDENTIAL_COMPONENT_TYPE_TO_CLASS_SCHEMA = { class CredentialsSchema(Schema): - # Use fields.List instead of fields.Tuple because marshmallow requires fields.Tuple to have a - # fixed length. - identities = fields.List(fields.Mapping()) - secrets = fields.List(fields.Mapping()) + identity = fields.Mapping() + secret = fields.Mapping() @post_load def _make_credentials( self, data: MutableMapping, **kwargs: Mapping[str, Any] ) -> Mapping[str, Sequence[Mapping[str, Any]]]: - data["identities"] = tuple( - [ - CredentialsSchema._build_credential_component(component) - for component in data["identities"] - ] - ) - data["secrets"] = tuple( - [ - CredentialsSchema._build_credential_component(component) - for component in data["secrets"] - ] - ) + data["identity"] = CredentialsSchema._build_credential_component(data["identity"]) + data["secret"] = CredentialsSchema._build_credential_component(data["secret"]) return data @@ -89,18 +77,8 @@ class CredentialsSchema(Schema): ) -> Mapping[str, Sequence[Mapping[str, Any]]]: data = {} - data["identities"] = tuple( - [ - CredentialsSchema._serialize_credential_component(component) - for component in credentials.identities - ] - ) - data["secrets"] = tuple( - [ - CredentialsSchema._serialize_credential_component(component) - for component in credentials.secrets - ] - ) + data["identity"] = CredentialsSchema._serialize_credential_component(credentials.identity) + data["secret"] = CredentialsSchema._serialize_credential_component(credentials.secret) return data @@ -117,8 +95,8 @@ class CredentialsSchema(Schema): @dataclass(frozen=True) class Credentials(IJSONSerializable): - identities: Tuple[ICredentialComponent] - secrets: Tuple[ICredentialComponent] + identity: ICredentialComponent + secret: ICredentialComponent @staticmethod def from_mapping(credentials: Mapping) -> Credentials: diff --git a/monkey/tests/data_for_tests/propagation_credentials.py b/monkey/tests/data_for_tests/propagation_credentials.py index df6c213cf..47862b2bc 100644 --- a/monkey/tests/data_for_tests/propagation_credentials.py +++ b/monkey/tests/data_for_tests/propagation_credentials.py @@ -6,21 +6,8 @@ nt_hash = "C1C58F96CDF212B50837BC11A00BE47C" lm_hash = "299BD128C1101FD6299BD128C1101FD6" password_1 = "trytostealthis" password_2 = "password" -password_3 = "12345678" -PROPAGATION_CREDENTIALS_1 = Credentials( - identities=(Username(username),), - secrets=(NTHash(nt_hash), LMHash(lm_hash), Password(password_1)), -) -PROPAGATION_CREDENTIALS_2 = Credentials( - identities=(Username(username), Username(special_username)), - secrets=(Password(password_1), Password(password_2), Password(password_3)), -) -PROPAGATION_CREDENTIALS_3 = Credentials( - identities=(Username(username),), - secrets=(Password(password_1),), -) -PROPAGATION_CREDENTIALS_4 = Credentials( - identities=(Username(username),), - secrets=(Password(password_2),), -) +PROPAGATION_CREDENTIALS_1 = Credentials(identity=Username(username), secret=Password(password_1)) +PROPAGATION_CREDENTIALS_2 = Credentials(identity=Username(special_username), secret=LMHash(lm_hash)) +PROPAGATION_CREDENTIALS_3 = Credentials(identity=Username(username), secret=NTHash(nt_hash)) +PROPAGATION_CREDENTIALS_4 = Credentials(identity=Username(username), secret=Password(password_2)) diff --git a/monkey/tests/unit_tests/common/credentials/test_credentials.py b/monkey/tests/unit_tests/common/credentials/test_credentials.py index 0f5fc519a..cb5fb76fc 100644 --- a/monkey/tests/unit_tests/common/credentials/test_credentials.py +++ b/monkey/tests/unit_tests/common/credentials/test_credentials.py @@ -1,3 +1,4 @@ +import copy import json import pytest @@ -14,7 +15,6 @@ from common.credentials import ( ) USER1 = "test_user_1" -USER2 = "test_user_2" PASSWORD = "12435" LM_HASH = "AEBD4DE384C7EC43AAD3B435B51404EE" NT_HASH = "7A21990FCD3D759941E45C490F143D5F" @@ -22,74 +22,93 @@ PUBLIC_KEY = "MY_PUBLIC_KEY" PRIVATE_KEY = "MY_PRIVATE_KEY" CREDENTIALS_DICT = { - "identities": [ - {"credential_type": "USERNAME", "username": USER1}, - {"credential_type": "USERNAME", "username": USER2}, - ], - "secrets": [ - {"credential_type": "PASSWORD", "password": PASSWORD}, - {"credential_type": "LM_HASH", "lm_hash": LM_HASH}, - {"credential_type": "NT_HASH", "nt_hash": NT_HASH}, - { - "credential_type": "SSH_KEYPAIR", - "public_key": PUBLIC_KEY, - "private_key": PRIVATE_KEY, - }, - ], + "identity": {"credential_type": "USERNAME", "username": USER1}, + "secret": {}, } -CREDENTIALS_JSON = json.dumps(CREDENTIALS_DICT) - -IDENTITIES = (Username(USER1), Username(USER2)) +IDENTITY = Username(USER1) SECRETS = ( Password(PASSWORD), LMHash(LM_HASH), NTHash(NT_HASH), SSHKeypair(PRIVATE_KEY, PUBLIC_KEY), ) -CREDENTIALS_OBJECT = Credentials(IDENTITIES, SECRETS) + +SECRETS_DICTS = [ + {"credential_type": "PASSWORD", "password": PASSWORD}, + {"credential_type": "LM_HASH", "lm_hash": LM_HASH}, + {"credential_type": "NT_HASH", "nt_hash": NT_HASH}, + { + "credential_type": "SSH_KEYPAIR", + "public_key": PUBLIC_KEY, + "private_key": PRIVATE_KEY, + }, +] -def test_credentials_serialization_json(): - serialized_credentials = Credentials.to_json(CREDENTIALS_OBJECT) +@pytest.mark.parametrize("secret, expected_secret", zip(SECRETS, SECRETS_DICTS)) +def test_credentials_serialization_json(secret, expected_secret): + expected_credentials = copy.copy(CREDENTIALS_DICT) + expected_credentials["secret"] = expected_secret + c = Credentials(IDENTITY, secret) - assert json.loads(serialized_credentials) == CREDENTIALS_DICT + serialized_credentials = Credentials.to_json(c) + + assert json.loads(serialized_credentials) == expected_credentials -def test_credentials_serialization_mapping(): - serialized_credentials = Credentials.to_mapping(CREDENTIALS_OBJECT) +@pytest.mark.parametrize("secret, expected_secret", zip(SECRETS, SECRETS_DICTS)) +def test_credentials_serialization_mapping(secret, expected_secret): + expected_credentials = copy.copy(CREDENTIALS_DICT) + expected_credentials["secret"] = expected_secret + c = Credentials(IDENTITY, secret) - assert serialized_credentials == CREDENTIALS_DICT + serialized_credentials = Credentials.to_mapping(c) + + assert serialized_credentials == expected_credentials -def test_credentials_deserialization__from_mapping(): - deserialized_credentials = Credentials.from_mapping(CREDENTIALS_DICT) +@pytest.mark.parametrize("secret, secret_dict", zip(SECRETS, SECRETS_DICTS)) +def test_credentials_deserialization__from_mapping(secret, secret_dict): + expected_credentials = Credentials(IDENTITY, secret) + credentials_dict = copy.copy(CREDENTIALS_DICT) + credentials_dict["secret"] = secret_dict - assert deserialized_credentials == CREDENTIALS_OBJECT + deserialized_credentials = Credentials.from_mapping(credentials_dict) + + assert deserialized_credentials == expected_credentials -def test_credentials_deserialization__from_json(): - deserialized_credentials = Credentials.from_json(CREDENTIALS_JSON) +@pytest.mark.parametrize("secret, secret_dict", zip(SECRETS, SECRETS_DICTS)) +def test_credentials_deserialization__from_json(secret, secret_dict): + expected_credentials = Credentials(IDENTITY, secret) + credentials_dict = copy.copy(CREDENTIALS_DICT) + credentials_dict["secret"] = secret_dict - assert deserialized_credentials == CREDENTIALS_OBJECT + deserialized_credentials = Credentials.from_json(json.dumps(credentials_dict)) + + assert deserialized_credentials == expected_credentials def test_credentials_deserialization__invalid_credentials(): - invalid_data = {"secrets": [], "unknown_key": []} + invalid_data = {"secret": SECRETS_DICTS[0], "unknown_key": []} with pytest.raises(InvalidCredentialsError): Credentials.from_mapping(invalid_data) def test_credentials_deserialization__invalid_component_type(): - invalid_data = {"secrets": [], "identities": [{"credential_type": "FAKE", "username": "user1"}]} + invalid_data = { + "secret": SECRETS_DICTS[0], + "identity": {"credential_type": "FAKE", "username": "user1"}, + } with pytest.raises(InvalidCredentialsError): Credentials.from_mapping(invalid_data) def test_credentials_deserialization__invalid_component(): invalid_data = { - "secrets": [], - "identities": [{"credential_type": "USERNAME", "unknown_field": "user1"}], + "secret": SECRETS_DICTS[0], + "identity": {"credential_type": "USERNAME", "unknown_field": "user1"}, } with pytest.raises(InvalidCredentialComponentError): Credentials.from_mapping(invalid_data)