diff options
-rw-r--r-- | gn_auth/auth/authorisation/resources/views.py | 39 | ||||
-rw-r--r-- | gn_auth/auth/authorisation/roles/models.py | 11 |
2 files changed, 45 insertions, 5 deletions
diff --git a/gn_auth/auth/authorisation/resources/views.py b/gn_auth/auth/authorisation/resources/views.py index 4583346..736b315 100644 --- a/gn_auth/auth/authorisation/resources/views.py +++ b/gn_auth/auth/authorisation/resources/views.py @@ -8,8 +8,9 @@ import time from dataclasses import asdict from functools import reduce -from authlib.integrations.flask_oauth2.errors import _HTTPException +from werkzeug.exceptions import BadRequest from authlib.jose import jwt +from authlib.integrations.flask_oauth2.errors import _HTTPException from flask import (make_response, request, jsonify, Response, Blueprint, current_app as app) @@ -17,7 +18,9 @@ from gn_auth.auth.db import sqlite3 as db from gn_auth.auth.db.sqlite3 import with_db_connection from gn_auth.auth.authorisation.roles import Role +from gn_auth.auth.authorisation.roles.models import create_role from gn_auth.auth.authorisation.privileges import Privilege +from gn_auth.auth.authorisation.privileges.models import privileges_by_ids from gn_auth.auth.errors import InvalidData, InconsistencyError, AuthorisationError from gn_auth.auth.authorisation.roles.models import ( role_by_id, @@ -570,3 +573,37 @@ def resource_role_users(resource_id: UUID, role_id: UUID): results = cursor.fetchall() or [] return jsonify(tuple(User.from_sqlite3_row(row) for row in results)), 200 + + +@resources.route("/<uuid:resource_id>/roles/create", methods=["POST"]) +@require_oauth("profile group resource") +def create_resource_role(resource_id: UUID): + """Create a role to act upon a specific resource.""" + role_name = request.json.get("role_name", "").strip() + if not bool(role_name): + raise BadRequest("You must provide the name for the new role.") + + with (require_oauth.acquire("profile group resource") as _token, + db.connection(app.config["AUTH_DB"]) as conn, + db.cursor(conn) as cursor): + resource = resource_by_id(conn, _token.user, resource_id) + if not bool(resource): + raise BadRequest("No resource with that ID exists.") + + privileges = privileges_by_ids(conn, request.json.get("privileges", [])) + if len(privileges) == 0: + raise BadRequest( + "You must provide at least one privilege for the new role.") + role = create_role(cursor, + f"{resource.resource_name}::{role_name}", + privileges) + cursor.execute( + "INSERT INTO resource_roles(resource_id, role_created_by, role_id) " + "VALUES (:resource_id, :user_id, :role_id)", + { + "resource_id": str(resource_id), + "user_id": str(_token.user.user_id), + "role_id": str(role.role_id) + }) + + return jsonify(asdict(role)) diff --git a/gn_auth/auth/authorisation/roles/models.py b/gn_auth/auth/authorisation/roles/models.py index e740bfd..d58c4a1 100644 --- a/gn_auth/auth/authorisation/roles/models.py +++ b/gn_auth/auth/authorisation/roles/models.py @@ -54,11 +54,14 @@ def db_rows_to_roles(rows) -> tuple[Role, ...]: if bool(rows) else []) @authorised_p( - privileges = ("group:role:create-role",), + privileges = ("resource:role:create-role",), error_description="Could not create role") def create_role( - cursor: db.DbCursor, role_name: str, - privileges: Iterable[Privilege]) -> Role: + cursor: db.DbCursor, + role_name: str, + privileges: Iterable[Privilege], + user_editable: bool=True +) -> Role: """ Create a new generic role. @@ -71,7 +74,7 @@ def create_role( RETURNS: An immutable `gn3.auth.authorisation.roles.Role` object """ - role = Role(uuid4(), role_name, True, tuple(privileges)) + role = Role(uuid4(), role_name, user_editable, tuple(privileges)) cursor.execute( "INSERT INTO roles(role_id, role_name, user_editable) VALUES (?, ?, ?)", |