aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gn_auth/auth/authorisation/resources/views.py39
-rw-r--r--gn_auth/auth/authorisation/roles/models.py11
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 (?, ?, ?)",