aboutsummaryrefslogtreecommitdiff
path: root/gn3/auth/authorisation/users
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2023-05-11 09:51:52 +0300
committerFrederick Muriuki Muriithi2023-05-11 10:10:27 +0300
commitfe7670fbad187d81e9c395d03d4b3c69a0a6a1f3 (patch)
treecd6c4a0bb782f9454b27528ec36438cf30ba04a4 /gn3/auth/authorisation/users
parente373450236e05a4be79b42e99615af20e8b8e536 (diff)
downloadgenenetwork3-fe7670fbad187d81e9c395d03d4b3c69a0a6a1f3.tar.gz
auth: Fix bug with migration of user collections
The code, as written previously had a subtle bug - if the user created a new collection before they had tried accessing their list of collections, the older code would not have migrated the older collections. This commit fixes that by enabling the migration of older collections, whether or not the user has created a collection with their new accounts.
Diffstat (limited to 'gn3/auth/authorisation/users')
-rw-r--r--gn3/auth/authorisation/users/collections.py83
-rw-r--r--gn3/auth/authorisation/users/views.py6
2 files changed, 67 insertions, 22 deletions
diff --git a/gn3/auth/authorisation/users/collections.py b/gn3/auth/authorisation/users/collections.py
index 9ddc138..2e2672c 100644
--- a/gn3/auth/authorisation/users/collections.py
+++ b/gn3/auth/authorisation/users/collections.py
@@ -1,30 +1,77 @@
"""Handle user collections."""
+import uuid
import json
from redis import Redis
+from email_validator import validate_email, EmailNotValidError
from .models import User
+def __valid_email__(email:str) -> bool:
+ """Check for email validity."""
+ try:
+ validate_email(email, check_deliverability=True)
+ except EmailNotValidError as _enve:
+ return False
+ return True
+
+def __toggle_boolean_field__(
+ rconn: Redis, email: str, field: str):
+ """Toggle the valuen of a boolean field"""
+ mig_dict = json.loads(rconn.hget("migratable-accounts", email) or "{}")
+ if bool(mig_dict):
+ rconn.hset("migratable-accounts", email,
+ {**mig_dict, field: not mig_dict.get(field, True)})
+
+def __build_email_uuid_bridge__(rconn: Redis):
+ """
+ Build a connection between new accounts and old user accounts.
+
+ The only thing that is common between the two is the email address,
+ therefore, we use that to link the two items.
+ """
+ old_accounts = {
+ account["email_address"]: {
+ "user_id": account["user_id"],
+ "collections-migrated": False,
+ "resources_migrated": False
+ } for account in (
+ acct for acct in
+ (json.loads(usr) for usr in rconn.hgetall("users").values())
+ if (bool(acct.get("email_address", False)) and
+ __valid_email__(acct["email_address"])))
+ }
+ if bool(old_accounts):
+ rconn.hset("migratable-accounts", mapping={
+ key: json.dumps(value) for key,value in old_accounts.items()
+ })
+ return old_accounts
+
+def __retrieve_old_accounts__(rconn: Redis) -> dict:
+ accounts = rconn.hgetall("migratable-accounts")
+ if accounts:
+ return {
+ key: json.loads(value) for key, value in accounts.items()
+ }
+ return __build_email_uuid_bridge__(rconn)
+
+def __retrieve_old_user_collections__(rconn: Redis, old_user_id: uuid.UUID) -> tuple:
+ """Retrieve any old collections relating to the user."""
+ return tuple(json.loads(rconn.hget("collections", old_user_id) or "[]"))
+
def user_collections(rconn: Redis, user: User) -> tuple:
"""Retrieve current user collections."""
- return tuple(json.loads(
+ collections = tuple(json.loads(
rconn.hget("collections", str(user.user_id)) or
"[]"))
-
-def old_user_collections(rconn: Redis, user: User) -> tuple:
- """
- Retrieve any old user collections and migrate them to new account.
- """
- collections = user_collections(rconn, user)
- old_user_accounts = [
- acct for acct in
- (json.loads(usr) for usr in rconn.hgetall("users").values())
- if acct.get("email_address", "") == user.email]
- for account in old_user_accounts:
- collections = collections + tuple(json.loads(
- rconn.hget("collections", account["user_id"]) or "[]"))
- rconn.hdel("collections", account["user_id"])
-
- rconn.hset(
- "collections", key=str(user.user_id), value=json.dumps(collections))
+ old_accounts = __retrieve_old_accounts__(rconn)
+ if (user.email in old_accounts and
+ not old_accounts[user.email]["collections-migrated"]):
+ old_user_id = old_accounts[user.email]["user_id"]
+ collections = tuple(set(collections + __retrieve_old_user_collections__(
+ rconn, uuid.UUID(old_user_id))))
+ rconn.hdel("collections", old_user_id)
+ __toggle_boolean_field__(rconn, user.email, "collections-migrated")
+ rconn.hset(
+ "collections", key=user.user_id, value=json.dumps(collections))
return collections
diff --git a/gn3/auth/authorisation/users/views.py b/gn3/auth/authorisation/users/views.py
index 0a82de3..2b4230e 100644
--- a/gn3/auth/authorisation/users/views.py
+++ b/gn3/auth/authorisation/users/views.py
@@ -13,7 +13,7 @@ from gn3.auth.dictify import dictify
from gn3.auth.db_utils import with_db_connection
from .models import list_users
-from .collections import user_collections, old_user_collections
+from .collections import user_collections
from ..groups.models import user_group as _user_group
from ..resources.models import user_resources as _user_resources
@@ -180,6 +180,4 @@ def list_user_collections() -> Response:
with (require_oauth.acquire("profile user") as the_token,
Redis.from_url(current_app.config["REDIS_URI"],
decode_responses=True) as redisconn):
- return jsonify(
- user_collections(redisconn, the_token.user) or
- old_user_collections(redisconn, the_token.user))
+ return jsonify(user_collections(redisconn, the_token.user))