about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--gn3/auth/authorisation/groups/views.py47
-rw-r--r--gn3/auth/db_utils.py14
-rw-r--r--migrations/auth/20230207_01_r0bkZ-create-group-requests-table.py27
-rw-r--r--tests/unit/auth/test_migrations_create_tables.py3
4 files changed, 89 insertions, 2 deletions
diff --git a/gn3/auth/authorisation/groups/views.py b/gn3/auth/authorisation/groups/views.py
index 02b3162..f6675ab 100644
--- a/gn3/auth/authorisation/groups/views.py
+++ b/gn3/auth/authorisation/groups/views.py
@@ -1,15 +1,21 @@
 """The views/routes for the `gn3.auth.authorisation.groups` package."""
 import uuid
+import datetime
+from functools import partial
 
 from flask import request, jsonify, Response, Blueprint, current_app
 
 from gn3.auth import db
 from gn3.auth.dictify import dictify
+from gn3.auth.db_utils import with_db_connection
 
 from .models import (
-    all_groups, GroupCreationError, group_users as _group_users,
+    user_group, all_groups, GroupCreationError, group_users as _group_users,
     create_group as _create_group)
 
+from ..errors import AuthorisationError
+
+from ...authentication.users import User
 from ...authentication.oauth2.resource_server import require_oauth
 
 groups = Blueprint("groups", __name__)
@@ -52,3 +58,42 @@ def group_members(group_id: uuid.UUID) -> Response:
         with db.connection(db_uri) as conn:
             return jsonify(tuple(
                 dictify(user) for user in _group_users(conn, group_id)))
+
+@groups.route("/requests/join/<uuid:group_id>", methods=["POST"])
+@require_oauth("profile group")
+def request_to_join(group_id: uuid.UUID) -> Response:
+    """Request to join a group."""
+    def __request__(conn: db.DbConnection, user: User, group_id: uuid.UUID,
+                    message: str):
+        with db.cursor(conn) as cursor:
+            group = user_group(cursor, user).maybe(# type: ignore[misc]
+                False, lambda grp: grp)# type: ignore[arg-type]
+            if group:
+                error = AuthorisationError(
+                    "You cannot request to join a new group while being a "
+                    "member of an existing group.")
+                error.error_code = 400
+                raise error
+            request_id = uuid.uuid4()
+            cursor.execute(
+                "INSERT INTO group_requests VALUES "
+                "(:request_id, :group_id, :user_id, :ts, :type, :msg)",
+                {
+                    "request_id": str(request_id),
+                    "group_id": str(group_id),
+                    "user_id": str(user.user_id),
+                    "ts": datetime.datetime.now().timestamp(),
+                    "type": "JOIN",
+                    "msg": message
+                })
+            return {
+                "request_id":  request_id,
+                "message": "Successfully sent the join request."
+            }
+
+    with require_oauth.acquire("profile group") as the_token:
+        form = request.form
+        results = with_db_connection(partial(
+            __request__, user=the_token.user, group_id=group_id, message=form.get(
+                "message", "I hereby request that you add me to your group.")))
+        return jsonify(results)
diff --git a/gn3/auth/db_utils.py b/gn3/auth/db_utils.py
new file mode 100644
index 0000000..c06b026
--- /dev/null
+++ b/gn3/auth/db_utils.py
@@ -0,0 +1,14 @@
+"""Some common auth db utilities"""
+from typing import Any, Callable
+from flask import current_app
+
+from . import db
+
+def with_db_connection(func: Callable[[db.DbConnection], Any]) -> Any:
+    """
+    Takes a function of one argument `func`, whose one argument is a database
+    connection.
+    """
+    db_uri = current_app.config["AUTH_DB"]
+    with db.connection(db_uri) as conn:
+        return func(conn)
diff --git a/migrations/auth/20230207_01_r0bkZ-create-group-requests-table.py b/migrations/auth/20230207_01_r0bkZ-create-group-requests-table.py
new file mode 100644
index 0000000..d2cf786
--- /dev/null
+++ b/migrations/auth/20230207_01_r0bkZ-create-group-requests-table.py
@@ -0,0 +1,27 @@
+"""
+Create group_requests table
+"""
+
+from yoyo import step
+
+__depends__ = {'20230116_01_KwuJ3-rework-privileges-schema'}
+
+steps = [
+    step(
+        """
+        CREATE TABLE IF NOT EXISTS group_requests(
+            request_id TEXT NOT NULL,
+            group_id TEXT NOT NULL,
+            requester_id TEXT NOT NULL,
+            request_type TEXT NOT NULL,
+            timestamp REAL NOT NULL,
+            message TEXT,
+            PRIMARY KEY(request_id, group_id),
+            FOREIGN KEY(group_id) REFERENCES groups(group_id)
+            ON UPDATE CASCADE ON DELETE CASCADE,
+            FOREIGN KEY (requester_id) REFERENCES users(user_id)
+            ON UPDATE CASCADE ON DELETE CASCADE
+        ) WITHOUT ROWID
+        """,
+        "DROP TABLE IF EXISTS group_requests")
+]
diff --git a/tests/unit/auth/test_migrations_create_tables.py b/tests/unit/auth/test_migrations_create_tables.py
index 0fda718..55d51aa 100644
--- a/tests/unit/auth/test_migrations_create_tables.py
+++ b/tests/unit/auth/test_migrations_create_tables.py
@@ -30,7 +30,8 @@ migrations_and_tables = (
     ("20221219_01_CI3tN-create-oauth2-clients-table.py", "oauth2_clients"),
     ("20221219_02_buSEU-create-oauth2-tokens-table.py", "oauth2_tokens"),
     ("20221219_03_PcTrb-create-authorisation-code-table.py",
-     "authorisation_code"))
+     "authorisation_code"),
+    ("20230207_01_r0bkZ-create-group-requests-table.py", "group_requests"))
 
 @pytest.mark.unit_test
 @pytest.mark.parametrize("migration_file,the_table", migrations_and_tables)