aboutsummaryrefslogtreecommitdiff
path: root/gn_auth/auth/authorisation/resources/groups
diff options
context:
space:
mode:
Diffstat (limited to 'gn_auth/auth/authorisation/resources/groups')
-rw-r--r--gn_auth/auth/authorisation/resources/groups/models.py118
-rw-r--r--gn_auth/auth/authorisation/resources/groups/views.py83
2 files changed, 180 insertions, 21 deletions
diff --git a/gn_auth/auth/authorisation/resources/groups/models.py b/gn_auth/auth/authorisation/resources/groups/models.py
index fa25594..a4aacc7 100644
--- a/gn_auth/auth/authorisation/resources/groups/models.py
+++ b/gn_auth/auth/authorisation/resources/groups/models.py
@@ -16,8 +16,10 @@ from gn_auth.auth.authentication.users import User, user_by_id
from gn_auth.auth.authorisation.checks import authorised_p
from gn_auth.auth.authorisation.privileges import Privilege
-from gn_auth.auth.authorisation.resources.base import Resource
from gn_auth.auth.authorisation.resources.errors import MissingGroupError
+from gn_auth.auth.authorisation.resources.base import (
+ Resource,
+ resource_from_dbrow)
from gn_auth.auth.errors import (
NotFoundError, AuthorisationError, InconsistencyError)
from gn_auth.auth.authorisation.roles.models import (
@@ -120,7 +122,7 @@ def create_group(
cursor, group_name, (
{"group_description": group_description}
if group_description else {}))
- group_resource = {
+ _group_resource = {
"group_id": str(new_group.group_id),
"resource_id": str(uuid4()),
"resource_name": group_name,
@@ -133,17 +135,17 @@ def create_group(
cursor.execute(
"INSERT INTO resources VALUES "
"(:resource_id, :resource_name, :resource_category_id, :public)",
- group_resource)
+ _group_resource)
cursor.execute(
"INSERT INTO group_resources(resource_id, group_id) "
"VALUES(:resource_id, :group_id)",
- group_resource)
+ _group_resource)
add_user_to_group(cursor, new_group, group_leader)
revoke_user_role_by_name(cursor, group_leader, "group-creator")
assign_user_role_by_name(
cursor,
group_leader,
- UUID(str(group_resource["resource_id"])),
+ UUID(str(_group_resource["resource_id"])),
"group-leader")
return new_group
@@ -235,15 +237,56 @@ def is_group_leader(conn: db.DbConnection, user: User, group: Group) -> bool:
return "group-leader" in role_names
-def all_groups(conn: db.DbConnection) -> Maybe[Sequence[Group]]:
+def __build_groups_list_query__(
+ base: str,
+ search: Optional[str] = None
+) -> tuple[str, tuple[Optional[str], ...]]:
+ """Build up the query from given search terms."""
+ if search is not None and search.strip() != "":
+ _search = search.strip()
+ return ((f"{base} WHERE groups.group_name LIKE ? "
+ "OR groups.group_metadata LIKE ?"),
+ (f"%{search}%", f"%{search}%"))
+ return base, tuple()
+
+
+def __limit_results_length__(base: str, start: int = 0, length: int = 0) -> str:
+ """Add the `LIMIT … OFFSET …` clause to query `base`."""
+ if length > 0:
+ return f"{base} LIMIT {length} OFFSET {start}"
+ return base
+
+
+def all_groups(
+ conn: db.DbConnection,
+ search: Optional[str] = None,
+ start: int = 0,
+ length: int = 0
+) -> Maybe[tuple[tuple[Group, ...], int, int]]:
"""Retrieve all existing groups"""
with db.cursor(conn) as cursor:
- cursor.execute("SELECT * FROM groups")
+ cursor.execute("SELECT COUNT(*) FROM groups")
+ _groups_total_count = int(cursor.fetchone()["COUNT(*)"])
+
+ _qdets = __build_groups_list_query__(
+ "SELECT COUNT(*) FROM groups", search)
+ cursor.execute(*__build_groups_list_query__(
+ "SELECT COUNT(*) FROM groups", search))
+ _filtered_total_count = int(cursor.fetchone()["COUNT(*)"])
+
+ _query, _params = __build_groups_list_query__(
+ "SELECT * FROM groups", search)
+
+ cursor.execute(__limit_results_length__(_query, start, length),
+ _params)
res = cursor.fetchall()
if res:
- return Just(tuple(
- Group(row["group_id"], row["group_name"],
- json.loads(row["group_metadata"])) for row in res))
+ return Just((
+ tuple(
+ Group(row["group_id"], row["group_name"],
+ json.loads(row["group_metadata"])) for row in res),
+ _groups_total_count,
+ _filtered_total_count))
return Nothing
@@ -519,3 +562,58 @@ def admin_group(conn: db.DbConnection) -> Either:
row["group_name"],
json.loads(row["group_metadata"]))),
cursor.fetchone())
+
+
+def group_resource(conn: db.DbConnection, group_id: UUID) -> Resource:
+ """Retrieve the system resource."""
+ with db.cursor(conn) as cursor:
+ cursor.execute(
+ "SELECT group_resources.group_id, resource_categories.*, "
+ "resources.resource_id, resources.resource_name, resources.public "
+ "FROM group_resources INNER JOIN resources "
+ "ON group_resources.resource_id=resources.resource_id "
+ "INNER JOIN resource_categories "
+ "ON resources.resource_category_id=resource_categories.resource_category_id "
+ "WHERE group_resources.group_id=? "
+ "AND resource_categories.resource_category_key='group'",
+ (str(group_id),))
+ row = cursor.fetchone()
+ if row:
+ return resource_from_dbrow(row)
+
+ raise NotFoundError("Could not find a resource for group with ID "
+ f"{group_id}")
+
+
+def data_resources(
+ conn: db.DbConnection, group_id: UUID) -> Iterable[Resource]:
+ """Fetch a group's data resources."""
+ with db.cursor(conn) as cursor:
+ cursor.execute(
+ "SELECT resource_ownership.group_id, resources.resource_id, "
+ "resources.resource_name, resources.public, resource_categories.* "
+ "FROM resource_ownership INNER JOIN resources "
+ "ON resource_ownership.resource_id=resources.resource_id "
+ "INNER JOIN resource_categories "
+ "ON resources.resource_category_id=resource_categories.resource_category_id "
+ "WHERE group_id=?",
+ (str(group_id),))
+ yield from (resource_from_dbrow(row) for row in cursor.fetchall())
+
+
+def group_leaders(conn: db.DbConnection, group_id: UUID) -> Iterable[User]:
+ """Fetch all of a group's group leaders."""
+ with db.cursor(conn) as cursor:
+ cursor.execute(
+ "SELECT users.* FROM group_users INNER JOIN group_resources "
+ "ON group_users.group_id=group_resources.group_id "
+ "INNER JOIN user_roles "
+ "ON group_resources.resource_id=user_roles.resource_id "
+ "INNER JOIN roles "
+ "ON user_roles.role_id=roles.role_id "
+ "INNER JOIN users "
+ "ON user_roles.user_id=users.user_id "
+ "WHERE group_users.group_id=? "
+ "AND roles.role_name='group-leader'",
+ (str(group_id),))
+ yield from (User.from_sqlite3_row(row) for row in cursor.fetchall())
diff --git a/gn_auth/auth/authorisation/resources/groups/views.py b/gn_auth/auth/authorisation/resources/groups/views.py
index 368284f..28f0645 100644
--- a/gn_auth/auth/authorisation/resources/groups/views.py
+++ b/gn_auth/auth/authorisation/resources/groups/views.py
@@ -22,12 +22,22 @@ from gn_auth.auth.authentication.users import User
from gn_auth.auth.authentication.oauth2.resource_server import require_oauth
from .data import link_data_to_group
-from .models import (
- Group, user_group, all_groups, DUMMY_GROUP, GroupRole, group_by_id,
- join_requests, group_role_by_id, GroupCreationError,
- accept_reject_join_request, group_users as _group_users,
- create_group as _create_group, add_privilege_to_group_role,
- delete_privilege_from_group_role)
+from .models import (Group,
+ GroupRole,
+ user_group,
+ all_groups,
+ DUMMY_GROUP,
+ group_by_id,
+ group_leaders,
+ join_requests,
+ data_resources,
+ group_role_by_id,
+ GroupCreationError,
+ accept_reject_join_request,
+ add_privilege_to_group_role,
+ group_users as _group_users,
+ create_group as _create_group,
+ delete_privilege_from_group_role)
groups = Blueprint("groups", __name__)
@@ -35,11 +45,31 @@ groups = Blueprint("groups", __name__)
@require_oauth("profile group")
def list_groups():
"""Return the list of groups that exist."""
+ _kwargs = request_json()
+ def __add_total_group_count__(groups_info):
+ return {
+ "groups": groups_info[0],
+ "total-groups": groups_info[1],
+ "total-filtered": groups_info[2]
+ }
+
with db.connection(current_app.config["AUTH_DB"]) as conn:
- the_groups = all_groups(conn)
+ return jsonify(all_groups(
+ conn,
+ search=_kwargs.get("search"),
+ start=int(_kwargs.get("start", "0")),
+ length=int(_kwargs.get("length", "0"))
+ ).then(
+ __add_total_group_count__
+ ).maybe(
+ {
+ "groups": [],
+ "message": "No groups found!",
+ "total-groups": 0,
+ "total-filtered": 0
+ },
+ lambda _grpdata: _grpdata))
- return jsonify(the_groups.maybe(
- [], lambda grps: [asdict(grp) for grp in grps]))
@groups.route("/create", methods=["POST"])
@require_oauth("profile group")
@@ -235,7 +265,7 @@ def unlinked_data(resource_type: str) -> Response:
if resource_type in ("system", "group"):
return jsonify(tuple())
- if resource_type not in ("all", "mrna", "genotype", "phenotype"):
+ if resource_type not in ("all", "mrna", "genotype", "phenotype", "inbredset-group"):
raise AuthorisationError(f"Invalid resource type {resource_type}")
with require_oauth.acquire("profile group resource") as the_token:
@@ -253,7 +283,8 @@ def unlinked_data(resource_type: str) -> Response:
"genotype": unlinked_genotype_data,
"phenotype": lambda conn, grp: partial(
unlinked_phenotype_data, gn3conn=gn3conn)(
- authconn=conn, group=grp)
+ authconn=conn, group=grp),
+ "inbredset-group": lambda authconn, ugroup: [] # Still need to implement this
}
return jsonify(tuple(
dict(row) for row in unlinked_fns[resource_type](
@@ -347,3 +378,33 @@ def delete_priv_from_role(group_role_id: uuid.UUID) -> Response:
direction="DELETE", user=the_token.user))),
"description": "Privilege deleted successfully"
})
+
+
+@groups.route("/<uuid:group_id>", methods=["GET"])
+@require_oauth("profile group")
+def view_group(group_id: uuid.UUID) -> Response:
+ """View a particular group's details."""
+ # TODO: do authorisation checks here…
+ with (require_oauth.acquire("profile group") as _token,
+ db.connection(current_app.config["AUTH_DB"]) as conn):
+ return jsonify(group_by_id(conn, group_id))
+
+
+@groups.route("/<uuid:group_id>/data-resources", methods=["GET"])
+@require_oauth("profile group")
+def view_group_data_resources(group_id: uuid.UUID) -> Response:
+ """View data resources linked to the group."""
+ # TODO: do authorisation checks here…
+ with (require_oauth.acquire("profile group") as _token,
+ db.connection(current_app.config["AUTH_DB"]) as conn):
+ return jsonify(tuple(data_resources(conn, group_id)))
+
+
+@groups.route("/<uuid:group_id>/leaders", methods=["GET"])
+@require_oauth("profile group")
+def view_group_leaders(group_id: uuid.UUID) -> Response:
+ """View a group's leaders."""
+ # TODO: do authorisation checks here…
+ with (require_oauth.acquire("profile group") as _token,
+ db.connection(current_app.config["AUTH_DB"]) as conn):
+ return jsonify(tuple(group_leaders(conn, group_id)))