aboutsummaryrefslogtreecommitdiff
"""Functions to check for authorisation."""
from functools import wraps
from typing import Callable

from flask import request, current_app as app

from gn3.auth import db

from . import privileges as auth_privs
from .errors import InvalidData, AuthorisationError

from ..authentication.oauth2.resource_server import require_oauth

def __system_privileges_in_roles__(conn, user):
    """
    This really is a hack since groups are not treated as resources at the
    moment of writing this.

    We need a way of allowing the user to have the system:group:* privileges.
    """
    query = (
        "SELECT DISTINCT p.* FROM users AS u "
        "INNER JOIN group_user_roles_on_resources AS guror "
        "ON u.user_id=guror.user_id "
        "INNER JOIN roles AS r ON guror.role_id=r.role_id "
        "INNER JOIN role_privileges AS rp ON r.role_id=rp.role_id "
        "INNER JOIN privileges AS p ON rp.privilege_id=p.privilege_id "
        "WHERE u.user_id=? AND p.privilege_id LIKE 'system:%'")
    with db.cursor(conn) as cursor:
        cursor.execute(query, (str(user.user_id),))
        return (row["privilege_id"] for row in cursor.fetchall())

def authorised_p(
        privileges: tuple[str, ...],
        error_description: str = (
            "You lack authorisation to perform requested action"),
        oauth2_scope = "profile"):
    """Authorisation decorator."""
    assert len(privileges) > 0, "You must provide at least one privilege"
    def __build_authoriser__(func: Callable):
        @wraps(func)
        def __authoriser__(*args, **kwargs):
            # the_user = user or (hasattr(g, "user") and g.user)
            with require_oauth.acquire(oauth2_scope) as the_token:
                the_user = the_token.user
                if the_user:
                    with db.connection(app.config["AUTH_DB"]) as conn:
                        user_privileges = tuple(
                            priv.privilege_id for priv in
                            auth_privs.user_privileges(conn, the_user)) + tuple(
                                priv_id for priv_id in
                                __system_privileges_in_roles__(conn, the_user))

                    not_assigned = [
                        priv for priv in privileges if priv not in user_privileges]
                    if len(not_assigned) == 0:
                        return func(*args, **kwargs)

                raise AuthorisationError(error_description)
        return __authoriser__
    return __build_authoriser__

def require_json(func):
    """Ensure the request has JSON data."""
    @wraps(func)
    def __req_json__(*args, **kwargs):
        if bool(request.json):
            return func(*args, **kwargs)
        raise InvalidData("Expected JSON data in the request.")
    return __req_json__