Common: Allow identities or secrets to be None

It's possible that credentials are stolen and an identity/secret
association can not be made. For example, a list of usernames can be
acquired by `ls /home`, but no passwords will be retrieved this way.
Credentials(identity=Username("username"), secret=None) will represent
this case.
This commit is contained in:
Mike Salvatore 2022-07-15 10:50:25 -04:00
parent e3b23993fa
commit 2af713dabd
2 changed files with 32 additions and 13 deletions

View File

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Mapping, MutableMapping, Sequence, Type from typing import Any, Mapping, MutableMapping, Optional, Sequence, Type
from marshmallow import Schema, fields, post_load, pre_dump from marshmallow import Schema, fields, post_load, pre_dump
from marshmallow.exceptions import MarshmallowError from marshmallow.exceptions import MarshmallowError
@ -42,8 +42,8 @@ CREDENTIAL_COMPONENT_TYPE_TO_CLASS_SCHEMA: Mapping[CredentialComponentType, Sche
class CredentialsSchema(Schema): class CredentialsSchema(Schema):
identity = fields.Mapping() identity = fields.Mapping(allow_none=True)
secret = fields.Mapping() secret = fields.Mapping(allow_none=True)
@post_load @post_load
def _make_credentials( def _make_credentials(
@ -55,9 +55,16 @@ class CredentialsSchema(Schema):
return data return data
@staticmethod @staticmethod
def _build_credential_component(data: Mapping[str, Any]) -> ICredentialComponent: def _build_credential_component(
credential_component: Optional[Mapping[str, Any]]
) -> Optional[ICredentialComponent]:
if credential_component is None:
return None
try: try:
credential_component_type = CredentialComponentType[data["credential_type"]] credential_component_type = CredentialComponentType[
credential_component["credential_type"]
]
except KeyError as err: except KeyError as err:
raise InvalidCredentialsError(f"Unknown credential component type {err}") raise InvalidCredentialsError(f"Unknown credential component type {err}")
@ -67,7 +74,9 @@ class CredentialsSchema(Schema):
] ]
try: try:
return credential_component_class(**credential_component_schema.load(data)) return credential_component_class(
**credential_component_schema.load(credential_component)
)
except MarshmallowError as err: except MarshmallowError as err:
raise InvalidCredentialComponentError(credential_component_class, str(err)) raise InvalidCredentialComponentError(credential_component_class, str(err))
@ -84,8 +93,11 @@ class CredentialsSchema(Schema):
@staticmethod @staticmethod
def _serialize_credential_component( def _serialize_credential_component(
credential_component: ICredentialComponent, credential_component: Optional[ICredentialComponent],
) -> Mapping[str, Any]: ) -> Optional[Mapping[str, Any]]:
if credential_component is None:
return None
credential_component_schema = CREDENTIAL_COMPONENT_TYPE_TO_CLASS_SCHEMA[ credential_component_schema = CREDENTIAL_COMPONENT_TYPE_TO_CLASS_SCHEMA[
credential_component.credential_type credential_component.credential_type
] ]
@ -95,8 +107,8 @@ class CredentialsSchema(Schema):
@dataclass(frozen=True) @dataclass(frozen=True)
class Credentials(IJSONSerializable): class Credentials(IJSONSerializable):
identity: ICredentialComponent identity: Optional[ICredentialComponent]
secret: ICredentialComponent secret: Optional[ICredentialComponent]
@staticmethod @staticmethod
def from_mapping(credentials: Mapping) -> Credentials: def from_mapping(credentials: Mapping) -> Credentials:

View File

@ -22,14 +22,15 @@ from common.credentials import (
Username, Username,
) )
IDENTITIES = [Username(USERNAME)] IDENTITIES = [Username(USERNAME), None]
IDENTITY_DICTS = [{"credential_type": "USERNAME", "username": USERNAME}] IDENTITY_DICTS = [{"credential_type": "USERNAME", "username": USERNAME}, None]
SECRETS = ( SECRETS = (
Password(PASSWORD_1), Password(PASSWORD_1),
LMHash(LM_HASH), LMHash(LM_HASH),
NTHash(NT_HASH), NTHash(NT_HASH),
SSHKeypair(PRIVATE_KEY, PUBLIC_KEY), SSHKeypair(PRIVATE_KEY, PUBLIC_KEY),
None,
) )
SECRET_DICTS = [ SECRET_DICTS = [
{"credential_type": "PASSWORD", "password": PASSWORD_1}, {"credential_type": "PASSWORD", "password": PASSWORD_1},
@ -40,13 +41,19 @@ SECRET_DICTS = [
"public_key": PUBLIC_KEY, "public_key": PUBLIC_KEY,
"private_key": PRIVATE_KEY, "private_key": PRIVATE_KEY,
}, },
None,
] ]
CREDENTIALS = [Credentials(identity, secret) for identity, secret in product(IDENTITIES, SECRETS)] CREDENTIALS = [
Credentials(identity, secret)
for identity, secret in product(IDENTITIES, SECRETS)
if not (identity is None and secret is None)
]
CREDENTIALS_DICTS = [ CREDENTIALS_DICTS = [
{"identity": identity, "secret": secret} {"identity": identity, "secret": secret}
for identity, secret in product(IDENTITY_DICTS, SECRET_DICTS) for identity, secret in product(IDENTITY_DICTS, SECRET_DICTS)
if not (identity is None and secret is None)
] ]