From dc8fdfdee59136b2b324042622ed012b296e4fa9 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Thu, 9 Mar 2023 05:23:39 +0300 Subject: auth: data migration: Data migration outline Provide an outline of the data migration steps to be taken from some results of the experiments with the data in redis. --- gn3/auth/authorisation/data/views.py | 49 ++++++++++++++++++++++++++++++++++- gn3/auth/authorisation/users/views.py | 5 ++-- gn3/settings.py | 11 ++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/gn3/auth/authorisation/data/views.py b/gn3/auth/authorisation/data/views.py index 08032db..89898c6 100644 --- a/gn3/auth/authorisation/data/views.py +++ b/gn3/auth/authorisation/data/views.py @@ -2,17 +2,21 @@ import uuid import json +from email_validator import validate_email, EmailNotValidError from authlib.integrations.flask_oauth2.errors import _HTTPException from flask import request, jsonify, Response, Blueprint, current_app as app from gn3.db.traits import build_trait_name from gn3.auth import db -from gn3.auth.authentication.oauth2.resource_server import require_oauth +from gn3.auth.authorisation.users.views import validate_password from gn3.auth.authorisation.resources.checks import authorised_for +from gn3.auth.authorisation.errors import ForbiddenAccess, AuthorisationError from gn3.auth.authorisation.resources.models import ( user_resources, public_resources, attach_resources_data) +from gn3.auth.authentication.oauth2.resource_server import require_oauth + data = Blueprint("data", __name__) @data.route("/authorisation", methods=["GET"]) @@ -83,3 +87,46 @@ def authorisation() -> Response: } for trait in (build_trait_name(trait_fullname) for trait_fullname in traits_names))) + +@data.route("/user/migrate", methods=["POST"]) +@require_oauth("migrate-data") +def migrate_user_data(): + """ + Special, protected endpoint to enable the migration of data from the older + system to the newer system with groups, resources and privileges. + + This is a temporary endpoint and should be removed after all the data has + been migrated. + """ + authorised_clients = app.config.get( + "OAUTH2_CLIENTS_WITH_DATA_MIGRATION_PRIVILEGE", []) + with require_oauth.acquire("migrate-data") as the_token: + if the_token.client.client_id in authorised_clients: + try: + _user_id = uuid.UUID(request.form.get("user_id", "")) + _email = validate_email(request.form.get("email", "")) + _password = validate_password( + request.form.get("password", ""), + request.form.get("confirm_password", "")) + ## TODO: Save the user: possible exception for duplicate emails + ## Create group from user's name + ## Filter all resources from redis owned by this user + ## resources = {key: json.loads(val) + ## for key,val + ## in rconn.hgetall("resources").items()} + ## filtered = dict(( + ## (key,val) for key,val + ## in resources.items() + ## if uuid.UUID(val.get("owner_id")) == user_id)) + ## Check that no resource is owned by existing user, use + ## 'name' and 'type' fields to check in + ## `linked_group_data` table + ## Link remaining data to the new group + ## Delete user from redis + return "WOULD TRIGGER DATA MIGRATION ..." + except EmailNotValidError as enve: + raise AuthorisationError(f"Email Error: {str(enve)}") from enve + except ValueError as verr: + raise AuthorisationError(verr.args[0]) from verr + + raise ForbiddenAccess("You cannot access this endpoint.") diff --git a/gn3/auth/authorisation/users/views.py b/gn3/auth/authorisation/users/views.py index 5015cac..e3901b4 100644 --- a/gn3/auth/authorisation/users/views.py +++ b/gn3/auth/authorisation/users/views.py @@ -51,7 +51,8 @@ def user_roles() -> Response: return jsonify(tuple( dictify(role) for role in _user_roles(conn, token.user))) -def __valid_password__(password, confirm_password) -> str: +def validate_password(password, confirm_password) -> str: + """Validate the provided password.""" if len(password) < 8: raise PasswordError("The password must be at least 8 characters long.") @@ -85,7 +86,7 @@ def register_user() -> Response: form = request.form email = validate_email(form.get("email", "").strip(), check_deliverability=True) - password = __valid_password__( + password = validate_password( form.get("password", "").strip(), form.get("confirm_password", "").strip()) user_name = __valid_username__(form.get("user_name", "").strip()) diff --git a/gn3/settings.py b/gn3/settings.py index 1b4a105..22bdc98 100644 --- a/gn3/settings.py +++ b/gn3/settings.py @@ -81,3 +81,14 @@ try: "OAUTH2_CLIENTS_WITH_INTROSPECTION_PRIVILEGE", "").split(",")) except ValueError as _valerr: OAUTH2_CLIENTS_WITH_INTROSPECTION_PRIVILEGE = tuple() + +try: + # *** SECURITY CONCERN *** + # Clients with access to this privileges create a security concern. + # Be careful when adding to this configuration + OAUTH2_CLIENTS_WITH_DATA_MIGRATION_PRIVILEGE = tuple( + uuid.UUID(client_id) for client_id in + os.environ.get( + "OAUTH2_CLIENTS_WITH_DATA_MIGRATION_PRIVILEGE", "").split(",")) +except ValueError as _valerr: + OAUTH2_CLIENTS_WITH_DATA_MIGRATION_PRIVILEGE = tuple() -- cgit v1.2.3