From 5d21e8195d1a51a633608b20baa8dbee01d51a7c Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 30 Jan 2023 03:00:27 +0300 Subject: auth: API: Retrieve a user's group Add endpoint to retrieve the group in which the user is a member. --- gn3/auth/authorisation/errors.py | 4 ++++ gn3/auth/authorisation/groups.py | 9 +++++---- gn3/auth/authorisation/roles.py | 8 ++------ gn3/auth/authorisation/views.py | 20 +++++++++++++++++++- 4 files changed, 30 insertions(+), 11 deletions(-) (limited to 'gn3/auth/authorisation') diff --git a/gn3/auth/authorisation/errors.py b/gn3/auth/authorisation/errors.py index 89c5983..e1fb1a0 100644 --- a/gn3/auth/authorisation/errors.py +++ b/gn3/auth/authorisation/errors.py @@ -10,3 +10,7 @@ class AuthorisationError(Exception): class UserRegistrationError(AuthorisationError): """Raised whenever a user registration fails""" + +class NotFoundError(AuthorisationError): + """Raised whenever we try fetching (a/an) object(s) that do(es) not exist.""" + error_code: int = 404 diff --git a/gn3/auth/authorisation/groups.py b/gn3/auth/authorisation/groups.py index 9dd5b71..cd7e034 100644 --- a/gn3/auth/authorisation/groups.py +++ b/gn3/auth/authorisation/groups.py @@ -4,6 +4,7 @@ from uuid import UUID, uuid4 from typing import Any, Sequence, Iterable, Optional, NamedTuple from flask import g +from pymonad.either import Left, Right, Either from pymonad.maybe import Just, Maybe, Nothing from gn3.auth import db @@ -13,7 +14,7 @@ from gn3.auth.authentication.checks import authenticated_p from .checks import authorised_p from .privileges import Privilege -from .errors import AuthorisationError +from .errors import NotFoundError, AuthorisationError from .roles import ( Role, create_role, revoke_user_role_by_name, assign_user_role_by_name) @@ -140,7 +141,7 @@ def authenticated_user_group(conn) -> Maybe: return Nothing -def user_group(cursor: db.DbCursor, user: User) -> Maybe[Group]: +def user_group(cursor: db.DbCursor, user: User) -> Either: """Returns the given user's group""" cursor.execute( ("SELECT groups.group_id, groups.group_name, groups.group_metadata " @@ -156,9 +157,9 @@ def user_group(cursor: db.DbCursor, user: User) -> Maybe[Group]: raise MembershipError(user, groups) if len(groups) == 1: - return Just(groups[0]) + return Right(groups[0]) - return Nothing + return Left(NotFoundError("User is not in any group.")) def is_group_leader(cursor: db.DbCursor, user: User, group: Group): """Check whether the given `user` is the leader of `group`.""" diff --git a/gn3/auth/authorisation/roles.py b/gn3/auth/authorisation/roles.py index e75163d..23b74cc 100644 --- a/gn3/auth/authorisation/roles.py +++ b/gn3/auth/authorisation/roles.py @@ -13,7 +13,7 @@ from gn3.auth.authentication.checks import authenticated_p from .checks import authorised_p from .privileges import Privilege -from .errors import AuthorisationError +from .errors import NotFoundError, AuthorisationError class Role(NamedTuple): """Class representing a role: creates immutable objects.""" @@ -28,10 +28,6 @@ class Role(NamedTuple): "privileges": tuple(dictify(priv) for priv in self.privileges) } -class RoleNotFoundError(AuthorisationError): - """Raised whenever we try fetching (a) role(s) that do(es) not exist.""" - error_code: int = 404 - @authenticated_p @authorised_p(("group:role:create-role",), error_message="Could not create role") def create_role( @@ -115,7 +111,7 @@ def user_role(conn: db.DbConnection, user: User, role_id: UUID) -> Either: if results: return Right(tuple( reduce(__organise_privileges__, results, {}).values())[0]) - return Left(RoleNotFoundError( + return Left(NotFoundError( f"Could not find role with id '{role_id}'",)) def assign_default_roles(cursor: db.DbCursor, user: User): diff --git a/gn3/auth/authorisation/views.py b/gn3/auth/authorisation/views.py index 6c6e5e3..33ac95b 100644 --- a/gn3/auth/authorisation/views.py +++ b/gn3/auth/authorisation/views.py @@ -19,6 +19,10 @@ from ..authentication.oauth2.resource_server import require_oauth from ..authentication.users import save_user, set_user_password from ..authentication.oauth2.models.oauth2token import token_by_access_token +def __raise_error__(exc): + current_app.logger.error(exc) + raise exc + @oauth2.route("/user", methods=["GET"]) @require_oauth("profile") def user_details(): @@ -28,11 +32,16 @@ def user_details(): with db.connection(current_app.config["AUTH_DB"]) as conn, db.cursor(conn) as cursor: group = user_group(cursor, user) + def __raise__(exc): + if type(exc) == NotFoundError: + return False + raise exc + return jsonify({ "user_id": user.user_id, "email": user.email, "name": user.name, - "group": group.maybe(False, dictify) + "group": group.either(__raise__, dictify) }) @oauth2.route("/user-roles", methods=["GET"]) @@ -159,3 +168,12 @@ def role(role_id: uuid.UUID) -> Response: the_role = user_role(conn, the_token.user, role_id) return the_role.either( __error__, lambda a_role: jsonify(dictify(a_role))) + +@oauth2.route("/user-group", methods=["GET"]) +@require_oauth("group") +def users_group(): + with require_oauth.acquire("profile group") as the_token: + db_uri = current_app.config["AUTH_DB"] + with db.connection(db_uri) as conn, db.cursor(conn) as cursor: + return user_group(cursor, the_token.user).either( + __raise_error__, lambda grp: jsonify(dictify(grp))) -- cgit v1.2.3