aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gn3/auth/authorisation/groups.py41
-rw-r--r--tests/unit/auth/test_groups.py28
2 files changed, 61 insertions, 8 deletions
diff --git a/gn3/auth/authorisation/groups.py b/gn3/auth/authorisation/groups.py
index 6d7b885..7597a04 100644
--- a/gn3/auth/authorisation/groups.py
+++ b/gn3/auth/authorisation/groups.py
@@ -1,8 +1,10 @@
"""Handle the management of resource/user groups."""
from uuid import UUID, uuid4
-from typing import Iterable, NamedTuple
+from typing import Sequence, Iterable, NamedTuple
from gn3.auth import db
+from gn3.auth.authentication.users import User
+
from .privileges import Privilege
from .roles import Role, create_role
from .checks import authorised_p
@@ -17,18 +19,45 @@ class GroupRole(NamedTuple):
group_role_id: UUID
role: Role
+class MembershipError(Exception):
+ """Raised when there is an error with a user's membership to a group."""
+
+ def __init__(self, user: User, groups: Sequence[Group]):
+ """Initialise the `MembershipError` exception object."""
+ groups_str = ", ".join(group.group_name for group in groups)
+ error_message = (
+ f"User '{user.name} ({user.email})' is a member of {len(groups)} "
+ f"groups ({groups_str})")
+ super().__init__(f"{type(self).__name__}: {error_message}.")
+
+def user_membership(conn: db.DbConnection, user: User) -> Sequence[Group]:
+ """Returns all the groups that a member belongs to"""
+ query = (
+ "SELECT groups.group_id, group_name FROM group_users INNER JOIN groups "
+ "ON group_users.group_id=groups.group_id "
+ "WHERE group_users.user_id=?")
+ with db.cursor(conn) as cursor:
+ cursor.execute(query, (str(user.user_id),))
+ groups = tuple(Group(row[0], row[1]) for row in cursor.fetchall())
+
+ return groups
+
@authorised_p(("create-group",), error_message="Failed to create group.")
-def create_group(conn: db.DbConnection, group_name: str) -> Group:
+def create_group(conn: db.DbConnection, group_name: str,
+ group_leader: User) -> Group:
"""Create a group"""
group = Group(uuid4(), group_name)
+ user_groups = user_membership(conn, group_leader)
+ if len(user_groups) > 0:
+ raise MembershipError(group_leader, user_groups)
+
with db.cursor(conn) as cursor:
- ## Maybe check whether the user is already a member of a group
- ## if they are not a member of any group, proceed to create the group
- ## if they are a member of a group, then fail with an exception
cursor.execute(
"INSERT INTO groups(group_id, group_name) VALUES (?, ?)",
(str(group.group_id), group_name))
- ## Maybe assign `group-leader` role to user creating the group
+ cursor.execute(
+ "INSERT INTO group_users VALUES (?, ?)",
+ (str(group.group_id), str(group_leader.user_id)))
return group
diff --git a/tests/unit/auth/test_groups.py b/tests/unit/auth/test_groups.py
index 9471cac..225bb59 100644
--- a/tests/unit/auth/test_groups.py
+++ b/tests/unit/auth/test_groups.py
@@ -4,10 +4,11 @@ from uuid import UUID
import pytest
from gn3.auth import db
+from gn3.auth.authentication.users import User
from gn3.auth.authorisation.roles import Role
from gn3.auth.authorisation.privileges import Privilege
from gn3.auth.authorisation.groups import (
- Group, GroupRole, create_group, create_group_role)
+ Group, GroupRole, create_group, MembershipError, create_group_role)
create_group_failure = {
"status": "error",
@@ -44,7 +45,8 @@ def test_create_group(# pylint: disable=[too-many-arguments]
with test_app.app_context() as flask_context:
flask_context.g.user_id = UUID(user_id)
with db.connection(auth_testdb_path) as conn:
- assert create_group(conn, "a_test_group") == expected
+ assert create_group(conn, "a_test_group", User(
+ UUID(user_id), "some@email.address", "a_test_user")) == expected
create_role_failure = {
"status": "error",
@@ -76,3 +78,25 @@ def test_create_group_role(mocker, test_users_in_group, test_app, user_id, expec
flask_context.g.user_id = UUID(user_id)
assert create_group_role(
conn, GROUP, "ResourceEditor", PRIVILEGES) == expected
+
+@pytest.mark.unit_test
+def test_create_multiple_groups(mocker, test_app, test_users):
+ """
+ GIVEN: An authenticated user with appropriate authorisation
+ WHEN: The user attempts to create a new group, while being a member of an
+ existing group
+ THEN: The system should prevent that, and respond with an appropriate error
+ message
+ """
+ mocker.patch("gn3.auth.authorisation.groups.uuid4", uuid_fn)
+ user_id = UUID("ecb52977-3004-469e-9428-2a1856725c7f")
+ conn, _test_users = test_users
+ with test_app.app_context() as flask_context:
+ flask_context.g.user_id = user_id
+ user = User(user_id, "some@email.address", "a_test_user")
+ # First time, successfully creates the group
+ assert create_group(conn, "a_test_group", user) == Group(
+ UUID("d32611e3-07fc-4564-b56c-786c6db6de2b"), "a_test_group")
+ # subsequent attempts should fail
+ with pytest.raises(MembershipError):
+ create_group(conn, "another_test_group", user)