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:
parent
e3b23993fa
commit
2af713dabd
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue