aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2023-09-12 07:24:31 +0300
committerFrederick Muriuki Muriithi2023-09-26 03:44:28 +0300
commita49f925eaaff5b29ffc387a7f02023e111208d5f (patch)
tree84979b47f93ea287a796f8beb1c9377fad02d845
parent6e515bd019c5ac6c4c224cd85805e2c608f41c69 (diff)
downloadgn-auth-a49f925eaaff5b29ffc387a7f02023e111208d5f.tar.gz
Fix foreign-key refs. Merge tables.
* Link the `role_id` field to the `roles` table rather than the `group_roles` table. * Merge the data in the `group_user_roles_on_resources` table in the `user_roles` table to have a single point-of-truth for all user roles on resources.
-rw-r--r--migrations/auth/20230912_02_hFmSn-drop-group-id-and-fix-foreign-key-references-on-group-user-roles-on-resources-table.py227
1 files changed, 227 insertions, 0 deletions
diff --git a/migrations/auth/20230912_02_hFmSn-drop-group-id-and-fix-foreign-key-references-on-group-user-roles-on-resources-table.py b/migrations/auth/20230912_02_hFmSn-drop-group-id-and-fix-foreign-key-references-on-group-user-roles-on-resources-table.py
new file mode 100644
index 0000000..1b3f0b1
--- /dev/null
+++ b/migrations/auth/20230912_02_hFmSn-drop-group-id-and-fix-foreign-key-references-on-group-user-roles-on-resources-table.py
@@ -0,0 +1,227 @@
+"""
+Drop 'group_id' and fix foreign key references on 'group_user_roles_on_resources' table
+"""
+
+import sqlite3
+from yoyo import step
+
+__depends__ = {'20230912_01_BxrhE-add-system-resource'}
+
+def drop_group_id(conn):
+ """Drop `group_id` from `group_user_roles_on_resources` table."""
+ conn.execute("PRAGMA foreign_keys = OFF")
+
+ conn.execute(
+ """
+ ALTER TABLE group_user_roles_on_resources
+ RENAME TO group_user_roles_on_resources_bkp
+ """)
+ conn.execute(
+ """
+ CREATE TABLE IF NOT EXISTS group_user_roles_on_resources (
+ user_id TEXT NOT NULL,
+ role_id TEXT NOT NULL,
+ resource_id TEXT NOT NULL,
+ PRIMARY KEY (user_id, role_id, resource_id),
+ FOREIGN KEY (user_id)
+ REFERENCES users(user_id)
+ ON UPDATE CASCADE ON DELETE RESTRICT,
+ FOREIGN KEY (role_id)
+ REFERENCES roles(role_id)
+ ON UPDATE CASCADE ON DELETE RESTRICT,
+ FOREIGN KEY (resource_id)
+ REFERENCES resources(resource_id)
+ ON UPDATE CASCADE ON DELETE RESTRICT
+ ) WITHOUT ROWID
+ """)
+ conn.execute(
+ "INSERT INTO group_user_roles_on_resources "
+ "SELECT user_id, role_id, resource_id "
+ "FROM group_user_roles_on_resources_bkp")
+ conn.execute("DROP TABLE IF EXISTS group_user_roles_on_resources_bkp")
+
+ conn.execute("PRAGMA foreign_key_check")
+ conn.execute("PRAGMA foreign_keys = ON")
+
+def restore_group_id(conn):
+ """Restore `group_id` to `group_user_roles_on_resources` table."""
+ conn.execute("PRAGMA foreign_keys = OFF")
+
+ conn.execute(
+ """
+ ALTER TABLE group_user_roles_on_resources
+ RENAME TO group_user_roles_on_resources_bkp
+ """)
+ conn.execute(
+ """
+ CREATE TABLE IF NOT EXISTS group_user_roles_on_resources (
+ group_id TEXT NOT NULL,
+ user_id TEXT NOT NULL,
+ role_id TEXT NOT NULL,
+ resource_id TEXT NOT NULL,
+ PRIMARY KEY (group_id, user_id, role_id, resource_id),
+ FOREIGN KEY (user_id)
+ REFERENCES users(user_id)
+ ON UPDATE CASCADE ON DELETE RESTRICT,
+ FOREIGN KEY (group_id, role_id)
+ REFERENCES group_roles(group_id, role_id)
+ ON UPDATE CASCADE ON DELETE RESTRICT,
+ FOREIGN KEY (resource_id)
+ REFERENCES resources(resource_id)
+ ON UPDATE CASCADE ON DELETE RESTRICT
+ ) WITHOUT ROWID
+ """)
+
+ cursor = conn.cursor()
+ cursor.execute(
+ """
+ INSERT INTO group_user_roles_on_resources
+ SELECT
+ ro.group_id, gurorb.user_id, gurorb.role_id, gurorb.resource_id
+ FROM resource_ownership AS ro
+ INNER JOIN group_user_roles_on_resources_bkp AS gurorb
+ ON ro.resource_id=gurorb.resource_id
+ """)
+
+ conn.execute("DROP TABLE IF EXISTS group_user_roles_on_resources_bkp")
+
+ conn.execute("PRAGMA foreign_key_check")
+ conn.execute("PRAGMA foreign_keys = ON")
+
+def link_sys_admin_user_roles(conn):
+ """Link system-admins to the system resource."""
+ conn.row_factory = sqlite3.Row
+ cursor = conn.cursor()
+ cursor.execute(
+ "SELECT ur.* FROM user_roles AS ur "
+ "INNER JOIN roles AS r ON ur.role_id=r.role_id "
+ "WHERE r.role_name='system-administrator'")
+ admins = cursor.fetchall()
+ cursor.execute(
+ "SELECT r.resource_id FROM resources AS r "
+ "INNER JOIN resource_categories AS rc "
+ "ON r.resource_category_id=rc.resource_category_id "
+ "WHERE rc.resource_category_key='system'")
+ system_resource_id = cursor.fetchone()["resource_id"]
+ cursor.executemany(
+ "INSERT INTO "
+ "group_user_roles_on_resources(user_id, role_id, resource_id) "
+ "VALUES (:user_id, :role_id, :resource_id)",
+ tuple({**admin, "resource_id": system_resource_id} for admin in admins))
+
+def restore_sys_admin_user_roles(conn):
+ """Restore fields into older `user_roles` table."""
+ conn.row_factory = sqlite3.Row
+ cursor = conn.cursor()
+ cursor.execute(
+ "SELECT guror.user_id, guror.role_id "
+ "FROM group_user_roles_on_resources AS guror "
+ "INNER JOIN resources AS r "
+ "ON guror.resource_id=r.resource_id "
+ "INNER JOIN resource_categories AS rc "
+ "ON r.resource_category_id=rc.resource_category_id "
+ "WHERE rc.resource_category_key='system'")
+ user_roles = tuple(cursor.fetchall())
+ cursor.executemany(
+ "INSERT INTO user_roles(user_id, role_id) "
+ "VALUES (:user_id, :role_id)",
+ user_roles)
+
+def link_group_leader_user_roles(conn):
+ """Link group leaders to their resources."""
+ conn.execute(
+ """
+ INSERT INTO group_user_roles_on_resources(user_id, role_id, resource_id)
+ SELECT gu.user_id, r.role_id, gr.resource_id
+ FROM group_resources AS gr INNER JOIN group_users AS gu
+ ON gr.group_id=gu.group_id INNER JOIN user_roles AS ur
+ ON gu.user_id=ur.user_id INNER JOIN roles AS r
+ ON ur.role_id=r.role_id
+ WHERE r.role_name='group-leader'
+ """)
+
+def restore_group_leader_user_roles(conn):
+ """Restore group admins to older `user_roles` table."""
+ conn.execute(
+ """
+ INSERT INTO user_roles(user_id, role_id)
+ SELECT guror.user_id, guror.role_id
+ FROM group_user_roles_on_resources AS guror
+ INNER JOIN resources AS r ON guror.resource_id=r.resource_id
+ INNER JOIN resource_categories AS rc
+ ON r.resource_category_id=rc.resource_category_id
+ WHERE rc.resource_category_key='group'
+ """)
+
+def link_group_creator_user_roles(conn):
+ """Link group-creators to system."""
+ conn.row_factory = sqlite3.Row
+ cursor = conn.cursor()
+ cursor.execute(
+ "SELECT ur.* FROM user_roles AS ur "
+ "INNER JOIN roles AS r ON ur.role_id=r.role_id "
+ "WHERE r.role_name='group_creator'")
+ creators = cursor.fetchall()
+ cursor.execute(
+ "SELECT r.resource_id FROM resources AS r "
+ "INNER JOIN resource_categories AS rc "
+ "ON r.resource_category_id=rc.resource_category_id "
+ "WHERE rc.resource_category_key='system'")
+ sys_res_id = cursor.fetchone()["resource_id"]
+ cursor.executemany(
+ "INSERT INTO "
+ "group_user_roles_on_resources(user_id, role_id, resource_id) "
+ "VALUES (:user_id, :role_id, :resource_id)",
+ tuple({**creator, "resource_id": sys_res_id} for creator in creators))
+
+def restore_group_creator_user_roles(conn):
+ "Restore group-creator user roles."
+ conn.execute(
+ """
+ INSERT INTO user_roles
+ SELECT guror.user_id, guror.role_id
+ FROM group_user_roles_on_resources AS guror
+ INNER JOIN roles AS r ON guror.role_id=r.role_id
+ WHERE r.role_name='group-creator'""")
+
+def rename_table(conn):
+ "rename `group_user_roles_on_resources`, drop `user_roles`."
+ conn.execute("PRAGMA foreign_keys = OFF")
+
+ conn.execute("DROP TABLE IF EXISTS user_roles")
+ conn.execute(
+ "ALTER TABLE group_user_roles_on_resources RENAME TO user_roles")
+
+ conn.execute("PRAGMA foreign_key_check")
+ conn.execute("PRAGMA foreign_keys = ON")
+
+def restore_tables(conn):
+ "rename to `group_user_roles_on_resources`, recreate original `user_roles`."
+ conn.execute("PRAGMA foreign_keys = OFF")
+
+ conn.execute(
+ "ALTER TABLE user_roles RENAME TO group_user_roles_on_resources")
+ conn.execute(
+ """
+ CREATE TABLE user_roles(
+ user_id TEXT NOT NULL,
+ role_id TEXT NOT NULL,
+ PRIMARY KEY(user_id, role_id),
+ FOREIGN KEY(user_id) REFERENCES users(user_id)
+ ON UPDATE CASCADE ON DELETE RESTRICT,
+ FOREIGN KEY(role_id) REFERENCES roles(role_id)
+ ON UPDATE CASCADE ON DELETE RESTRICT
+ ) WITHOUT ROWID
+ """)
+
+ conn.execute("PRAGMA foreign_key_check")
+ conn.execute("PRAGMA foreign_keys = ON")
+
+steps = [
+ step(drop_group_id, restore_group_id),
+ step(link_sys_admin_user_roles, restore_sys_admin_user_roles),
+ step(link_group_leader_user_roles, restore_group_leader_user_roles),
+ step(link_group_creator_user_roles, restore_group_creator_user_roles),
+ step(rename_table, restore_tables)
+]
+