From 76c464946d01073b8bcb757345d0d42b9a8207e4 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Sat, 28 Jan 2023 03:16:45 +0300 Subject: auth: rework dictify Define a Protocol type to use with the `dictify` function and implement the `dictify` methods for the various classes. --- gn3/auth/authentication/users.py | 6 +++++- gn3/auth/authorisation/groups.py | 18 ++++++++++++++---- gn3/auth/authorisation/privileges.py | 9 ++++++++- gn3/auth/authorisation/resources.py | 20 +++++++++++++++++++- gn3/auth/authorisation/roles.py | 9 ++++++++- gn3/auth/dictify.py | 19 +++++++------------ 6 files changed, 61 insertions(+), 20 deletions(-) diff --git a/gn3/auth/authentication/users.py b/gn3/auth/authentication/users.py index ee3b5c2..b2d8d53 100644 --- a/gn3/auth/authentication/users.py +++ b/gn3/auth/authentication/users.py @@ -1,6 +1,6 @@ """User-specific code and data structures.""" from uuid import UUID, uuid4 -from typing import Tuple, NamedTuple +from typing import Any, Tuple, NamedTuple import bcrypt from pymonad.maybe import Just, Maybe, Nothing @@ -17,6 +17,10 @@ class User(NamedTuple): """Return the user's UUID. Mostly for use with Authlib.""" return self.user_id + def dictify(self) -> dict[str, Any]: + """Return a dict representation of `User` objects.""" + return {"user_id": self.user_id, "email": self.email, "name": self.name} + def user_by_email(conn: db.DbConnection, email: str) -> Maybe: """Retrieve user from database by their email address""" with db.cursor(conn) as cursor: diff --git a/gn3/auth/authorisation/groups.py b/gn3/auth/authorisation/groups.py index 6d1b1a3..9dd5b71 100644 --- a/gn3/auth/authorisation/groups.py +++ b/gn3/auth/authorisation/groups.py @@ -7,8 +7,8 @@ from flask import g from pymonad.maybe import Just, Maybe, Nothing from gn3.auth import db +from gn3.auth.dictify import dictify from gn3.auth.authentication.users import User -from gn3.auth.dictify import register_dictifier from gn3.auth.authentication.checks import authenticated_p from .checks import authorised_p @@ -23,9 +23,12 @@ class Group(NamedTuple): group_name: str group_metadata: dict[str, Any] -register_dictifier(Group, lambda grp: { - "group_id": grp.group_id, "group_name": grp.group_name, - "group_metadata": grp.group_metadata}) + def dictify(self): + """Return a dict representation of `Group` objects.""" + return { + "group_id": self.group_id, "group_name": self.group_name, + "group_metadata": self.group_metadata + } class GroupRole(NamedTuple): """Class representing a role tied/belonging to a group.""" @@ -33,6 +36,13 @@ class GroupRole(NamedTuple): group: Group role: Role + def dictify(self) -> dict[str, Any]: + """Return a dict representation of `GroupRole` objects.""" + return { + "group_role_id": self.group_role_id, "group": dictify(self.group), + "role": dictify(self.role) + } + class GroupCreationError(AuthorisationError): """Raised whenever a group creation fails""" diff --git a/gn3/auth/authorisation/privileges.py b/gn3/auth/authorisation/privileges.py index 6cfd1d8..ae4ed88 100644 --- a/gn3/auth/authorisation/privileges.py +++ b/gn3/auth/authorisation/privileges.py @@ -1,5 +1,5 @@ """Handle privileges""" -from typing import Iterable, NamedTuple +from typing import Any, Iterable, NamedTuple from gn3.auth import db from gn3.auth.authentication.users import User @@ -9,6 +9,13 @@ class Privilege(NamedTuple): privilege_id: str privilege_description: str + def dictify(self) -> dict[str, Any]: + """Return a dict representation of `Privilege` objects.""" + return { + "privilege_id": self.privilege_id, + "privilege_description": self.privilege_description + } + def user_privileges(conn: db.DbConnection, user: User) -> Iterable[Privilege]: """Fetch the user's privileges from the database.""" with db.cursor(conn) as cursor: diff --git a/gn3/auth/authorisation/resources.py b/gn3/auth/authorisation/resources.py index 29e50bf..1e37d7a 100644 --- a/gn3/auth/authorisation/resources.py +++ b/gn3/auth/authorisation/resources.py @@ -1,9 +1,10 @@ """Handle the management of resources.""" import json from uuid import UUID, uuid4 -from typing import Dict, Sequence, NamedTuple +from typing import Any, Dict, Sequence, NamedTuple from gn3.auth import db +from gn3.auth.dictify import dictify from gn3.auth.authentication.users import User from .checks import authorised_p @@ -19,6 +20,14 @@ class ResourceCategory(NamedTuple): resource_category_key: str resource_category_description: str + def dictify(self) -> dict[str, Any]: + """Return a dict representation of `ResourceCategory` objects.""" + return { + "resource_category_id": self.resource_category_id, + "resource_category_key": self.resource_category_key, + "resource_category_description": self.resource_category_description + } + class Resource(NamedTuple): """Class representing a resource.""" group: Group @@ -27,6 +36,15 @@ class Resource(NamedTuple): resource_category: ResourceCategory public: bool + def dictify(self) -> dict[str, Any]: + """Return a dict representation of `Resource` objects.""" + return { + "group": dictify(self.group), "resource_id": self.resource_id, + "resource_name": self.resource_name, + "resource_category": dictify(self.resource_category), + "public": self.public + } + @authorised_p(("group:resource:create-resource",), error_message="Could not create resource") def create_resource( diff --git a/gn3/auth/authorisation/roles.py b/gn3/auth/authorisation/roles.py index cd59a36..86759b1 100644 --- a/gn3/auth/authorisation/roles.py +++ b/gn3/auth/authorisation/roles.py @@ -1,11 +1,12 @@ """Handle management of roles""" from uuid import UUID, uuid4 from functools import reduce -from typing import Sequence, Iterable, NamedTuple +from typing import Any, Sequence, Iterable, NamedTuple from pymonad.maybe import Just, Maybe, Nothing from gn3.auth import db +from gn3.auth.dictify import dictify from gn3.auth.authentication.users import User from gn3.auth.authentication.checks import authenticated_p @@ -18,6 +19,12 @@ class Role(NamedTuple): role_name: str privileges: Iterable[Privilege] + def dictify(self) -> dict[str, Any]: + """Return a dict representation of `Role` objects.""" + return { + "role_id": self.role_id, "role_name": self.role_name, + "privileges": tuple(dictify(priv) for priv in self.privileges) + } @authenticated_p @authorised_p(("group:role:create-role",), error_message="Could not create role") def create_role( diff --git a/gn3/auth/dictify.py b/gn3/auth/dictify.py index 274ebb0..f9337f6 100644 --- a/gn3/auth/dictify.py +++ b/gn3/auth/dictify.py @@ -1,17 +1,12 @@ """Module for dictifying objects""" -from typing import Any +from typing import Any, Protocol -# TYPE = TypeVar("TYPE") +class Dictifiable(Protocol):# pylint: disable=[too-few-public-methods] + """Type annotation for generic object with a `dictify` method.""" + def dictify(self): + """Convert the object to a dict""" -__dictifiers__ = {}#: dict[TYPE, Callable[[TYPE], dict[str, Any]]] = {} - -# def register_dictifier(obj_type: TYPE, dictifier: Callable[[TYPE], dict[str, Any]]): -def register_dictifier(obj_type, dictifier): - """Register a new dictifier function""" - global __dictifiers__ # pylint: disable=[global-variable-not-assigned] - __dictifiers__[obj_type] = dictifier - -def dictify(obj: Any) -> dict[str, Any]: +def dictify(obj: Dictifiable) -> dict[str, Any]: """Turn `obj` to a dict representation.""" - return __dictifiers__[type(obj)](obj) + return obj.dictify() -- cgit v1.2.3