forked from p15670423/monkey
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.
This commit is contained in:
parent
8e332e5285
commit
2b245b34cb
|
@ -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:
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue