mirror of
https://github.com/enpaul/keyosk.git
synced 2024-11-25 07:57:24 +00:00
Update serializers to work with new database models
This commit is contained in:
parent
8de4da92ef
commit
ad7a4ea278
@ -1,4 +1,4 @@
|
|||||||
# pylint: disable=missing-docstring
|
# pylint: disable=missing-docstring
|
||||||
from keyosk.serializers.account import AccountSerializer
|
from keyosk.serializers.account import AccountSerializer
|
||||||
from keyosk.serializers.account_acl import AccountACLSerializer
|
|
||||||
from keyosk.serializers.domain import DomainSerializer
|
from keyosk.serializers.domain import DomainSerializer
|
||||||
|
from keyosk.serializers.scope import AccountScopeSerializer
|
||||||
|
@ -1,25 +1,61 @@
|
|||||||
from typing import List
|
import datetime
|
||||||
|
from typing import Any
|
||||||
|
from typing import Dict
|
||||||
|
from typing import Union
|
||||||
|
from uuid import UUID
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
import marshmallow as msh
|
import marshmallow as msh
|
||||||
|
from playhouse import shortcuts
|
||||||
|
|
||||||
from keyosk import fields as custom_fields
|
from keyosk._fields import Epoch
|
||||||
from keyosk.serializers.account_acl import AccountACLSerializer
|
from keyosk._fields import RawMultiType
|
||||||
|
from keyosk.database import KeyoskAccount
|
||||||
|
from keyosk.serializers.scope import AccountScopeSerializer
|
||||||
|
|
||||||
|
|
||||||
class AccountSerializer(msh.Schema):
|
class AccountSerializer(msh.Schema):
|
||||||
|
|
||||||
uuid = msh.fields.UUID(required=True)
|
uuid = msh.fields.UUID(required=True)
|
||||||
created = custom_fields.Epoch(required=True)
|
created = Epoch(required=True)
|
||||||
updated = custom_fields.Epoch(required=True)
|
updated = Epoch(required=True)
|
||||||
username = msh.fields.String(required=True)
|
username = msh.fields.String(required=True)
|
||||||
enabled = msh.fields.Boolean(required=True)
|
enabled = msh.fields.Boolean(required=True)
|
||||||
extras = msh.fields.Dict(
|
extras = msh.fields.Dict(
|
||||||
keys=msh.fields.String(),
|
keys=msh.fields.String(),
|
||||||
values=custom_fields.RawMultiType([int, float, bool, str], allow_none=True),
|
values=RawMultiType([int, float, bool, str], allow_none=True),
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
acls = msh.fields.List(msh.fields.Nested(AccountACLSerializer), required=True)
|
scopes = msh.fields.List(msh.fields.Nested(AccountScopeSerializer), required=True)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def creation_fields() -> List[str]:
|
@msh.post_load
|
||||||
return ["uuid", "created", "updated"]
|
def _make_model(data: Dict[str, Any], **kwargs) -> KeyoskAccount:
|
||||||
|
scopes = []
|
||||||
|
for item in data["scopes"]:
|
||||||
|
item.account_id = data["uuid"]
|
||||||
|
scopes.append(item)
|
||||||
|
data["scopes"] = scopes
|
||||||
|
return KeyoskAccount(**data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@msh.pre_dump
|
||||||
|
def _unmake_model(data: KeyoskAccount, **kwargs) -> Dict[str, Any]:
|
||||||
|
return shortcuts.model_to_dict(
|
||||||
|
data, recurse=False, backrefs=True, extra_attrs=["extras"],
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update(cls, uuid: Union[str, uuid.UUID], data: Dict[str, Any]) -> KeyoskAccount:
|
||||||
|
data.update({"uuid": UUID(str(uuid))})
|
||||||
|
loaded = cls(exclude=["created", "updated"]).load(data)
|
||||||
|
loaded.updated = datetime.datetime.utcnow()
|
||||||
|
return loaded
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, data: Dict[str, Any]) -> KeyoskAccount:
|
||||||
|
data.update({"uuid": uuid4()})
|
||||||
|
loaded = cls(exclude=["created", "updated"]).load(data)
|
||||||
|
loaded.updated = datetime.datetime.utcnow()
|
||||||
|
loaded.created = datetime.datetime.utcnow()
|
||||||
|
return loaded
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
import marshmallow as msh
|
|
||||||
|
|
||||||
|
|
||||||
class AccountACLSerializer(msh.Schema):
|
|
||||||
|
|
||||||
access_list = msh.fields.String(required=True, data_key="access-list")
|
|
||||||
permission = msh.fields.String(required=True)
|
|
||||||
with_server_secret = msh.fields.Boolean(
|
|
||||||
required=True, data_key="with-server-secret"
|
|
||||||
)
|
|
||||||
with_client_secret = msh.fields.Boolean(
|
|
||||||
required=True, data_key="with-client-secret"
|
|
||||||
)
|
|
@ -1,21 +1,20 @@
|
|||||||
|
import datetime
|
||||||
|
import re
|
||||||
|
from typing import Any
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
from uuid import UUID
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
import marshmallow as msh
|
import marshmallow as msh
|
||||||
|
from playhouse import shortcuts
|
||||||
|
|
||||||
from keyosk import constants
|
from keyosk import constants
|
||||||
from keyosk import fields as custom_fields
|
from keyosk._fields import Epoch
|
||||||
|
from keyosk.database import KeyoskDomain
|
||||||
|
from keyosk.database import KeyoskDomainAccessList
|
||||||
class DomainPermissionSerializer(msh.Schema):
|
from keyosk.database import KeyoskDomainPermission
|
||||||
""""""
|
|
||||||
|
|
||||||
name = msh.fields.String(
|
|
||||||
required=True,
|
|
||||||
validate=msh.validate.Regexp(constants.REGEX_DOMAIN_PERMISSION_NAME),
|
|
||||||
)
|
|
||||||
bitindex = msh.fields.Integer(required=True, validate=msh.validate.Range(min=0))
|
|
||||||
|
|
||||||
|
|
||||||
class DomainSerializer(msh.Schema):
|
class DomainSerializer(msh.Schema):
|
||||||
@ -30,8 +29,8 @@ class DomainSerializer(msh.Schema):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
uuid = msh.fields.UUID(required=True)
|
uuid = msh.fields.UUID(required=True)
|
||||||
created = custom_fields.Epoch(required=True)
|
created = Epoch(required=True)
|
||||||
updated = custom_fields.Epoch(required=True)
|
updated = Epoch(required=True)
|
||||||
name = msh.fields.String(
|
name = msh.fields.String(
|
||||||
required=True, validate=msh.validate.Regexp(constants.REGEX_DOMAIN_NAME)
|
required=True, validate=msh.validate.Regexp(constants.REGEX_DOMAIN_NAME)
|
||||||
)
|
)
|
||||||
@ -55,35 +54,102 @@ class DomainSerializer(msh.Schema):
|
|||||||
enable_refresh = msh.fields.Boolean(required=True, data_key="enable-refresh")
|
enable_refresh = msh.fields.Boolean(required=True, data_key="enable-refresh")
|
||||||
lifespan_access = msh.fields.TimeDelta(required=True, data_key="lifespan-access")
|
lifespan_access = msh.fields.TimeDelta(required=True, data_key="lifespan-access")
|
||||||
lifespan_refresh = msh.fields.TimeDelta(required=True, data_key="lifespan-refresh")
|
lifespan_refresh = msh.fields.TimeDelta(required=True, data_key="lifespan-refresh")
|
||||||
access_list_names = msh.fields.List(
|
access_lists = msh.fields.Method(
|
||||||
msh.fields.String(
|
serialize="serialize_access_lists",
|
||||||
validate=msh.validate.Regexp(constants.REGEX_DOMAIN_ACCESS_LIST_NAME)
|
deserialize="deserialize_access_lists",
|
||||||
),
|
|
||||||
required=True,
|
required=True,
|
||||||
data_key="access-lists",
|
data_key="access-lists",
|
||||||
)
|
)
|
||||||
permissions = msh.fields.List(
|
permissions = msh.fields.Method(
|
||||||
msh.fields.Nested(DomainPermissionSerializer), required=True,
|
serialize="serialize_permissions",
|
||||||
|
deserialize="deserialize_permissions",
|
||||||
|
required=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@msh.validates("access_list_names")
|
@staticmethod
|
||||||
def validate_acl_names(self, data: List[str], **kwargs):
|
def deserialize_access_lists(value: List[str]) -> List[KeyoskDomainAccessList]:
|
||||||
if len(data) != len(set(data)):
|
models = []
|
||||||
raise msh.ValidationError("Duplicate access list names")
|
errors = {}
|
||||||
|
for index, item in enumerate(set(value)):
|
||||||
|
if not isinstance(item, str):
|
||||||
|
errors[index] = f"Invalid type '{type(item)}', expected 'str'"
|
||||||
|
elif not re.search(constants.REGEX_DOMAIN_ACCESS_LIST_NAME, item):
|
||||||
|
errors[
|
||||||
|
index
|
||||||
|
] = f"Invalid format for value '{item}', must match '{constants.REGEX_DOMAIN_ACCESS_LIST_NAME}'"
|
||||||
|
else:
|
||||||
|
models.append(KeyoskDomainAccessList(name=item))
|
||||||
|
|
||||||
@msh.validates("permissions")
|
if errors:
|
||||||
def validate_permissions(self, data: List[Dict[str, Union[str, int]]], **kwargs):
|
raise msh.ValidationError(errors)
|
||||||
names = [item["name"] for item in data]
|
|
||||||
if len(names) != len(set(names)):
|
|
||||||
raise msh.ValidationError("Duplicat permission names")
|
|
||||||
|
|
||||||
indexes = sorted([item["bitindex"] for item in data])
|
return models
|
||||||
for index in len(indexes):
|
|
||||||
if indexes[index - 1] != (index - 1):
|
|
||||||
raise msh.ValidationError(
|
|
||||||
f"Invalid bitindexes provided: expected zero-index sequential sequence, recieved {indexes}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def creation_fields() -> List[str]:
|
def serialize_access_lists(obj: Dict[Any, Any]) -> List[str]:
|
||||||
return ["uuid", "created", "updated"]
|
return [item.name for item in obj["access_lists"]]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def deserialize_permissions(value: List[str]) -> List[KeyoskDomainPermission]:
|
||||||
|
models = []
|
||||||
|
errors = {}
|
||||||
|
for index, item in enumerate(set(value)):
|
||||||
|
if not isinstance(item, str):
|
||||||
|
errors[index] = f"Invalid type '{type(item)}', expected 'str'"
|
||||||
|
elif not re.search(constants.REGEX_DOMAIN_PERMISSION_NAME, item):
|
||||||
|
errors[
|
||||||
|
index
|
||||||
|
] = f"Invalid format for value '{item}', must match '{constants.REGEX_DOMAIN_PERMISSION_NAME}'"
|
||||||
|
else:
|
||||||
|
models.append(KeyoskDomainPermission(name=item))
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise msh.ValidationError(errors)
|
||||||
|
|
||||||
|
return models
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def serialize_permissions(obj: Dict[Any, Any]) -> List[str]:
|
||||||
|
return [item.name for item in obj["permissions"]]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@msh.post_load
|
||||||
|
def _make_model(data: Dict[str, Any], **kwargs) -> KeyoskDomain:
|
||||||
|
acls = []
|
||||||
|
for item in data["access_lists"]:
|
||||||
|
item.domain_id = data["uuid"]
|
||||||
|
acls.append(item)
|
||||||
|
data["access_lists"] = acls
|
||||||
|
|
||||||
|
permissions = []
|
||||||
|
for item in data["permissions"]:
|
||||||
|
item.domain_id = data["uuid"]
|
||||||
|
permissions.append(item)
|
||||||
|
data["permissions"] = permissions
|
||||||
|
|
||||||
|
return KeyoskDomain(**data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@msh.pre_dump
|
||||||
|
def _unmake_model(data: KeyoskDomain, **kwargs) -> Dict[str, Any]:
|
||||||
|
return shortcuts.model_to_dict(
|
||||||
|
data,
|
||||||
|
recurse=False,
|
||||||
|
backrefs=True,
|
||||||
|
extra_attrs=["lifespan_access", "lifespan_refresh",],
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update(cls, uuid: Union[str, uuid.UUID], data: Dict[str, Any]) -> KeyoskDomain:
|
||||||
|
data.update({"uuid": UUID(str(uuid))})
|
||||||
|
loaded = cls(exclude=["created", "updated"]).load(data)
|
||||||
|
loaded.updated = datetime.datetime.utcnow()
|
||||||
|
return loaded
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, data: Dict[str, Any]) -> KeyoskDomain:
|
||||||
|
data.update({"uuid": uuid4()})
|
||||||
|
loaded = cls(exclude=["created", "updated"]).load(data)
|
||||||
|
loaded.updated = datetime.datetime.utcnow()
|
||||||
|
loaded.created = datetime.datetime.utcnow()
|
||||||
|
return loaded
|
||||||
|
29
keyosk/serializers/scope.py
Normal file
29
keyosk/serializers/scope.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from typing import Any
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
import marshmallow as msh
|
||||||
|
from playhouse import shortcuts
|
||||||
|
|
||||||
|
from keyosk.database import KeyoskAccountScope
|
||||||
|
|
||||||
|
|
||||||
|
class AccountScopeSerializer(msh.Schema):
|
||||||
|
|
||||||
|
access_list = msh.fields.String(required=True, data_key="access-list")
|
||||||
|
permission = msh.fields.String(required=True)
|
||||||
|
with_server_secret = msh.fields.Boolean(
|
||||||
|
required=True, data_key="with-server-secret"
|
||||||
|
)
|
||||||
|
with_client_secret = msh.fields.Boolean(
|
||||||
|
required=True, data_key="with-client-secret"
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@msh.post_load
|
||||||
|
def _make_model(data: Dict[str, Any], **kwargs) -> KeyoskAccountScope:
|
||||||
|
return KeyoskAccountScope(**data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@msh.pre_dump
|
||||||
|
def _unmake_model(data: KeyoskAccountScope, **kwargs) -> Dict[str, Any]:
|
||||||
|
return shortcuts.model_to_dict(data, recurse=False, backrefs=False,)
|
Loading…
Reference in New Issue
Block a user