from uuid import UUID
from flask import (
flash, request, url_for, redirect, Response, Blueprint)
from . import client
from . import session
from .ui import render_ui as _render_ui
from .checks import require_oauth2
from .client import oauth2_get, oauth2_post
from .request_utils import (flash_error,
flash_success,
request_error,
process_error,
with_flash_error,
with_flash_success)
resources = Blueprint("resource", __name__)
def render_ui(template, **kwargs):
return _render_ui(template, uipages="resources", **kwargs)
@resources.route("/", methods=["GET"])
@require_oauth2
def user_resources():
"""List the resources the user has access to."""
def __success__(resources):
return render_ui("oauth2/resources.html", resources=resources)
return oauth2_get("auth/user/resources").either(
request_error, __success__)
@resources.route("/create", methods=["GET", "POST"])
@require_oauth2
def create_resource():
"""Create a new resource."""
def __render_template__(categories=[], error=None):
return render_ui(
"oauth2/create-resource.html",
resource_categories=categories,
resource_category_error=error,
resource_name=request.args.get("resource_name"),
resource_category=request.args.get("resource_category"))
if request.method == "GET":
return oauth2_get("auth/resource/categories").either(
lambda error: __render_template__(error=process_error(
error, "Could not retrieve resource categories")),
lambda cats: __render_template__(categories=cats))
def __perr__(error):
err = process_error(error)
flash(f"{err['error']}: {err['error_description']}", "alert-danger")
return redirect(url_for(
"oauth2.resource.create_resource",
resource_name=request.form.get("resource_name"),
resource_category=request.form.get("resource_category")))
def __psuc__(succ):
flash("Resource created successfully", "alert-success")
return redirect(url_for("oauth2.resource.user_resources"))
return oauth2_post(
"auth/resource/create", json=dict(request.form)).either(
__perr__, __psuc__)
def __compute_page__(submit, current_page):
if submit == "next":
return current_page + 1
return (current_page - 1) or 1
@resources.route("/<uuid:resource_id>/view", methods=["GET"])
@require_oauth2
def view_resource(resource_id: UUID):
"""View the given resource."""
page = __compute_page__(request.args.get("submit"),
int(request.args.get("page", "1"), base=10))
count_per_page = int(request.args.get("count_per_page", "100"), base=10)
def __users_success__(
resource, unlinked_data, users_n_roles, this_user, resource_roles,
users):
return render_ui(
"oauth2/view-resource.html", resource=resource,
unlinked_data=unlinked_data, users_n_roles=users_n_roles,
this_user=this_user, resource_roles=resource_roles, users=users,
page=page, count_per_page=count_per_page)
def __resource_roles_success__(
resource, unlinked_data, users_n_roles, this_user, resource_roles):
return oauth2_get("auth/user/list").either(
lambda err: render_ui(
"oauth2/view-resource.html", resource=resource,
unlinked_data=unlinked_data, users_n_roles=users_n_roles,
this_user=this_user, resource_roles=resource_roles,
users_error=process_error(err), count_per_page=count_per_page),
lambda users: __users_success__(
resource, unlinked_data, users_n_roles, this_user, resource_roles,
users))
def __this_user_success__(resource, unlinked_data, users_n_roles, this_user):
return oauth2_get(f"auth/resource/{resource_id}/roles").either(
lambda err: render_ui(
"oauth2/view-resource.html", resource=resource,
unlinked_data=unlinked_data, users_n_roles=users_n_roles,
this_user=this_user, resource_roles_error=process_error(err),
count_per_page=count_per_page),
lambda rroles: __resource_roles_success__(
resource, unlinked_data, users_n_roles, this_user, rroles))
def __users_n_roles_success__(resource, unlinked_data, users_n_roles):
return oauth2_get("auth/user/").either(
lambda err: render_ui(
"oauth2/view-resource.html",
this_user_error=process_error(err)),
lambda usr_dets: __this_user_success__(
resource, unlinked_data, users_n_roles, usr_dets))
def __unlinked_success__(resource, unlinked_data):
return oauth2_get(f"auth/resource/{resource_id}/user/list").either(
lambda err: render_ui(
"oauth2/view-resource.html",
resource=resource,
unlinked_data=unlinked_data,
users_n_roles_error=process_error(err),
page=page,
count_per_page=count_per_page),
lambda users_n_roles: __users_n_roles_success__(
resource, unlinked_data, users_n_roles))
def __resource_success__(resource):
dataset_type = resource["resource_category"]["resource_category_key"]
return oauth2_get(f"auth/group/{dataset_type}/unlinked-data").either(
lambda err: render_ui(
"oauth2/view-resource.html",
resource=resource,
unlinked_error=process_error(err),
count_per_page=count_per_page),
lambda unlinked: __unlinked_success__(resource, unlinked))
def __fetch_resource_data__(resource):
"""Fetch the resource's data."""
return client.get(
f"auth/resource/view/{resource['resource_id']}/data?page={page}"
f"&count_per_page={count_per_page}").either(
lambda err: {
**resource, "resource_data_error": process_error(err)
},
lambda resdata: {**resource, "resource_data": resdata})
return oauth2_get(f"auth/resource/view/{resource_id}").map(
__fetch_resource_data__).either(
lambda err: render_ui(
"oauth2/view-resource.html",
resource=None, resource_error=process_error(err)),
__resource_success__)
@resources.route("/data/link", methods=["POST"])
@require_oauth2
def link_data_to_resource():
"""Link group data to a resource"""
form = request.form
try:
assert "resource_id" in form, "Resource ID not provided."
assert "data_link_id" in form, "Data Link ID not provided."
assert "dataset_type" in form, "Dataset type not specified"
assert form["dataset_type"].lower() in (
"mrna", "genotype", "phenotype"), "Invalid dataset type provided."
resource_id = form["resource_id"]
def __error__(error):
err = process_error(error)
flash(f"{err['error']}: {err['error_description']}", "alert-danger")
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=resource_id))
def __success__(success):
flash(f"Data linked to resource successfully", "alert-success")
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=resource_id))
return oauth2_post("auth/resource/data/link", json=dict(form)).either(
__error__,
__success__)
except AssertionError as aserr:
flash(aserr.args[0], "alert-danger")
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=form["resource_id"]))
@resources.route("/data/unlink", methods=["POST"])
@require_oauth2
def unlink_data_from_resource():
"""Unlink group data from a resource"""
form = request.form
try:
assert "resource_id" in form, "Resource ID not provided."
assert "data_link_id" in form, "Data Link ID not provided."
resource_id = form["resource_id"]
def __error__(error):
err = process_error(error)
flash(f"{err['error']}: {err['error_description']}", "alert-danger")
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=resource_id))
def __success__(success):
flash(f"Data unlinked from resource successfully", "alert-success")
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=resource_id))
return oauth2_post(
"auth/resource/data/unlink", json=dict(form)).either(
__error__, __success__)
except AssertionError as aserr:
flash(aserr.args[0], "alert-danger")
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=form["resource_id"]))
@resources.route("<uuid:resource_id>/user/assign", methods=["POST"])
@require_oauth2
def assign_role(resource_id: UUID) -> Response:
form = request.form
role_id = form.get("role_id", "")
user_email = form.get("user_email", "")
try:
assert bool(role_id), "The role must be provided."
assert bool(user_email), "The user email must be provided."
def __assign_error__(error):
err = process_error(error)
flash(f"{err['error']}: {err['error_description']}", "alert-danger")
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=resource_id))
def __assign_success__(success):
flash(success["description"], "alert-success")
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=resource_id))
return oauth2_post(
f"auth/resource/{resource_id}/user/assign",
json={
"role_id": role_id,
"user_email": user_email
}).either(__assign_error__, __assign_success__)
except AssertionError as aserr:
flash(aserr.args[0], "alert-danger")
return redirect(url_for("oauth2.resource.view_resource", resource_id=resource_id))
@resources.route("<uuid:resource_id>/user/unassign", methods=["POST"])
@require_oauth2
def unassign_role(resource_id: UUID) -> Response:
form = request.form
role_id = form.get("role_id", "")
user_id = form.get("user_id", "")
try:
assert bool(role_id), "The role must be provided."
assert bool(user_id), "The user id must be provided."
def __unassign_error__(error):
err = process_error(error)
flash(f"{err['error']}: {err['error_description']}", "alert-danger")
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=resource_id))
def __unassign_success__(success):
flash(success["description"], "alert-success")
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=resource_id))
return oauth2_post(
f"auth/resource/{resource_id}/user/unassign",
json={
"role_id": role_id,
"user_id": user_id
}).either(__unassign_error__, __unassign_success__)
except AssertionError as aserr:
flash(aserr.args[0], "alert-danger")
return redirect(url_for("oauth2.resource.view_resource", resource_id=resource_id))
@resources.route("/toggle/<uuid:resource_id>", methods=["POST"])
@require_oauth2
def toggle_public(resource_id: UUID):
"""Toggle the given resource's public status."""
def __handle_error__(err):
flash_error(process_error(err))
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=resource_id))
def __handle_success__(success):
flash_success(success)
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=resource_id))
return oauth2_post(
f"auth/resource/{resource_id}/toggle-public").either(
lambda err: __handle_error__(err),
lambda suc: __handle_success__(suc))
@resources.route("/edit/<uuid:resource_id>", methods=["GET"])
@require_oauth2
def edit_resource(resource_id: UUID):
"""Edit the given resource."""
return "WOULD Edit THE GIVEN RESOURCE'S DETAILS"
@resources.route("/delete/<uuid:resource_id>", methods=["GET"])
@require_oauth2
def delete_resource(resource_id: UUID):
"""Delete the given resource."""
return "WOULD DELETE THE GIVEN RESOURCE"
@resources.route("/<uuid:resource_id>/roles/<uuid:role_id>", methods=["GET"])
@require_oauth2
def view_resource_role(resource_id: UUID, role_id: UUID):
"""View resource role page."""
def __render_template__(**kwargs):
return render_ui("oauth2/view-resource-role.html", **kwargs)
def __fetch_users__(resource, role, unassigned_privileges):
return oauth2_get(
f"auth/resource/{resource_id}/role/{role_id}/users").either(
lambda error: __render_template__(
resource=resource,
role=role,
unassigned_privileges=unassigned_privileges,
user_error=process_error(error)),
lambda users: __render_template__(
resource=resource,
role=role,
unassigned_privileges=unassigned_privileges,
users=users))
def __fetch_all_roles__(resource, role):
return oauth2_get(f"auth/resource/{resource_id}/roles").either(
lambda error: __render_template__(
all_roles_error=process_error(error)),
lambda all_roles: __fetch_users__(
resource=resource,
role=role,
unassigned_privileges=[
priv for role in all_roles
for priv in role["privileges"]
if priv not in role["privileges"]
]))
def __fetch_resource_role__(resource):
return oauth2_get(
f"auth/resource/{resource_id}/role/{role_id}").either(
lambda error: __render_template__(
resource=resource,
role_id=role_id,
role_error=process_error(error)),
lambda role: __fetch_all_roles__(resource, role))
return oauth2_get(
f"auth/resource/view/{resource_id}").either(
lambda error: __render_template__(
resource_error=process_error(error)),
lambda resource: __fetch_resource_role__(resource=resource))
@resources.route("/<uuid:resource_id>/roles/<uuid:role_id>/unassign-privilege",
methods=["GET", "POST"])
@require_oauth2
def unassign_privilege_from_resource_role(resource_id: UUID, role_id: UUID):
"""Remove a privilege from a resource role."""
form = request.form
returnto = redirect(url_for("oauth2.resource.view_resource_role",
resource_id=resource_id,
role_id=role_id))
privilege_id = (request.args.get("privilege_id")
or form.get("privilege_id"))
if not privilege_id:
flash("You need to specify a privilege to unassign.", "alert-danger")
return returnto
if request.method=="POST" and form.get("confirm") == "Unassign":
return oauth2_post(
f"auth/resource/{resource_id}/role/{role_id}/unassign-privilege",
json={
"privilege_id": form["privilege_id"]
}).either(with_flash_error(returnto), with_flash_success(returnto))
if form.get("confirm") == "Cancel":
flash("Cancelled the operation to unassign the privilege.",
"alert-info")
return returnto
def __fetch_privilege__(resource, role):
return oauth2_get(
f"auth/privileges/{privilege_id}/view").either(
with_flash_error(returnto),
lambda privilege: render_ui(
"oauth2/confirm-resource-role-unassign-privilege.html",
resource=resource,
role=role,
privilege=privilege))
def __fetch_resource_role__(resource):
return oauth2_get(
f"auth/resource/{resource_id}/role/{role_id}").either(
with_flash_error(returnto),
lambda role: __fetch_privilege__(resource, role))
return oauth2_get(
f"auth/resource/view/{resource_id}").either(
with_flash_error(returnto),
__fetch_resource_role__)
@resources.route("/<uuid:resource_id>/roles/create-role",
methods=["GET", "POST"])
@require_oauth2
def create_resource_role(resource_id: UUID):
"""Create new role for the resource."""
def __render__(**kwargs):
return render_ui("oauth2/create-role.html", **kwargs)
def __fetch_resource_roles__(resource):
user = session.session_info()["user"]
return oauth2_get(
f"auth/resource/{resource_id}/users/{user['user_id']}"
"/roles").either(
lambda error: {
"resource": resource,
"resource_role_error": process_error(error)
},
lambda roles: {"resource": resource, "roles": roles})
if request.method == "GET":
return oauth2_get(f"auth/resource/view/{resource_id}").map(
__fetch_resource_roles__).either(
lambda error: __render__(resource_error=error),
lambda kwargs: __render__(**kwargs))
formdata = request.form
privileges = formdata.getlist("privileges[]")
if not bool(privileges):
flash(
"You must provide at least one privilege for creation of the new "
"role.",
"alert-danger")
return redirect(url_for("oauth2.resource.create_resource_role",
resource_id=resource_id))
def __handle_error__(error):
flash_error(process_error(error))
return redirect(url_for(
"oauth2.resource.create_resource_role", resource_id=resource_id))
def __handle_success__(success):
flash("Role successfully created.", "alert-success")
return redirect(url_for(
"oauth2.resource.view_resource", resource_id=resource_id))
return oauth2_post(
f"auth/resource/{resource_id}/roles/create",
json={
"role_name": formdata["role_name"],
"privileges": privileges
}).either(
__handle_error__, __handle_success__)