From 6568edd6bf616a0c17c5226f40b494a6b2967dad Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Thu, 21 May 2026 13:32:56 -0500 Subject: Move migrations to top-level gn_auth package. In preparation for migrating to pyproject.toml (from setup.py and friends) we need to have only one top-level package. This will also help in improving testing and checks down the line, since everything will be relative to one single top-level directory. --- gn_auth/migrations.py | 33 --- gn_auth/migrations/__init__.py | 34 +++ ...nitialise-the-auth-entic-oris-ation-database.py | 19 ++ ...21103_02_sGrIs-create-user-credentials-table.py | 20 ++ .../20221108_01_CoxYh-create-the-groups-table.py | 19 ++ .../20221108_02_wxTr9-create-privileges-table.py | 18 ++ ...08_03_Pbhb1-create-resource-categories-table.py | 19 ++ ...CKcSL-init-data-in-resource-categories-table.py | 25 ++ ...urce-meta-field-to-resource-categories-field.py | 17 ++ .../20221110_01_WtZ1I-create-resources-table.py | 26 ++ .../auth/20221110_05_BaNtL-create-roles-table.py | 19 ++ ...20221110_06_Pq2kT-create-generic-roles-table.py | 24 ++ ...221110_07_7WGa1-create-role-privileges-table.py | 29 ++ ...lege-description-columns-to-privileges-table.py | 22 ++ ...221113_01_7M0hv-enumerate-initial-privileges.py | 66 +++++ ...1_n8gsF-create-generic-role-privileges-table.py | 35 +++ .../20221114_02_DKKjn-drop-generic-role-tables.py | 41 +++ .../20221114_03_PtWjc-create-group-roles-table.py | 29 ++ .../20221114_04_tLUzB-initialise-basic-roles.py | 56 ++++ .../20221114_05_hQun6-create-user-roles-table.py | 29 ++ ...01_nKUmX-add-privileges-to-group-leader-role.py | 35 +++ ...1_RDlfx-modify-group-roles-add-group-role-id.py | 52 ++++ .../20221117_02_fmuZh-create-group-users-table.py | 25 ++ ...9-create-group-user-roles-on-resources-table.py | 39 +++ ...1_sSdHz-add-public-column-to-resources-table.py | 16 + ...0221219_01_CI3tN-create-oauth2-clients-table.py | 25 ++ ...20221219_02_buSEU-create-oauth2-tokens-table.py | 31 ++ ...219_03_PcTrb-create-authorisation-code-table.py | 31 ++ ...ove-create-group-privilege-from-group-leader.py | 40 +++ .../20230116_01_KwuJ3-rework-privileges-schema.py | 111 +++++++ ...07_01_r0bkZ-create-group-join-requests-table.py | 29 ++ ...ystem-admin-privileges-for-data-distribution.py | 22 ++ .../20230210_02_lDK14-create-system-admin-role.py | 38 +++ ...0306_01_pRfxl-add-system-user-list-privilege.py | 26 ++ ...-system-administrator-and-group-leader-roles.py | 42 +++ ..._01_0dDZR-create-linked-phenotype-data-table.py | 30 ++ ...22_02_Ll854-create-phenotype-resources-table.py | 29 ++ ...4_01_VKxXg-create-linked-genotype-data-table.py | 29 ++ ...404_02_la33P-create-genotype-resources-table.py | 29 ++ ...30410_01_8mwaf-create-linked-mrna-data-table.py | 30 ++ ...0230410_02_WZqSf-create-mrna-resources-table.py | 28 ++ ..._pjnxz-refactor-add-resource-ownership-table.py | 32 ++ ...tor-add-system-and-group-resource-categories.py | 29 ++ ...-refactor-drop-group-id-from-resources-table.py | 325 +++++++++++++++++++++ ..._3LnrG-refactor-create-group-resources-table.py | 58 ++++ .../auth/20230912_01_BxrhE-add-system-resource.py | 39 +++ ...ences-on-group-user-roles-on-resources-table.py | 227 ++++++++++++++ .../20230925_01_TWJuR-add-new-public-view-role.py | 61 ++++ ...1002_01_tzxTf-link-inbredsets-to-auth-system.py | 84 ++++++ ..._CS8NZ-create-new-inbredset-group-owner-role.py | 40 +++ ...506_01_798tW-create-jwt-refresh-tokens-table.py | 34 +++ ...01_ALNWj-update-schema-for-user-verification.py | 64 ++++ ...ipulation-privileges-from-group-to-resources.py | 94 ++++++ ...0240606_02_ubZri-create-resource-roles-table.py | 36 +++ .../20240606_03_BY7Us-drop-group-roles-table.py | 35 +++ ...01_p2vXR-create-forgot-password-tokens-table.py | 26 ++ .../20240924_01_thbvh-hooks-for-edu-domains.py | 24 ++ ...in-ui-privilege-to-system-administrator-role.py | 42 +++ .../20250609_01_LB60X-add-batch-edit-privileges.py | 49 ++++ ...l-add-new-group-data-link-to-group-privilege.py | 19 ++ ...data-link-to-group-privilege-to-group-leader.py | 23 ++ ...e-management-privileges-to-group-leader-role.py | 27 ++ ..._7Gro7-create-new-system-user-edit-privilege.py | 18 ++ ...tem-user-edit-privilege-to-system-admin-role.py | 36 +++ ...tial-system-wide-resources-access-privileges.py | 31 ++ ...de-resources-access-privileges-to-sys-admins.py | 53 ++++ ...ant-role-to-all-resources-to-sys-admin-users.py | 75 +++++ ...dmin-privileges-for-acting-on-groups-members.py | 70 +++++ ...06_01_v3f4P-add-role-systemwide-data-curator.py | 61 ++++ ...-privilege-for-gn-docs-documentation-editing.py | 62 ++++ ...ign-systemwide-docs-editor-role-to-sysadmins.py | 66 +++++ ...rict-access-to-resources-make-public-feature.py | 49 ++++ ...d-privileges-to-role-systemwide-data-curator.py | 69 +++++ ...dd-user-and-time-tracking-to-resources-table.py | 185 ++++++++++++ ...O-new-privilege-system-system-wide-data-view.py | 19 ++ ...2_L6zIV-add-privileges-to-batch-editors-role.py | 62 ++++ gn_auth/migrations/auth/__init__.py | 1 + gn_auth/settings.py | 2 +- migrations/__init__.py | 1 - ...nitialise-the-auth-entic-oris-ation-database.py | 19 -- ...21103_02_sGrIs-create-user-credentials-table.py | 20 -- .../20221108_01_CoxYh-create-the-groups-table.py | 19 -- .../20221108_02_wxTr9-create-privileges-table.py | 18 -- ...08_03_Pbhb1-create-resource-categories-table.py | 19 -- ...CKcSL-init-data-in-resource-categories-table.py | 25 -- ...urce-meta-field-to-resource-categories-field.py | 17 -- .../20221110_01_WtZ1I-create-resources-table.py | 26 -- .../auth/20221110_05_BaNtL-create-roles-table.py | 19 -- ...20221110_06_Pq2kT-create-generic-roles-table.py | 24 -- ...221110_07_7WGa1-create-role-privileges-table.py | 29 -- ...lege-description-columns-to-privileges-table.py | 22 -- ...221113_01_7M0hv-enumerate-initial-privileges.py | 66 ----- ...1_n8gsF-create-generic-role-privileges-table.py | 35 --- .../20221114_02_DKKjn-drop-generic-role-tables.py | 41 --- .../20221114_03_PtWjc-create-group-roles-table.py | 29 -- .../20221114_04_tLUzB-initialise-basic-roles.py | 56 ---- .../20221114_05_hQun6-create-user-roles-table.py | 29 -- ...01_nKUmX-add-privileges-to-group-leader-role.py | 35 --- ...1_RDlfx-modify-group-roles-add-group-role-id.py | 52 ---- .../20221117_02_fmuZh-create-group-users-table.py | 25 -- ...9-create-group-user-roles-on-resources-table.py | 39 --- ...1_sSdHz-add-public-column-to-resources-table.py | 16 - ...0221219_01_CI3tN-create-oauth2-clients-table.py | 25 -- ...20221219_02_buSEU-create-oauth2-tokens-table.py | 31 -- ...219_03_PcTrb-create-authorisation-code-table.py | 31 -- ...ove-create-group-privilege-from-group-leader.py | 40 --- .../20230116_01_KwuJ3-rework-privileges-schema.py | 111 ------- ...07_01_r0bkZ-create-group-join-requests-table.py | 29 -- ...ystem-admin-privileges-for-data-distribution.py | 22 -- .../20230210_02_lDK14-create-system-admin-role.py | 38 --- ...0306_01_pRfxl-add-system-user-list-privilege.py | 26 -- ...-system-administrator-and-group-leader-roles.py | 42 --- ..._01_0dDZR-create-linked-phenotype-data-table.py | 30 -- ...22_02_Ll854-create-phenotype-resources-table.py | 29 -- ...4_01_VKxXg-create-linked-genotype-data-table.py | 29 -- ...404_02_la33P-create-genotype-resources-table.py | 29 -- ...30410_01_8mwaf-create-linked-mrna-data-table.py | 30 -- ...0230410_02_WZqSf-create-mrna-resources-table.py | 28 -- ..._pjnxz-refactor-add-resource-ownership-table.py | 32 -- ...tor-add-system-and-group-resource-categories.py | 29 -- ...-refactor-drop-group-id-from-resources-table.py | 325 --------------------- ..._3LnrG-refactor-create-group-resources-table.py | 58 ---- .../auth/20230912_01_BxrhE-add-system-resource.py | 39 --- ...ences-on-group-user-roles-on-resources-table.py | 227 -------------- .../20230925_01_TWJuR-add-new-public-view-role.py | 61 ---- ...1002_01_tzxTf-link-inbredsets-to-auth-system.py | 84 ------ ..._CS8NZ-create-new-inbredset-group-owner-role.py | 40 --- ...506_01_798tW-create-jwt-refresh-tokens-table.py | 34 --- ...01_ALNWj-update-schema-for-user-verification.py | 64 ---- ...ipulation-privileges-from-group-to-resources.py | 94 ------ ...0240606_02_ubZri-create-resource-roles-table.py | 36 --- .../20240606_03_BY7Us-drop-group-roles-table.py | 35 --- ...01_p2vXR-create-forgot-password-tokens-table.py | 26 -- .../20240924_01_thbvh-hooks-for-edu-domains.py | 24 -- ...in-ui-privilege-to-system-administrator-role.py | 42 --- .../20250609_01_LB60X-add-batch-edit-privileges.py | 49 ---- ...l-add-new-group-data-link-to-group-privilege.py | 19 -- ...data-link-to-group-privilege-to-group-leader.py | 23 -- ...e-management-privileges-to-group-leader-role.py | 27 -- ..._7Gro7-create-new-system-user-edit-privilege.py | 18 -- ...tem-user-edit-privilege-to-system-admin-role.py | 36 --- ...tial-system-wide-resources-access-privileges.py | 31 -- ...de-resources-access-privileges-to-sys-admins.py | 53 ---- ...ant-role-to-all-resources-to-sys-admin-users.py | 75 ----- ...dmin-privileges-for-acting-on-groups-members.py | 70 ----- ...06_01_v3f4P-add-role-systemwide-data-curator.py | 61 ---- ...-privilege-for-gn-docs-documentation-editing.py | 62 ---- ...ign-systemwide-docs-editor-role-to-sysadmins.py | 66 ----- ...rict-access-to-resources-make-public-feature.py | 49 ---- ...d-privileges-to-role-systemwide-data-curator.py | 69 ----- ...dd-user-and-time-tracking-to-resources-table.py | 185 ------------ ...O-new-privilege-system-system-wide-data-view.py | 19 -- ...2_L6zIV-add-privileges-to-batch-editors-role.py | 62 ---- migrations/auth/__init__.py | 1 - ...tions_init_data_in_resource_categories_table.py | 2 +- tests/unit/conftest.py | 3 +- 156 files changed, 3513 insertions(+), 3512 deletions(-) delete mode 100644 gn_auth/migrations.py create mode 100644 gn_auth/migrations/__init__.py create mode 100644 gn_auth/migrations/auth/20221103_01_js9ub-initialise-the-auth-entic-oris-ation-database.py create mode 100644 gn_auth/migrations/auth/20221103_02_sGrIs-create-user-credentials-table.py create mode 100644 gn_auth/migrations/auth/20221108_01_CoxYh-create-the-groups-table.py create mode 100644 gn_auth/migrations/auth/20221108_02_wxTr9-create-privileges-table.py create mode 100644 gn_auth/migrations/auth/20221108_03_Pbhb1-create-resource-categories-table.py create mode 100644 gn_auth/migrations/auth/20221108_04_CKcSL-init-data-in-resource-categories-table.py create mode 100644 gn_auth/migrations/auth/20221109_01_HbD5F-add-resource-meta-field-to-resource-categories-field.py create mode 100644 gn_auth/migrations/auth/20221110_01_WtZ1I-create-resources-table.py create mode 100644 gn_auth/migrations/auth/20221110_05_BaNtL-create-roles-table.py create mode 100644 gn_auth/migrations/auth/20221110_06_Pq2kT-create-generic-roles-table.py create mode 100644 gn_auth/migrations/auth/20221110_07_7WGa1-create-role-privileges-table.py create mode 100644 gn_auth/migrations/auth/20221110_08_23psB-add-privilege-category-and-privilege-description-columns-to-privileges-table.py create mode 100644 gn_auth/migrations/auth/20221113_01_7M0hv-enumerate-initial-privileges.py create mode 100644 gn_auth/migrations/auth/20221114_01_n8gsF-create-generic-role-privileges-table.py create mode 100644 gn_auth/migrations/auth/20221114_02_DKKjn-drop-generic-role-tables.py create mode 100644 gn_auth/migrations/auth/20221114_03_PtWjc-create-group-roles-table.py create mode 100644 gn_auth/migrations/auth/20221114_04_tLUzB-initialise-basic-roles.py create mode 100644 gn_auth/migrations/auth/20221114_05_hQun6-create-user-roles-table.py create mode 100644 gn_auth/migrations/auth/20221116_01_nKUmX-add-privileges-to-group-leader-role.py create mode 100644 gn_auth/migrations/auth/20221117_01_RDlfx-modify-group-roles-add-group-role-id.py create mode 100644 gn_auth/migrations/auth/20221117_02_fmuZh-create-group-users-table.py create mode 100644 gn_auth/migrations/auth/20221206_01_BbeF9-create-group-user-roles-on-resources-table.py create mode 100644 gn_auth/migrations/auth/20221208_01_sSdHz-add-public-column-to-resources-table.py create mode 100644 gn_auth/migrations/auth/20221219_01_CI3tN-create-oauth2-clients-table.py create mode 100644 gn_auth/migrations/auth/20221219_02_buSEU-create-oauth2-tokens-table.py create mode 100644 gn_auth/migrations/auth/20221219_03_PcTrb-create-authorisation-code-table.py create mode 100644 gn_auth/migrations/auth/20230111_01_Wd6IZ-remove-create-group-privilege-from-group-leader.py create mode 100644 gn_auth/migrations/auth/20230116_01_KwuJ3-rework-privileges-schema.py create mode 100644 gn_auth/migrations/auth/20230207_01_r0bkZ-create-group-join-requests-table.py create mode 100644 gn_auth/migrations/auth/20230210_01_8xMa1-system-admin-privileges-for-data-distribution.py create mode 100644 gn_auth/migrations/auth/20230210_02_lDK14-create-system-admin-role.py create mode 100644 gn_auth/migrations/auth/20230306_01_pRfxl-add-system-user-list-privilege.py create mode 100644 gn_auth/migrations/auth/20230306_02_7GnRY-add-system-user-list-privilege-to-system-administrator-and-group-leader-roles.py create mode 100644 gn_auth/migrations/auth/20230322_01_0dDZR-create-linked-phenotype-data-table.py create mode 100644 gn_auth/migrations/auth/20230322_02_Ll854-create-phenotype-resources-table.py create mode 100644 gn_auth/migrations/auth/20230404_01_VKxXg-create-linked-genotype-data-table.py create mode 100644 gn_auth/migrations/auth/20230404_02_la33P-create-genotype-resources-table.py create mode 100644 gn_auth/migrations/auth/20230410_01_8mwaf-create-linked-mrna-data-table.py create mode 100644 gn_auth/migrations/auth/20230410_02_WZqSf-create-mrna-resources-table.py create mode 100644 gn_auth/migrations/auth/20230907_01_pjnxz-refactor-add-resource-ownership-table.py create mode 100644 gn_auth/migrations/auth/20230907_02_Enicg-refactor-add-system-and-group-resource-categories.py create mode 100644 gn_auth/migrations/auth/20230907_03_BwAmf-refactor-drop-group-id-from-resources-table.py create mode 100644 gn_auth/migrations/auth/20230907_04_3LnrG-refactor-create-group-resources-table.py create mode 100644 gn_auth/migrations/auth/20230912_01_BxrhE-add-system-resource.py create mode 100644 gn_auth/migrations/auth/20230912_02_hFmSn-drop-group-id-and-fix-foreign-key-references-on-group-user-roles-on-resources-table.py create mode 100644 gn_auth/migrations/auth/20230925_01_TWJuR-add-new-public-view-role.py create mode 100644 gn_auth/migrations/auth/20231002_01_tzxTf-link-inbredsets-to-auth-system.py create mode 100644 gn_auth/migrations/auth/20231011_01_CS8NZ-create-new-inbredset-group-owner-role.py create mode 100644 gn_auth/migrations/auth/20240506_01_798tW-create-jwt-refresh-tokens-table.py create mode 100644 gn_auth/migrations/auth/20240529_01_ALNWj-update-schema-for-user-verification.py create mode 100644 gn_auth/migrations/auth/20240606_01_xQDwL-move-role-manipulation-privileges-from-group-to-resources.py create mode 100644 gn_auth/migrations/auth/20240606_02_ubZri-create-resource-roles-table.py create mode 100644 gn_auth/migrations/auth/20240606_03_BY7Us-drop-group-roles-table.py create mode 100644 gn_auth/migrations/auth/20240819_01_p2vXR-create-forgot-password-tokens-table.py create mode 100644 gn_auth/migrations/auth/20240924_01_thbvh-hooks-for-edu-domains.py create mode 100644 gn_auth/migrations/auth/20250328_01_72EFk-add-admin-ui-privilege-to-system-administrator-role.py create mode 100644 gn_auth/migrations/auth/20250609_01_LB60X-add-batch-edit-privileges.py create mode 100644 gn_auth/migrations/auth/20250609_01_bj9Pl-add-new-group-data-link-to-group-privilege.py create mode 100644 gn_auth/migrations/auth/20250609_02_9UBPl-assign-group-data-link-to-group-privilege-to-group-leader.py create mode 100644 gn_auth/migrations/auth/20250703_01_aDVwP-add-role-management-privileges-to-group-leader-role.py create mode 100644 gn_auth/migrations/auth/20250722_01_7Gro7-create-new-system-user-edit-privilege.py create mode 100644 gn_auth/migrations/auth/20250722_02_M8TXv-add-system-user-edit-privilege-to-system-admin-role.py create mode 100644 gn_auth/migrations/auth/20250729_01_CNn2p-create-initial-system-wide-resources-access-privileges.py create mode 100644 gn_auth/migrations/auth/20250729_02_7ycSm-assign-initial-system-wide-resources-access-privileges-to-sys-admins.py create mode 100644 gn_auth/migrations/auth/20250729_03_oCvvq-grant-role-to-all-resources-to-sys-admin-users.py create mode 100644 gn_auth/migrations/auth/20250731_01_Ke1us-add-sysadmin-privileges-for-acting-on-groups-members.py create mode 100644 gn_auth/migrations/auth/20260206_01_v3f4P-add-role-systemwide-data-curator.py create mode 100644 gn_auth/migrations/auth/20260311_01_TfRlV-add-privilege-for-gn-docs-documentation-editing.py create mode 100644 gn_auth/migrations/auth/20260311_02_v3EFQ-assign-systemwide-docs-editor-role-to-sysadmins.py create mode 100644 gn_auth/migrations/auth/20260311_03_vxBCX-restrict-access-to-resources-make-public-feature.py create mode 100644 gn_auth/migrations/auth/20260331_01_FV1sL-add-privileges-to-role-systemwide-data-curator.py create mode 100644 gn_auth/migrations/auth/20260402_01_Bf8nm-add-user-and-time-tracking-to-resources-table.py create mode 100644 gn_auth/migrations/auth/20260428_01_Tak6O-new-privilege-system-system-wide-data-view.py create mode 100644 gn_auth/migrations/auth/20260428_02_L6zIV-add-privileges-to-batch-editors-role.py create mode 100644 gn_auth/migrations/auth/__init__.py delete mode 100644 migrations/__init__.py delete mode 100644 migrations/auth/20221103_01_js9ub-initialise-the-auth-entic-oris-ation-database.py delete mode 100644 migrations/auth/20221103_02_sGrIs-create-user-credentials-table.py delete mode 100644 migrations/auth/20221108_01_CoxYh-create-the-groups-table.py delete mode 100644 migrations/auth/20221108_02_wxTr9-create-privileges-table.py delete mode 100644 migrations/auth/20221108_03_Pbhb1-create-resource-categories-table.py delete mode 100644 migrations/auth/20221108_04_CKcSL-init-data-in-resource-categories-table.py delete mode 100644 migrations/auth/20221109_01_HbD5F-add-resource-meta-field-to-resource-categories-field.py delete mode 100644 migrations/auth/20221110_01_WtZ1I-create-resources-table.py delete mode 100644 migrations/auth/20221110_05_BaNtL-create-roles-table.py delete mode 100644 migrations/auth/20221110_06_Pq2kT-create-generic-roles-table.py delete mode 100644 migrations/auth/20221110_07_7WGa1-create-role-privileges-table.py delete mode 100644 migrations/auth/20221110_08_23psB-add-privilege-category-and-privilege-description-columns-to-privileges-table.py delete mode 100644 migrations/auth/20221113_01_7M0hv-enumerate-initial-privileges.py delete mode 100644 migrations/auth/20221114_01_n8gsF-create-generic-role-privileges-table.py delete mode 100644 migrations/auth/20221114_02_DKKjn-drop-generic-role-tables.py delete mode 100644 migrations/auth/20221114_03_PtWjc-create-group-roles-table.py delete mode 100644 migrations/auth/20221114_04_tLUzB-initialise-basic-roles.py delete mode 100644 migrations/auth/20221114_05_hQun6-create-user-roles-table.py delete mode 100644 migrations/auth/20221116_01_nKUmX-add-privileges-to-group-leader-role.py delete mode 100644 migrations/auth/20221117_01_RDlfx-modify-group-roles-add-group-role-id.py delete mode 100644 migrations/auth/20221117_02_fmuZh-create-group-users-table.py delete mode 100644 migrations/auth/20221206_01_BbeF9-create-group-user-roles-on-resources-table.py delete mode 100644 migrations/auth/20221208_01_sSdHz-add-public-column-to-resources-table.py delete mode 100644 migrations/auth/20221219_01_CI3tN-create-oauth2-clients-table.py delete mode 100644 migrations/auth/20221219_02_buSEU-create-oauth2-tokens-table.py delete mode 100644 migrations/auth/20221219_03_PcTrb-create-authorisation-code-table.py delete mode 100644 migrations/auth/20230111_01_Wd6IZ-remove-create-group-privilege-from-group-leader.py delete mode 100644 migrations/auth/20230116_01_KwuJ3-rework-privileges-schema.py delete mode 100644 migrations/auth/20230207_01_r0bkZ-create-group-join-requests-table.py delete mode 100644 migrations/auth/20230210_01_8xMa1-system-admin-privileges-for-data-distribution.py delete mode 100644 migrations/auth/20230210_02_lDK14-create-system-admin-role.py delete mode 100644 migrations/auth/20230306_01_pRfxl-add-system-user-list-privilege.py delete mode 100644 migrations/auth/20230306_02_7GnRY-add-system-user-list-privilege-to-system-administrator-and-group-leader-roles.py delete mode 100644 migrations/auth/20230322_01_0dDZR-create-linked-phenotype-data-table.py delete mode 100644 migrations/auth/20230322_02_Ll854-create-phenotype-resources-table.py delete mode 100644 migrations/auth/20230404_01_VKxXg-create-linked-genotype-data-table.py delete mode 100644 migrations/auth/20230404_02_la33P-create-genotype-resources-table.py delete mode 100644 migrations/auth/20230410_01_8mwaf-create-linked-mrna-data-table.py delete mode 100644 migrations/auth/20230410_02_WZqSf-create-mrna-resources-table.py delete mode 100644 migrations/auth/20230907_01_pjnxz-refactor-add-resource-ownership-table.py delete mode 100644 migrations/auth/20230907_02_Enicg-refactor-add-system-and-group-resource-categories.py delete mode 100644 migrations/auth/20230907_03_BwAmf-refactor-drop-group-id-from-resources-table.py delete mode 100644 migrations/auth/20230907_04_3LnrG-refactor-create-group-resources-table.py delete mode 100644 migrations/auth/20230912_01_BxrhE-add-system-resource.py delete mode 100644 migrations/auth/20230912_02_hFmSn-drop-group-id-and-fix-foreign-key-references-on-group-user-roles-on-resources-table.py delete mode 100644 migrations/auth/20230925_01_TWJuR-add-new-public-view-role.py delete mode 100644 migrations/auth/20231002_01_tzxTf-link-inbredsets-to-auth-system.py delete mode 100644 migrations/auth/20231011_01_CS8NZ-create-new-inbredset-group-owner-role.py delete mode 100644 migrations/auth/20240506_01_798tW-create-jwt-refresh-tokens-table.py delete mode 100644 migrations/auth/20240529_01_ALNWj-update-schema-for-user-verification.py delete mode 100644 migrations/auth/20240606_01_xQDwL-move-role-manipulation-privileges-from-group-to-resources.py delete mode 100644 migrations/auth/20240606_02_ubZri-create-resource-roles-table.py delete mode 100644 migrations/auth/20240606_03_BY7Us-drop-group-roles-table.py delete mode 100644 migrations/auth/20240819_01_p2vXR-create-forgot-password-tokens-table.py delete mode 100644 migrations/auth/20240924_01_thbvh-hooks-for-edu-domains.py delete mode 100644 migrations/auth/20250328_01_72EFk-add-admin-ui-privilege-to-system-administrator-role.py delete mode 100644 migrations/auth/20250609_01_LB60X-add-batch-edit-privileges.py delete mode 100644 migrations/auth/20250609_01_bj9Pl-add-new-group-data-link-to-group-privilege.py delete mode 100644 migrations/auth/20250609_02_9UBPl-assign-group-data-link-to-group-privilege-to-group-leader.py delete mode 100644 migrations/auth/20250703_01_aDVwP-add-role-management-privileges-to-group-leader-role.py delete mode 100644 migrations/auth/20250722_01_7Gro7-create-new-system-user-edit-privilege.py delete mode 100644 migrations/auth/20250722_02_M8TXv-add-system-user-edit-privilege-to-system-admin-role.py delete mode 100644 migrations/auth/20250729_01_CNn2p-create-initial-system-wide-resources-access-privileges.py delete mode 100644 migrations/auth/20250729_02_7ycSm-assign-initial-system-wide-resources-access-privileges-to-sys-admins.py delete mode 100644 migrations/auth/20250729_03_oCvvq-grant-role-to-all-resources-to-sys-admin-users.py delete mode 100644 migrations/auth/20250731_01_Ke1us-add-sysadmin-privileges-for-acting-on-groups-members.py delete mode 100644 migrations/auth/20260206_01_v3f4P-add-role-systemwide-data-curator.py delete mode 100644 migrations/auth/20260311_01_TfRlV-add-privilege-for-gn-docs-documentation-editing.py delete mode 100644 migrations/auth/20260311_02_v3EFQ-assign-systemwide-docs-editor-role-to-sysadmins.py delete mode 100644 migrations/auth/20260311_03_vxBCX-restrict-access-to-resources-make-public-feature.py delete mode 100644 migrations/auth/20260331_01_FV1sL-add-privileges-to-role-systemwide-data-curator.py delete mode 100644 migrations/auth/20260402_01_Bf8nm-add-user-and-time-tracking-to-resources-table.py delete mode 100644 migrations/auth/20260428_01_Tak6O-new-privilege-system-system-wide-data-view.py delete mode 100644 migrations/auth/20260428_02_L6zIV-add-privileges-to-batch-editors-role.py delete mode 100644 migrations/auth/__init__.py diff --git a/gn_auth/migrations.py b/gn_auth/migrations.py deleted file mode 100644 index 3451e07..0000000 --- a/gn_auth/migrations.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Run the migrations in the app, rather than with yoyo CLI.""" -from pathlib import Path -from typing import Union - -from yoyo import read_migrations -from yoyo.backends import DatabaseBackend -from yoyo.migrations import Migration, MigrationList - -class MigrationNotFound(Exception): - """Raised if a migration is not found at the given path.""" - def __init__(self, migration_path: Path): - """Initialise the exception.""" - super().__init__(f"Could not find migration '{migration_path}'") - -def apply_migrations(backend: DatabaseBackend, migrations: MigrationList): - "Apply the provided migrations." - with backend.lock(): - backend.apply_migrations(backend.to_apply(migrations)) - -def rollback_migrations(backend: DatabaseBackend, migrations: MigrationList): - "Rollback the provided migrations." - with backend.lock(): - backend.rollback_migrations(backend.to_rollback(migrations)) - -def get_migration(migration_path: Union[Path, str]) -> Migration: - """Retrieve a migration at thi given `migration_path`.""" - migration_path = Path(migration_path) - if migration_path.exists(): - for migration in read_migrations(str(migration_path.parent)): - if Path(migration.path) == migration_path: - return migration - - raise MigrationNotFound(migration_path) diff --git a/gn_auth/migrations/__init__.py b/gn_auth/migrations/__init__.py new file mode 100644 index 0000000..6acb058 --- /dev/null +++ b/gn_auth/migrations/__init__.py @@ -0,0 +1,34 @@ +"""Migrations package: Provides the migrations, and some utility functions to +help with dealing with migrations.""" +from pathlib import Path +from typing import Union + +from yoyo import read_migrations +from yoyo.backends import DatabaseBackend +from yoyo.migrations import Migration, MigrationList + +class MigrationNotFound(Exception): + """Raised if a migration is not found at the given path.""" + def __init__(self, migration_path: Path): + """Initialise the exception.""" + super().__init__(f"Could not find migration '{migration_path}'") + +def apply_migrations(backend: DatabaseBackend, migrations: MigrationList): + "Apply the provided migrations." + with backend.lock(): + backend.apply_migrations(backend.to_apply(migrations)) + +def rollback_migrations(backend: DatabaseBackend, migrations: MigrationList): + "Rollback the provided migrations." + with backend.lock(): + backend.rollback_migrations(backend.to_rollback(migrations)) + +def get_migration(migration_path: Union[Path, str]) -> Migration: + """Retrieve a migration at thi given `migration_path`.""" + migration_path = Path(migration_path) + if migration_path.exists(): + for migration in read_migrations(str(migration_path.parent)): + if Path(migration.path) == migration_path: + return migration + + raise MigrationNotFound(migration_path) diff --git a/gn_auth/migrations/auth/20221103_01_js9ub-initialise-the-auth-entic-oris-ation-database.py b/gn_auth/migrations/auth/20221103_01_js9ub-initialise-the-auth-entic-oris-ation-database.py new file mode 100644 index 0000000..d511f5d --- /dev/null +++ b/gn_auth/migrations/auth/20221103_01_js9ub-initialise-the-auth-entic-oris-ation-database.py @@ -0,0 +1,19 @@ +""" +Initialise the auth(entic|oris)ation database. +""" + +from yoyo import step + +__depends__ = {} # type: ignore[var-annotated] + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS users( + user_id TEXT PRIMARY KEY NOT NULL, + email TEXT UNIQUE NOT NULL, + name TEXT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS users") +] diff --git a/gn_auth/migrations/auth/20221103_02_sGrIs-create-user-credentials-table.py b/gn_auth/migrations/auth/20221103_02_sGrIs-create-user-credentials-table.py new file mode 100644 index 0000000..48bd663 --- /dev/null +++ b/gn_auth/migrations/auth/20221103_02_sGrIs-create-user-credentials-table.py @@ -0,0 +1,20 @@ +""" +create user_credentials table +""" + +from yoyo import step + +__depends__ = {'20221103_01_js9ub-initialise-the-auth-entic-oris-ation-database'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS user_credentials( + user_id TEXT PRIMARY KEY, + password TEXT NOT NULL, + FOREIGN KEY(user_id) REFERENCES users(user_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS user_credentials") +] diff --git a/gn_auth/migrations/auth/20221108_01_CoxYh-create-the-groups-table.py b/gn_auth/migrations/auth/20221108_01_CoxYh-create-the-groups-table.py new file mode 100644 index 0000000..29f92d4 --- /dev/null +++ b/gn_auth/migrations/auth/20221108_01_CoxYh-create-the-groups-table.py @@ -0,0 +1,19 @@ +""" +Create the groups table +""" + +from yoyo import step + +__depends__ = {'20221103_02_sGrIs-create-user-credentials-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS groups( + group_id TEXT PRIMARY KEY NOT NULL, + group_name TEXT NOT NULL, + group_metadata TEXT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS groups") +] diff --git a/gn_auth/migrations/auth/20221108_02_wxTr9-create-privileges-table.py b/gn_auth/migrations/auth/20221108_02_wxTr9-create-privileges-table.py new file mode 100644 index 0000000..67720b2 --- /dev/null +++ b/gn_auth/migrations/auth/20221108_02_wxTr9-create-privileges-table.py @@ -0,0 +1,18 @@ +""" +Create privileges table +""" + +from yoyo import step + +__depends__ = {'20221108_01_CoxYh-create-the-groups-table'} + +steps = [ + step( + """ + CREATE TABLE privileges( + privilege_id TEXT PRIMARY KEY, + privilege_name TEXT NOT NULL + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS privileges") +] diff --git a/gn_auth/migrations/auth/20221108_03_Pbhb1-create-resource-categories-table.py b/gn_auth/migrations/auth/20221108_03_Pbhb1-create-resource-categories-table.py new file mode 100644 index 0000000..ce752ef --- /dev/null +++ b/gn_auth/migrations/auth/20221108_03_Pbhb1-create-resource-categories-table.py @@ -0,0 +1,19 @@ +""" +Create resource_categories table +""" + +from yoyo import step + +__depends__ = {'20221108_02_wxTr9-create-privileges-table'} + +steps = [ + step( + """ + CREATE TABLE resource_categories( + resource_category_id TEXT PRIMARY KEY, + resource_category_key TEXT NOT NULL, + resource_category_description TEXT NOT NULL + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS resource_categories") +] diff --git a/gn_auth/migrations/auth/20221108_04_CKcSL-init-data-in-resource-categories-table.py b/gn_auth/migrations/auth/20221108_04_CKcSL-init-data-in-resource-categories-table.py new file mode 100644 index 0000000..76ffbef --- /dev/null +++ b/gn_auth/migrations/auth/20221108_04_CKcSL-init-data-in-resource-categories-table.py @@ -0,0 +1,25 @@ +""" +Init data in resource_categories table +""" + +from yoyo import step + +__depends__ = {'20221108_03_Pbhb1-create-resource-categories-table'} + +steps = [ + step( + """ + INSERT INTO resource_categories VALUES + ('fad071a3-2fc8-40b8-992b-cdefe7dcac79', 'mrna', 'mRNA Dataset'), + ('548d684b-d4d1-46fb-a6d3-51a56b7da1b3', 'phenotype', 'Phenotype (Publish) Dataset'), + ('48056f84-a2a6-41ac-8319-0e1e212cba2a', 'genotype', 'Genotype Dataset') + """, + """ + DELETE FROM resource_categories WHERE resource_category_id IN + ( + 'fad071a3-2fc8-40b8-992b-cdefe7dcac79', + '548d684b-d4d1-46fb-a6d3-51a56b7da1b3', + '48056f84-a2a6-41ac-8319-0e1e212cba2a' + ) + """) +] diff --git a/gn_auth/migrations/auth/20221109_01_HbD5F-add-resource-meta-field-to-resource-categories-field.py b/gn_auth/migrations/auth/20221109_01_HbD5F-add-resource-meta-field-to-resource-categories-field.py new file mode 100644 index 0000000..6c829b1 --- /dev/null +++ b/gn_auth/migrations/auth/20221109_01_HbD5F-add-resource-meta-field-to-resource-categories-field.py @@ -0,0 +1,17 @@ +""" +Add 'resource_meta' field to 'resource_categories' field. +""" + +from yoyo import step + +__depends__ = {'20221108_04_CKcSL-init-data-in-resource-categories-table'} + +steps = [ + step( + """ + ALTER TABLE resource_categories + ADD COLUMN + resource_meta TEXT NOT NULL DEFAULT '[]' + """, + "ALTER TABLE resource_categories DROP COLUMN resource_meta") +] diff --git a/gn_auth/migrations/auth/20221110_01_WtZ1I-create-resources-table.py b/gn_auth/migrations/auth/20221110_01_WtZ1I-create-resources-table.py new file mode 100644 index 0000000..abc8895 --- /dev/null +++ b/gn_auth/migrations/auth/20221110_01_WtZ1I-create-resources-table.py @@ -0,0 +1,26 @@ +""" +Create 'resources' table +""" + +from yoyo import step + +__depends__ = {'20221109_01_HbD5F-add-resource-meta-field-to-resource-categories-field'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS resources( + group_id TEXT NOT NULL, + resource_id TEXT NOT NULL, + resource_name TEXT NOT NULL UNIQUE, + resource_category_id TEXT NOT NULL, + PRIMARY KEY(group_id, resource_id), + FOREIGN KEY(group_id) REFERENCES groups(group_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(resource_category_id) + REFERENCES resource_categories(resource_category_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS resources") +] diff --git a/gn_auth/migrations/auth/20221110_05_BaNtL-create-roles-table.py b/gn_auth/migrations/auth/20221110_05_BaNtL-create-roles-table.py new file mode 100644 index 0000000..51e19e8 --- /dev/null +++ b/gn_auth/migrations/auth/20221110_05_BaNtL-create-roles-table.py @@ -0,0 +1,19 @@ +""" +Create 'roles' table +""" + +from yoyo import step + +__depends__ = {'20221110_01_WtZ1I-create-resources-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS roles( + role_id TEXT NOT NULL PRIMARY KEY, + role_name TEXT NOT NULL, + user_editable INTEGER NOT NULL DEFAULT 1 CHECK (user_editable=0 or user_editable=1) + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS roles") +] diff --git a/gn_auth/migrations/auth/20221110_06_Pq2kT-create-generic-roles-table.py b/gn_auth/migrations/auth/20221110_06_Pq2kT-create-generic-roles-table.py new file mode 100644 index 0000000..2b55c2b --- /dev/null +++ b/gn_auth/migrations/auth/20221110_06_Pq2kT-create-generic-roles-table.py @@ -0,0 +1,24 @@ +""" +Create 'generic_roles' table + +The roles in this table will be template roles, defining some common roles that +can be used within the groups. + +They could also be used to define system-level roles, though those will not be +provided to the "common" users. +""" + +from yoyo import step + +__depends__ = {'20221110_05_BaNtL-create-roles-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS generic_roles( + role_id TEXT PRIMARY KEY, + role_name TEXT NOT NULL + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS generic_roles") +] diff --git a/gn_auth/migrations/auth/20221110_07_7WGa1-create-role-privileges-table.py b/gn_auth/migrations/auth/20221110_07_7WGa1-create-role-privileges-table.py new file mode 100644 index 0000000..0d0eeb9 --- /dev/null +++ b/gn_auth/migrations/auth/20221110_07_7WGa1-create-role-privileges-table.py @@ -0,0 +1,29 @@ +""" +Create 'role_privileges' table +""" + +from yoyo import step + +__depends__ = {'20221110_06_Pq2kT-create-generic-roles-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS role_privileges( + role_id TEXT NOT NULL, + privilege_id TEXT NOT NULL, + PRIMARY KEY(role_id, privilege_id), + FOREIGN KEY(role_id) REFERENCES roles(role_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(privilege_id) REFERENCES privileges(privilege_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS role_privileges"), + step( + """ + CREATE INDEX IF NOT EXISTS idx_tbl_role_privileges_cols_role_id + ON role_privileges(role_id) + """, + "DROP INDEX IF EXISTS idx_tbl_role_privileges_cols_role_id") +] diff --git a/gn_auth/migrations/auth/20221110_08_23psB-add-privilege-category-and-privilege-description-columns-to-privileges-table.py b/gn_auth/migrations/auth/20221110_08_23psB-add-privilege-category-and-privilege-description-columns-to-privileges-table.py new file mode 100644 index 0000000..077182b --- /dev/null +++ b/gn_auth/migrations/auth/20221110_08_23psB-add-privilege-category-and-privilege-description-columns-to-privileges-table.py @@ -0,0 +1,22 @@ +""" +Add 'privilege_category' and 'privilege_description' columns to 'privileges' table +""" + +from yoyo import step + +__depends__ = {'20221110_07_7WGa1-create-role-privileges-table'} + +steps = [ + step( + """ + ALTER TABLE privileges ADD COLUMN + privilege_category TEXT NOT NULL DEFAULT 'common' + """, + "ALTER TABLE privileges DROP COLUMN privilege_category"), + step( + """ + ALTER TABLE privileges ADD COLUMN + privilege_description TEXT + """, + "ALTER TABLE privileges DROP COLUMN privilege_description") +] diff --git a/gn_auth/migrations/auth/20221113_01_7M0hv-enumerate-initial-privileges.py b/gn_auth/migrations/auth/20221113_01_7M0hv-enumerate-initial-privileges.py new file mode 100644 index 0000000..072f226 --- /dev/null +++ b/gn_auth/migrations/auth/20221113_01_7M0hv-enumerate-initial-privileges.py @@ -0,0 +1,66 @@ +""" +Enumerate initial privileges +""" + +from yoyo import step + +__depends__ = {'20221110_08_23psB-add-privilege-category-and-privilege-description-columns-to-privileges-table'} + +steps = [ + step( + """ + INSERT INTO + privileges(privilege_id, privilege_name, privilege_category, + privilege_description) + VALUES + -- group-management privileges + ('4842e2aa-38b9-4349-805e-0a99a9cf8bff', 'create-group', + 'group-management', 'Create a group'), + ('3ebfe79c-d159-4629-8b38-772cf4bc2261', 'view-group', + 'group-management', 'View the details of a group'), + ('52576370-b3c7-4e6a-9f7e-90e9dbe24d8f', 'edit-group', + 'group-management', 'Edit the details of a group'), + ('13ec2a94-4f1a-442d-aad2-936ad6dd5c57', 'delete-group', + 'group-management', 'Delete a group'), + ('ae4add8c-789a-4d11-a6e9-a306470d83d9', 'add-group-member', + 'group-management', 'Add a user to a group'), + ('f1bd3f42-567e-4965-9643-6d1a52ddee64', 'remove-group-member', + 'group-management', 'Remove a user from a group'), + ('80f11285-5079-4ec0-907c-06509f88a364', 'assign-group-leader', + 'group-management', 'Assign user group-leader privileges'), + ('d4afe2b3-4ca0-4edd-b37d-966535b5e5bd', + 'transfer-group-leadership', 'group-management', + 'Transfer leadership of the group to some other member'), + + -- resource-management privileges + ('aa25b32a-bff2-418d-b0a2-e26b4a8f089b', 'create-resource', + 'resource-management', 'Create a resource object'), + ('7f261757-3211-4f28-a43f-a09b800b164d', 'view-resource', + 'resource-management', 'view a resource and use it in computations'), + ('2f980855-959b-4339-b80e-25d1ec286e21', 'edit-resource', + 'resource-management', 'edit/update a resource'), + ('d2a070fd-e031-42fb-ba41-d60cf19e5d6d', 'delete-resource', + 'resource-management', 'Delete a resource'), + + -- role-management privileges + ('221660b1-df05-4be1-b639-f010269dbda9', 'create-role', + 'role-management', 'Create a new role'), + ('7bcca363-cba9-4169-9e31-26bdc6179b28', 'edit-role', + 'role-management', 'edit/update an existing role'), + ('5103cc68-96f8-4ebb-83a4-a31692402c9b', 'assign-role', + 'role-management', 'Assign a role to an existing user'), + ('1c59eff5-9336-4ed2-a166-8f70d4cb012e', 'delete-role', + 'role-management', 'Delete an existing role'), + + -- user-management privileges + ('e7252301-6ee0-43ba-93ef-73b607cf06f6', 'reset-any-password', + 'user-management', 'Reset the password for any user'), + ('1fe61370-cae9-4983-bd6c-ce61050c510f', 'delete-any-user', + 'user-management', 'Delete any user from the system'), + + -- sytem-admin privileges + ('519db546-d44e-4fdc-9e4e-25aa67548ab3', 'masquerade', + 'system-admin', 'Masquerade as some other user') + """, + "DELETE FROM privileges") +] diff --git a/gn_auth/migrations/auth/20221114_01_n8gsF-create-generic-role-privileges-table.py b/gn_auth/migrations/auth/20221114_01_n8gsF-create-generic-role-privileges-table.py new file mode 100644 index 0000000..2048f4a --- /dev/null +++ b/gn_auth/migrations/auth/20221114_01_n8gsF-create-generic-role-privileges-table.py @@ -0,0 +1,35 @@ +""" +Create 'generic_role_privileges' table + +This table links the generic_roles to the privileges they provide +""" + +from yoyo import step + +__depends__ = {'20221113_01_7M0hv-enumerate-initial-privileges'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS generic_role_privileges( + generic_role_id TEXT NOT NULL, + privilege_id TEXT NOT NULL, + PRIMARY KEY(generic_role_id, privilege_id), + FOREIGN KEY(generic_role_id) REFERENCES generic_roles(role_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(privilege_id) REFERENCES privileges(privilege_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS generic_role_privileges"), + step( + """ + CREATE INDEX IF NOT EXISTS + idx_tbl_generic_role_privileges_cols_generic_role_id + ON generic_role_privileges(generic_role_id) + """, + """ + DROP INDEX IF EXISTS + idx_tbl_generic_role_privileges_cols_generic_role_id + """) +] diff --git a/gn_auth/migrations/auth/20221114_02_DKKjn-drop-generic-role-tables.py b/gn_auth/migrations/auth/20221114_02_DKKjn-drop-generic-role-tables.py new file mode 100644 index 0000000..6bd101b --- /dev/null +++ b/gn_auth/migrations/auth/20221114_02_DKKjn-drop-generic-role-tables.py @@ -0,0 +1,41 @@ +""" +Drop 'generic_role*' tables +""" + +from yoyo import step + +__depends__ = {'20221114_01_n8gsF-create-generic-role-privileges-table'} + +steps = [ + step( + """ + DROP INDEX IF EXISTS + idx_tbl_generic_role_privileges_cols_generic_role_id + """, + """ + CREATE INDEX IF NOT EXISTS + idx_tbl_generic_role_privileges_cols_generic_role_id + ON generic_role_privileges(generic_role_id) + """), + step( + "DROP TABLE IF EXISTS generic_role_privileges", + """ + CREATE TABLE IF NOT EXISTS generic_role_privileges( + generic_role_id TEXT NOT NULL, + privilege_id TEXT NOT NULL, + PRIMARY KEY(generic_role_id, privilege_id), + FOREIGN KEY(generic_role_id) REFERENCES generic_roles(role_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(privilege_id) REFERENCES privileges(privilege_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """), + step( + "DROP TABLE IF EXISTS generic_roles", + """ + CREATE TABLE IF NOT EXISTS generic_roles( + role_id TEXT PRIMARY KEY, + role_name TEXT NOT NULL + ) WITHOUT ROWID + """) +] diff --git a/gn_auth/migrations/auth/20221114_03_PtWjc-create-group-roles-table.py b/gn_auth/migrations/auth/20221114_03_PtWjc-create-group-roles-table.py new file mode 100644 index 0000000..a7e7b45 --- /dev/null +++ b/gn_auth/migrations/auth/20221114_03_PtWjc-create-group-roles-table.py @@ -0,0 +1,29 @@ +""" +Create 'group_roles' table +""" + +from yoyo import step + +__depends__ = {'20221114_02_DKKjn-drop-generic-role-tables'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS group_roles( + group_id TEXT NOT NULL, + role_id TEXT NOT NULL, + PRIMARY KEY(group_id, role_id), + FOREIGN KEY(group_id) REFERENCES groups(group_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(role_id) REFERENCES roles(role_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS group_roles"), + step( + """ + CREATE INDEX IF NOT EXISTS idx_tbl_group_roles_cols_group_id + ON group_roles(group_id) + """, + "DROP INDEX IF EXISTS idx_tbl_group_roles_cols_group_id") +] diff --git a/gn_auth/migrations/auth/20221114_04_tLUzB-initialise-basic-roles.py b/gn_auth/migrations/auth/20221114_04_tLUzB-initialise-basic-roles.py new file mode 100644 index 0000000..386f481 --- /dev/null +++ b/gn_auth/migrations/auth/20221114_04_tLUzB-initialise-basic-roles.py @@ -0,0 +1,56 @@ +""" +Initialise basic roles +""" + +from yoyo import step + +__depends__ = {'20221114_03_PtWjc-create-group-roles-table'} + +steps = [ + step( + """ + INSERT INTO roles(role_id, role_name, user_editable) VALUES + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', 'group-leader', '0'), + ('522e4d40-aefc-4a64-b7e0-768b8be517ee', 'resource-owner', '0') + """, + "DELETE FROM roles"), + step( + """ + INSERT INTO role_privileges(role_id, privilege_id) + VALUES + -- group-management + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + '4842e2aa-38b9-4349-805e-0a99a9cf8bff'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + '3ebfe79c-d159-4629-8b38-772cf4bc2261'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + '52576370-b3c7-4e6a-9f7e-90e9dbe24d8f'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + '13ec2a94-4f1a-442d-aad2-936ad6dd5c57'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + 'ae4add8c-789a-4d11-a6e9-a306470d83d9'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + 'f1bd3f42-567e-4965-9643-6d1a52ddee64'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + 'd4afe2b3-4ca0-4edd-b37d-966535b5e5bd'), + + -- resource-management + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + 'aa25b32a-bff2-418d-b0a2-e26b4a8f089b'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + '7f261757-3211-4f28-a43f-a09b800b164d'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + '2f980855-959b-4339-b80e-25d1ec286e21'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + 'd2a070fd-e031-42fb-ba41-d60cf19e5d6d'), + ('522e4d40-aefc-4a64-b7e0-768b8be517ee', + 'aa25b32a-bff2-418d-b0a2-e26b4a8f089b'), + ('522e4d40-aefc-4a64-b7e0-768b8be517ee', + '7f261757-3211-4f28-a43f-a09b800b164d'), + ('522e4d40-aefc-4a64-b7e0-768b8be517ee', + '2f980855-959b-4339-b80e-25d1ec286e21'), + ('522e4d40-aefc-4a64-b7e0-768b8be517ee', + 'd2a070fd-e031-42fb-ba41-d60cf19e5d6d') + """, + "DELETE FROM role_privileges") +] diff --git a/gn_auth/migrations/auth/20221114_05_hQun6-create-user-roles-table.py b/gn_auth/migrations/auth/20221114_05_hQun6-create-user-roles-table.py new file mode 100644 index 0000000..e0de751 --- /dev/null +++ b/gn_auth/migrations/auth/20221114_05_hQun6-create-user-roles-table.py @@ -0,0 +1,29 @@ +""" +Create 'user_roles' table. +""" + +from yoyo import step + +__depends__ = {'20221114_04_tLUzB-initialise-basic-roles'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS 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 + """, + "DROP TABLE IF EXISTS user_roles"), + step( + """ + CREATE INDEX IF NOT EXISTS idx_tbl_user_roles_cols_user_id + ON user_roles(user_id) + """, + "DROP INDEX IF EXISTS idx_tbl_user_roles_cols_user_id") +] diff --git a/gn_auth/migrations/auth/20221116_01_nKUmX-add-privileges-to-group-leader-role.py b/gn_auth/migrations/auth/20221116_01_nKUmX-add-privileges-to-group-leader-role.py new file mode 100644 index 0000000..2e4ae28 --- /dev/null +++ b/gn_auth/migrations/auth/20221116_01_nKUmX-add-privileges-to-group-leader-role.py @@ -0,0 +1,35 @@ +""" +Add privileges to 'group-leader' role. +""" + +from yoyo import step + +__depends__ = {'20221114_05_hQun6-create-user-roles-table'} + +steps = [ + step( + """ + INSERT INTO role_privileges(role_id, privilege_id) + VALUES + -- role management + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + '221660b1-df05-4be1-b639-f010269dbda9'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + '7bcca363-cba9-4169-9e31-26bdc6179b28'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + '5103cc68-96f8-4ebb-83a4-a31692402c9b'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + '1c59eff5-9336-4ed2-a166-8f70d4cb012e') + """, + """ + DELETE FROM role_privileges + WHERE + role_id='a0e67630-d502-4b9f-b23f-6805d0f30e30' + AND privilege_id IN ( + '221660b1-df05-4be1-b639-f010269dbda9', + '7bcca363-cba9-4169-9e31-26bdc6179b28', + '5103cc68-96f8-4ebb-83a4-a31692402c9b', + '1c59eff5-9336-4ed2-a166-8f70d4cb012e' + ) + """) +] diff --git a/gn_auth/migrations/auth/20221117_01_RDlfx-modify-group-roles-add-group-role-id.py b/gn_auth/migrations/auth/20221117_01_RDlfx-modify-group-roles-add-group-role-id.py new file mode 100644 index 0000000..a4d7806 --- /dev/null +++ b/gn_auth/migrations/auth/20221117_01_RDlfx-modify-group-roles-add-group-role-id.py @@ -0,0 +1,52 @@ +""" +Modify 'group_roles': add 'group_role_id' + +At this point, there is no data in the `group_roles` table and therefore, it +should be safe to simply recreate it. +""" + +from yoyo import step + +__depends__ = {'20221116_01_nKUmX-add-privileges-to-group-leader-role'} + +steps = [ + step( + "DROP INDEX IF EXISTS idx_tbl_group_roles_cols_group_id", + """ + CREATE INDEX IF NOT EXISTS idx_tbl_group_roles_cols_group_id + ON group_roles(group_id) + """), + step( + "DROP TABLE IF EXISTS group_roles", + """ + CREATE TABLE IF NOT EXISTS group_roles( + group_id TEXT NOT NULL, + role_id TEXT NOT NULL, + PRIMARY KEY(group_id, role_id), + FOREIGN KEY(group_id) REFERENCES groups(group_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(role_id) REFERENCES roles(role_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """), + step( + """ + CREATE TABLE IF NOT EXISTS group_roles( + group_role_id TEXT PRIMARY KEY, + group_id TEXT NOT NULL, + role_id TEXT NOT NULL, + UNIQUE (group_id, role_id), + FOREIGN KEY(group_id) REFERENCES groups(group_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(role_id) REFERENCES roles(role_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS group_roles"), + step( + """ + CREATE INDEX IF NOT EXISTS idx_tbl_group_roles_cols_group_id + ON group_roles(group_id) + """, + "DROP INDEX IF EXISTS idx_tbl_group_roles_cols_group_id") +] diff --git a/gn_auth/migrations/auth/20221117_02_fmuZh-create-group-users-table.py b/gn_auth/migrations/auth/20221117_02_fmuZh-create-group-users-table.py new file mode 100644 index 0000000..92885ef --- /dev/null +++ b/gn_auth/migrations/auth/20221117_02_fmuZh-create-group-users-table.py @@ -0,0 +1,25 @@ +""" +Create 'group_users' table. +""" + +from yoyo import step + +__depends__ = {'20221117_01_RDlfx-modify-group-roles-add-group-role-id'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS group_users( + group_id TEXT NOT NULL, + user_id TEXT NOT NULL UNIQUE, -- user can only be in one group + PRIMARY KEY(group_id, user_id) + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS group_users"), + step( + """ + CREATE INDEX IF NOT EXISTS tbl_group_users_cols_group_id + ON group_users(group_id) + """, + "DROP INDEX IF EXISTS tbl_group_users_cols_group_id") +] diff --git a/gn_auth/migrations/auth/20221206_01_BbeF9-create-group-user-roles-on-resources-table.py b/gn_auth/migrations/auth/20221206_01_BbeF9-create-group-user-roles-on-resources-table.py new file mode 100644 index 0000000..9aa3667 --- /dev/null +++ b/gn_auth/migrations/auth/20221206_01_BbeF9-create-group-user-roles-on-resources-table.py @@ -0,0 +1,39 @@ +""" +Create 'group_user_roles_on_resources' table +""" + +from yoyo import step + +__depends__ = {'20221117_02_fmuZh-create-group-users-table'} + +steps = [ + step( + """ + CREATE TABLE 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 (group_id, resource_id) + REFERENCES resources(group_id, resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS group_user_roles_on_resources"), + step( + """ + CREATE INDEX IF NOT EXISTS + idx_tbl_group_user_roles_on_resources_group_user_resource + ON group_user_roles_on_resources(group_id, user_id, resource_id) + """, + """ + DROP INDEX IF EXISTS + idx_tbl_group_user_roles_on_resources_group_user_resource""") +] diff --git a/gn_auth/migrations/auth/20221208_01_sSdHz-add-public-column-to-resources-table.py b/gn_auth/migrations/auth/20221208_01_sSdHz-add-public-column-to-resources-table.py new file mode 100644 index 0000000..2238069 --- /dev/null +++ b/gn_auth/migrations/auth/20221208_01_sSdHz-add-public-column-to-resources-table.py @@ -0,0 +1,16 @@ +""" +Add 'public' column to 'resources' table +""" + +from yoyo import step + +__depends__ = {'20221206_01_BbeF9-create-group-user-roles-on-resources-table'} + +steps = [ + step( + """ + ALTER TABLE resources ADD COLUMN + public INTEGER NOT NULL DEFAULT 0 CHECK (public=0 or public=1) + """, + "ALTER TABLE resources DROP COLUMN public") +] diff --git a/gn_auth/migrations/auth/20221219_01_CI3tN-create-oauth2-clients-table.py b/gn_auth/migrations/auth/20221219_01_CI3tN-create-oauth2-clients-table.py new file mode 100644 index 0000000..475be01 --- /dev/null +++ b/gn_auth/migrations/auth/20221219_01_CI3tN-create-oauth2-clients-table.py @@ -0,0 +1,25 @@ +""" +create oauth2_clients table +""" + +from yoyo import step + +__depends__ = {'20221208_01_sSdHz-add-public-column-to-resources-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS oauth2_clients( + client_id TEXT NOT NULL, + client_secret TEXT NOT NULL, + client_id_issued_at INTEGER NOT NULL, + client_secret_expires_at INTEGER NOT NULL, + client_metadata TEXT, + user_id TEXT NOT NULL, + PRIMARY KEY(client_id), + FOREIGN KEY(user_id) REFERENCES users(user_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS oauth2_clients") +] diff --git a/gn_auth/migrations/auth/20221219_02_buSEU-create-oauth2-tokens-table.py b/gn_auth/migrations/auth/20221219_02_buSEU-create-oauth2-tokens-table.py new file mode 100644 index 0000000..778282b --- /dev/null +++ b/gn_auth/migrations/auth/20221219_02_buSEU-create-oauth2-tokens-table.py @@ -0,0 +1,31 @@ +""" +create oauth2_tokens table +""" + +from yoyo import step + +__depends__ = {'20221219_01_CI3tN-create-oauth2-clients-table'} + +steps = [ + step( + """ + CREATE TABLE oauth2_tokens( + token_id TEXT NOT NULL, + client_id TEXT NOT NULL, + token_type TEXT NOT NULL, + access_token TEXT UNIQUE NOT NULL, + refresh_token TEXT, + scope TEXT, + revoked INTEGER CHECK (revoked = 0 or revoked = 1), + issued_at INTEGER NOT NULL, + expires_in INTEGER NOT NULL, + user_id TEXT NOT NULL, + PRIMARY KEY(token_id), + FOREIGN KEY (client_id) REFERENCES oauth2_clients(client_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (user_id) REFERENCES users(user_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS oauth2_tokens") +] diff --git a/gn_auth/migrations/auth/20221219_03_PcTrb-create-authorisation-code-table.py b/gn_auth/migrations/auth/20221219_03_PcTrb-create-authorisation-code-table.py new file mode 100644 index 0000000..1683f87 --- /dev/null +++ b/gn_auth/migrations/auth/20221219_03_PcTrb-create-authorisation-code-table.py @@ -0,0 +1,31 @@ +""" +create authorisation_code table +""" + +from yoyo import step + +__depends__ = {'20221219_02_buSEU-create-oauth2-tokens-table'} + +steps = [ + step( + """ + CREATE TABLE authorisation_code ( + code_id TEXT NOT NULL, + code TEXT UNIQUE NOT NULL, + client_id NOT NULL, + redirect_uri TEXT, + scope TEXT, + nonce TEXT, + auth_time INTEGER NOT NULL, + code_challenge TEXT, + code_challenge_method TEXT, + user_id TEXT NOT NULL, + PRIMARY KEY (code_id), + FOREIGN KEY (client_id) REFERENCES oauth2_clients(client_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (user_id) REFERENCES users(user_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS authorisation_code") +] diff --git a/gn_auth/migrations/auth/20230111_01_Wd6IZ-remove-create-group-privilege-from-group-leader.py b/gn_auth/migrations/auth/20230111_01_Wd6IZ-remove-create-group-privilege-from-group-leader.py new file mode 100644 index 0000000..7e7fda2 --- /dev/null +++ b/gn_auth/migrations/auth/20230111_01_Wd6IZ-remove-create-group-privilege-from-group-leader.py @@ -0,0 +1,40 @@ +""" +remove 'create-group' privilege from group-leader. +""" + +from yoyo import step + +__depends__ = {'20221219_03_PcTrb-create-authorisation-code-table'} + +steps = [ + step( + """ + DELETE FROM role_privileges + WHERE role_id='a0e67630-d502-4b9f-b23f-6805d0f30e30' + AND privilege_id='4842e2aa-38b9-4349-805e-0a99a9cf8bff' + """, + """ + INSERT INTO role_privileges VALUES + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', + '4842e2aa-38b9-4349-805e-0a99a9cf8bff') + """), + step( + """ + INSERT INTO roles(role_id, role_name, user_editable) VALUES + ('ade7e6b0-ba9c-4b51-87d0-2af7fe39a347', 'group-creator', '0') + """, + """ + DELETE FROM roles WHERE role_id='ade7e6b0-ba9c-4b51-87d0-2af7fe39a347' + """), + step( + """ + INSERT INTO role_privileges VALUES + ('ade7e6b0-ba9c-4b51-87d0-2af7fe39a347', + '4842e2aa-38b9-4349-805e-0a99a9cf8bff') + """, + """ + DELETE FROM role_privileges + WHERE role_id='ade7e6b0-ba9c-4b51-87d0-2af7fe39a347' + AND privilege_id='4842e2aa-38b9-4349-805e-0a99a9cf8bff' + """) +] diff --git a/gn_auth/migrations/auth/20230116_01_KwuJ3-rework-privileges-schema.py b/gn_auth/migrations/auth/20230116_01_KwuJ3-rework-privileges-schema.py new file mode 100644 index 0000000..1ef5ab0 --- /dev/null +++ b/gn_auth/migrations/auth/20230116_01_KwuJ3-rework-privileges-schema.py @@ -0,0 +1,111 @@ +""" +rework privileges schema +""" +import contextlib + +from yoyo import step + +__depends__ = {'20230111_01_Wd6IZ-remove-create-group-privilege-from-group-leader'} + +privileges = ( # format: (original_id, original_name, new_id, category) + ("13ec2a94-4f1a-442d-aad2-936ad6dd5c57", "delete-group", + "system:group:delete-group", "group-management"), + ("1c59eff5-9336-4ed2-a166-8f70d4cb012e", "delete-role", + "group:role:delete-role", "role-management"), + ("1fe61370-cae9-4983-bd6c-ce61050c510f", "delete-any-user", + "system:user:delete-user", "user-management"), + ("221660b1-df05-4be1-b639-f010269dbda9", "create-role", + "group:role:create-role", "role-management"), + ("2f980855-959b-4339-b80e-25d1ec286e21", "edit-resource", + "group:resource:edit-resource", "resource-management"), + ("3ebfe79c-d159-4629-8b38-772cf4bc2261", "view-group", + "system:group:view-group", "group-management"), + ("4842e2aa-38b9-4349-805e-0a99a9cf8bff", "create-group", + "system:group:create-group", "group-management"), + ("5103cc68-96f8-4ebb-83a4-a31692402c9b", "assign-role", + "group:user:assign-role", "role-management"), + ("519db546-d44e-4fdc-9e4e-25aa67548ab3", "masquerade", + "system:user:masquerade", "system-admin"), + ("52576370-b3c7-4e6a-9f7e-90e9dbe24d8f", "edit-group", + "system:group:edit-group", "group-management"), + ("7bcca363-cba9-4169-9e31-26bdc6179b28", "edit-role", + "group:role:edit-role", "role-management"), + ("7f261757-3211-4f28-a43f-a09b800b164d", "view-resource", + "group:resource:view-resource", "resource-management"), + ("80f11285-5079-4ec0-907c-06509f88a364", "assign-group-leader", + "system:user:assign-group-leader", "group-management"), + ("aa25b32a-bff2-418d-b0a2-e26b4a8f089b", "create-resource", + "group:resource:create-resource", "resource-management"), + ("ae4add8c-789a-4d11-a6e9-a306470d83d9", "add-group-member", + "group:user:add-group-member", "group-management"), + ("d2a070fd-e031-42fb-ba41-d60cf19e5d6d", "delete-resource", + "group:resource:delete-resource", "resource-management"), + ("d4afe2b3-4ca0-4edd-b37d-966535b5e5bd", "transfer-group-leadership", + "system:group:transfer-group-leader", "group-management"), + ("e7252301-6ee0-43ba-93ef-73b607cf06f6", "reset-any-password", + "system:user:reset-password", "user-management"), + ("f1bd3f42-567e-4965-9643-6d1a52ddee64", "remove-group-member", + "group:user:remove-group-member", "group-management")) + +def rework_privileges_table(cursor): + "rework the schema" + cursor.executemany( + ("UPDATE privileges SET privilege_id=:id " + "WHERE privilege_id=:old_id"), + ({"id": row[2], "old_id": row[0]} for row in privileges)) + cursor.execute("ALTER TABLE privileges DROP COLUMN privilege_category") + cursor.execute("ALTER TABLE privileges DROP COLUMN privilege_name") + +def restore_privileges_table(cursor): + "restore the schema" + cursor.execute(( + "CREATE TABLE privileges_restore (" + " privilege_id TEXT PRIMARY KEY," + " privilege_name TEXT NOT NULL," + " privilege_category TEXT NOT NULL DEFAULT 'common'," + " privilege_description TEXT" + ")")) + id_dict = {row[2]: {"id": row[0], "name": row[1], "cat": row[3]} + for row in privileges} + cursor.execute( + "SELECT privilege_id, privilege_description FROM privileges") + params = ({**id_dict[row[0]], "desc": row[1]} for row in cursor.fetchall()) + cursor.executemany( + "INSERT INTO privileges_restore VALUES (:id, :name, :cat, :desc)", + params) + cursor.execute("DROP TABLE privileges") + cursor.execute("ALTER TABLE privileges_restore RENAME TO privileges") + +def update_privilege_ids_in_role_privileges(cursor): + """Update the ids to new form.""" + cursor.executemany( + ("UPDATE role_privileges SET privilege_id=:new_id " + "WHERE privilege_id=:old_id"), + ({"new_id": row[2], "old_id": row[0]} for row in privileges)) + +def restore_privilege_ids_in_role_privileges(cursor): + """Restore original ids""" + cursor.executemany( + ("UPDATE role_privileges SET privilege_id=:old_id " + "WHERE privilege_id=:new_id"), + ({"new_id": row[2], "old_id": row[0]} for row in privileges)) + +def change_schema(conn): + """Change the privileges schema and IDs""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute("PRAGMA foreign_keys=OFF") + rework_privileges_table(cursor) + update_privilege_ids_in_role_privileges(cursor) + cursor.execute("PRAGMA foreign_keys=ON") + +def restore_schema(conn): + """Change the privileges schema and IDs""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute("PRAGMA foreign_keys=OFF") + restore_privilege_ids_in_role_privileges(cursor) + restore_privileges_table(cursor) + cursor.execute("PRAGMA foreign_keys=ON") + +steps = [ + step(change_schema, restore_schema) +] diff --git a/gn_auth/migrations/auth/20230207_01_r0bkZ-create-group-join-requests-table.py b/gn_auth/migrations/auth/20230207_01_r0bkZ-create-group-join-requests-table.py new file mode 100644 index 0000000..ceae5ea --- /dev/null +++ b/gn_auth/migrations/auth/20230207_01_r0bkZ-create-group-join-requests-table.py @@ -0,0 +1,29 @@ +""" +Create group_requests table +""" + +from yoyo import step + +__depends__ = {'20230116_01_KwuJ3-rework-privileges-schema'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS group_join_requests( + request_id TEXT NOT NULL, + group_id TEXT NOT NULL, + requester_id TEXT NOT NULL, + timestamp REAL NOT NULL, + status TEXT NOT NULL DEFAULT 'PENDING', + message TEXT, + PRIMARY KEY(request_id, group_id), + FOREIGN KEY(group_id) REFERENCES groups(group_id) + ON UPDATE CASCADE ON DELETE CASCADE, + FOREIGN KEY (requester_id) REFERENCES users(user_id) + ON UPDATE CASCADE ON DELETE CASCADE, + UNIQUE(group_id, requester_id), + CHECK (status IN ('PENDING', 'ACCEPTED', 'REJECTED')) + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS group_join_requests") +] diff --git a/gn_auth/migrations/auth/20230210_01_8xMa1-system-admin-privileges-for-data-distribution.py b/gn_auth/migrations/auth/20230210_01_8xMa1-system-admin-privileges-for-data-distribution.py new file mode 100644 index 0000000..8b406a6 --- /dev/null +++ b/gn_auth/migrations/auth/20230210_01_8xMa1-system-admin-privileges-for-data-distribution.py @@ -0,0 +1,22 @@ +""" +System admin privileges for data distribution + +These privileges are focussed on allowing the system administrator to link the +datasets and traits in the main database to specific groups in the auth system. +""" + +from yoyo import step + +__depends__ = {'20230207_01_r0bkZ-create-group-join-requests-table'} + +steps = [ + step( + """ + INSERT INTO privileges VALUES + ('system:data:link-to-group', 'Link a dataset or trait to a group.') + """, + """ + DELETE FROM privileges WHERE privilege_id IN + ('system:data:link-to-group') + """) +] diff --git a/gn_auth/migrations/auth/20230210_02_lDK14-create-system-admin-role.py b/gn_auth/migrations/auth/20230210_02_lDK14-create-system-admin-role.py new file mode 100644 index 0000000..9b3fc2b --- /dev/null +++ b/gn_auth/migrations/auth/20230210_02_lDK14-create-system-admin-role.py @@ -0,0 +1,38 @@ +""" +Create system-admin role +""" +import uuid +from contextlib import closing + +from yoyo import step + +__depends__ = {'20230210_01_8xMa1-system-admin-privileges-for-data-distribution'} + +def create_sys_admin_role(conn): + with closing(conn.cursor()) as cursor: + role_id = uuid.uuid4() + cursor.execute( + "INSERT INTO roles VALUES (?, 'system-administrator', '0')", + (str(role_id),)) + + cursor.executemany( + "INSERT INTO role_privileges VALUES (:role_id, :privilege_id)", + ({"role_id": f"{role_id}", "privilege_id": priv} + for priv in ( + "system:data:link-to-group", + "system:group:create-group", + "system:group:delete-group", + "system:group:edit-group", + "system:group:transfer-group-leader", + "system:group:view-group", + "system:user:assign-group-leader", + "system:user:delete-user", + "system:user:masquerade", + "system:user:reset-password"))) + +def drop_sys_admin_role(conn): + pass + +steps = [ + step(create_sys_admin_role, drop_sys_admin_role) +] diff --git a/gn_auth/migrations/auth/20230306_01_pRfxl-add-system-user-list-privilege.py b/gn_auth/migrations/auth/20230306_01_pRfxl-add-system-user-list-privilege.py new file mode 100644 index 0000000..84bbd49 --- /dev/null +++ b/gn_auth/migrations/auth/20230306_01_pRfxl-add-system-user-list-privilege.py @@ -0,0 +1,26 @@ +""" +Add system:user:list privilege +""" +import contextlib + +from yoyo import step + +__depends__ = {'20230210_02_lDK14-create-system-admin-role'} + +def insert_users_list_priv(conn): + """Create a new 'system:user:list' privilege.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "INSERT INTO privileges(privilege_id, privilege_description) " + "VALUES('system:user:list', 'List users in the system') " + "ON CONFLICT (privilege_id) DO NOTHING") + +def delete_users_list_priv(conn): + """Delete the new 'system:user:list' privilege.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "DELETE FROM privileges WHERE privilege_id='system:user:list'") + +steps = [ + step(insert_users_list_priv, delete_users_list_priv) +] diff --git a/gn_auth/migrations/auth/20230306_02_7GnRY-add-system-user-list-privilege-to-system-administrator-and-group-leader-roles.py b/gn_auth/migrations/auth/20230306_02_7GnRY-add-system-user-list-privilege-to-system-administrator-and-group-leader-roles.py new file mode 100644 index 0000000..3caad55 --- /dev/null +++ b/gn_auth/migrations/auth/20230306_02_7GnRY-add-system-user-list-privilege-to-system-administrator-and-group-leader-roles.py @@ -0,0 +1,42 @@ +""" +Add system:user:list privilege to system-administrator and group-leader roles. +""" +import uuid +import contextlib + +from yoyo import step + +__depends__ = {'20230306_01_pRfxl-add-system-user-list-privilege'} + +def role_ids(cursor): + """Get role ids from names""" + cursor.execute( + "SELECT * FROM roles WHERE role_name IN " + "('system-administrator', 'group-leader')") + return (uuid.UUID(row[0]) for row in cursor.fetchall()) + +def add_privilege_to_roles(conn): + """ + Add 'system:user:list' privilege to 'system-administrator' and + 'group-leader' roles.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.executemany( + "INSERT INTO role_privileges(role_id,privilege_id) " + "VALUES(?, ?)", + tuple((str(role_id), "system:user:list") + for role_id in role_ids(cursor))) + +def del_privilege_from_roles(conn): + """ + Delete 'system:user:list' privilege to 'system-administrator' and + 'group-leader' roles. + """ + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "DELETE FROM role_privileges WHERE " + "role_id IN (?, ?) AND privilege_id='system:user:list'", + tuple(str(role_id) for role_id in role_ids(cursor))) + +steps = [ + step(add_privilege_to_roles, del_privilege_from_roles) +] diff --git a/gn_auth/migrations/auth/20230322_01_0dDZR-create-linked-phenotype-data-table.py b/gn_auth/migrations/auth/20230322_01_0dDZR-create-linked-phenotype-data-table.py new file mode 100644 index 0000000..647325f --- /dev/null +++ b/gn_auth/migrations/auth/20230322_01_0dDZR-create-linked-phenotype-data-table.py @@ -0,0 +1,30 @@ +""" +Create linked-phenotype-data table +""" + +from yoyo import step + +__depends__ = {'20230306_02_7GnRY-add-system-user-list-privilege-to-system-administrator-and-group-leader-roles'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS linked_phenotype_data + -- Link the data in MariaDB to user groups in the auth system + ( + data_link_id TEXT NOT NULL PRIMARY KEY, -- A new ID for the auth system + group_id TEXT NOT NULL, -- The user group the data is linked to + SpeciesId TEXT NOT NULL, -- The species in MariaDB + InbredSetId TEXT NOT NULL, -- The traits group in MariaDB + PublishFreezeId TEXT NOT NULL, -- The dataset Id in MariaDB + dataset_name TEXT, -- dataset Name in MariaDB + dataset_fullname, -- dataset FullName in MariaDB + dataset_shortname, -- dataset ShortName in MariaDB + PublishXRefId TEXT NOT NULL, -- The trait's ID in MariaDB + FOREIGN KEY (group_id) + REFERENCES groups(group_id) ON UPDATE CASCADE ON DELETE RESTRICT + UNIQUE (SpeciesId, InbredSetId, PublishFreezeId, PublishXRefId) + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS linked_phenotype_data") +] diff --git a/gn_auth/migrations/auth/20230322_02_Ll854-create-phenotype-resources-table.py b/gn_auth/migrations/auth/20230322_02_Ll854-create-phenotype-resources-table.py new file mode 100644 index 0000000..7c9e986 --- /dev/null +++ b/gn_auth/migrations/auth/20230322_02_Ll854-create-phenotype-resources-table.py @@ -0,0 +1,29 @@ +""" +Create phenotype_resources table +""" + +from yoyo import step + +__depends__ = {'20230322_01_0dDZR-create-linked-phenotype-data-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS phenotype_resources + -- Link phenotype data to specific resources + ( + group_id TEXT NOT NULL, + resource_id TEXT NOT NULL, -- A resource can have multiple data items + data_link_id TEXT NOT NULL, + PRIMARY KEY(group_id, resource_id, data_link_id), + UNIQUE (data_link_id), -- ensure data is linked to only one resource + FOREIGN KEY (group_id, resource_id) + REFERENCES resources(group_id, resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (data_link_id) + REFERENCES linked_phenotype_data(data_link_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS phenotype_resources") +] diff --git a/gn_auth/migrations/auth/20230404_01_VKxXg-create-linked-genotype-data-table.py b/gn_auth/migrations/auth/20230404_01_VKxXg-create-linked-genotype-data-table.py new file mode 100644 index 0000000..02e8718 --- /dev/null +++ b/gn_auth/migrations/auth/20230404_01_VKxXg-create-linked-genotype-data-table.py @@ -0,0 +1,29 @@ +""" +Create linked genotype data table +""" + +from yoyo import step + +__depends__ = {'20230322_02_Ll854-create-phenotype-resources-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS linked_genotype_data + -- Link genotype data in MariaDB to user groups in auth system + ( + data_link_id TEXT NOT NULL PRIMARY KEY, -- A new ID for the auth system + group_id TEXT NOT NULL, -- The user group the data is linked to + SpeciesId TEXT NOT NULL, -- The species in MariaDB + InbredSetId TEXT NOT NULL, -- The traits group in MariaDB + GenoFreezeId TEXT NOT NULL, -- The dataset Id in MariaDB + dataset_name TEXT, -- dataset Name in MariaDB + dataset_fullname, -- dataset FullName in MariaDB + dataset_shortname, -- dataset ShortName in MariaDB + FOREIGN KEY (group_id) + REFERENCES groups(group_id) ON UPDATE CASCADE ON DELETE RESTRICT + UNIQUE (SpeciesId, InbredSetId, GenoFreezeId) + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS linked_genotype_data") +] diff --git a/gn_auth/migrations/auth/20230404_02_la33P-create-genotype-resources-table.py b/gn_auth/migrations/auth/20230404_02_la33P-create-genotype-resources-table.py new file mode 100644 index 0000000..1a865e0 --- /dev/null +++ b/gn_auth/migrations/auth/20230404_02_la33P-create-genotype-resources-table.py @@ -0,0 +1,29 @@ +""" +Create genotype resources table +""" + +from yoyo import step + +__depends__ = {'20230404_01_VKxXg-create-linked-genotype-data-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS genotype_resources + -- Link genotype data to specific resource + ( + group_id TEXT NOT NULL, + resource_id TEXT NOT NULL, -- A resource can have multiple items + data_link_id TEXT NOT NULL, + PRIMARY KEY (group_id, resource_id, data_link_id), + UNIQUE (data_link_id) -- ensure data is linked to single resource + FOREIGN KEY (group_id, resource_id) + REFERENCES resources(group_id, resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (data_link_id) + REFERENCES linked_genotype_data(data_link_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS genotype_resources") +] diff --git a/gn_auth/migrations/auth/20230410_01_8mwaf-create-linked-mrna-data-table.py b/gn_auth/migrations/auth/20230410_01_8mwaf-create-linked-mrna-data-table.py new file mode 100644 index 0000000..db9a6bf --- /dev/null +++ b/gn_auth/migrations/auth/20230410_01_8mwaf-create-linked-mrna-data-table.py @@ -0,0 +1,30 @@ +""" +Create linked mrna data table +""" + +from yoyo import step + +__depends__ = {'20230404_02_la33P-create-genotype-resources-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS linked_mrna_data + -- Link mRNA Assay data in MariaDB to user groups in auth system + ( + data_link_id TEXT NOT NULL PRIMARY KEY, -- A new ID for the auth system + group_id TEXT NOT NULL, -- The user group the data is linked to + SpeciesId TEXT NOT NULL, -- The species in MariaDB + InbredSetId TEXT NOT NULL, -- The traits group in MariaDB + ProbeFreezeId TEXT NOT NULL, -- The study ID in MariaDB + ProbeSetFreezeId TEXT NOT NULL, -- The dataset Id in MariaDB + dataset_name TEXT, -- dataset Name in MariaDB + dataset_fullname, -- dataset FullName in MariaDB + dataset_shortname, -- dataset ShortName in MariaDB + FOREIGN KEY (group_id) + REFERENCES groups(group_id) ON UPDATE CASCADE ON DELETE RESTRICT + UNIQUE (SpeciesId, InbredSetId, ProbeFreezeId, ProbeSetFreezeId) + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS linked_mrna_data") +] diff --git a/gn_auth/migrations/auth/20230410_02_WZqSf-create-mrna-resources-table.py b/gn_auth/migrations/auth/20230410_02_WZqSf-create-mrna-resources-table.py new file mode 100644 index 0000000..2ad1056 --- /dev/null +++ b/gn_auth/migrations/auth/20230410_02_WZqSf-create-mrna-resources-table.py @@ -0,0 +1,28 @@ +""" +Create mRNA resources table +""" + +from yoyo import step + +__depends__ = {'20230410_01_8mwaf-create-linked-mrna-data-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS mrna_resources + -- Link mRNA data to specific resource + ( + group_id TEXT NOT NULL, + resource_id TEXT NOT NULL, -- A resource can have multiple items + data_link_id TEXT NOT NULL, + PRIMARY KEY (resource_id, data_link_id), + UNIQUE (data_link_id) -- ensure data is linked to single resource + FOREIGN KEY (group_id, resource_id) + REFERENCES resources(group_id, resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (data_link_id) REFERENCES linked_mrna_data(data_link_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS mrna_resources") +] diff --git a/gn_auth/migrations/auth/20230907_01_pjnxz-refactor-add-resource-ownership-table.py b/gn_auth/migrations/auth/20230907_01_pjnxz-refactor-add-resource-ownership-table.py new file mode 100644 index 0000000..37fcfe7 --- /dev/null +++ b/gn_auth/migrations/auth/20230907_01_pjnxz-refactor-add-resource-ownership-table.py @@ -0,0 +1,32 @@ +""" +refactor: add resource_ownership table +""" + +from yoyo import step + +__depends__ = {'20230410_02_WZqSf-create-mrna-resources-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS resource_ownership( + -- This table links resources to groups, where relevant + group_id TEXT NOT NULL, + resource_id TEXT NOT NULL, + PRIMARY KEY(group_id, resource_id), + FOREIGN KEY(group_id) + REFERENCES groups(group_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(resource_id) + REFERENCES resources(resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS resource_ownership"), + step(# Copy over data + """ + INSERT INTO resource_ownership + SELECT group_id, resource_id FROM resources + """ + ) +] diff --git a/gn_auth/migrations/auth/20230907_02_Enicg-refactor-add-system-and-group-resource-categories.py b/gn_auth/migrations/auth/20230907_02_Enicg-refactor-add-system-and-group-resource-categories.py new file mode 100644 index 0000000..c4397c9 --- /dev/null +++ b/gn_auth/migrations/auth/20230907_02_Enicg-refactor-add-system-and-group-resource-categories.py @@ -0,0 +1,29 @@ +""" +refactor: add 'system' and 'group' resource categories +""" + +from yoyo import step + +__depends__ = {'20230907_01_pjnxz-refactor-add-resource-ownership-table'} + +steps = [ + step( + """ + INSERT INTO resource_categories VALUES + ('aa3d787f-af6a-44fa-9b0b-c82d40e54ad2', + 'system', + 'The overall system.', + '{"default-access-level": "public-read"}'), + ('1e0f70ee-add5-4358-8c6c-43de77fa4cce', + 'group', + 'A group resource.', + '{}') + """, + """ + DELETE FROM resource_categories + WHERE resource_category_id IN ( + 'aa3d787f-af6a-44fa-9b0b-c82d40e54ad2', + '1e0f70ee-add5-4358-8c6c-43de77fa4cce' + ) + """) +] diff --git a/gn_auth/migrations/auth/20230907_03_BwAmf-refactor-drop-group-id-from-resources-table.py b/gn_auth/migrations/auth/20230907_03_BwAmf-refactor-drop-group-id-from-resources-table.py new file mode 100644 index 0000000..0f491c2 --- /dev/null +++ b/gn_auth/migrations/auth/20230907_03_BwAmf-refactor-drop-group-id-from-resources-table.py @@ -0,0 +1,325 @@ +""" +refactor: drop 'group_id' from 'resources' table. +""" + +import sqlite3 +from yoyo import step + +__depends__ = {'20230907_02_Enicg-refactor-add-system-and-group-resource-categories'} + +def drop_group_id_from_group_user_roles_on_resources(conn): + conn.execute( + "ALTER TABLE group_user_roles_on_resources " + "RENAME TO group_user_roles_on_resources_bkp") + conn.execute( + """ + CREATE TABLE 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 + """) + conn.execute( + "INSERT INTO group_user_roles_on_resources " + "(group_id, user_id, role_id, resource_id)" + "SELECT group_id, 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") + +def drop_group_id_from_mrna_resources(conn): + conn.execute("ALTER TABLE mrna_resources RENAME TO mrna_resources_bkp") + conn.execute( + """ + CREATE TABLE IF NOT EXISTS mrna_resources + -- Link mRNA data to specific resource + ( + resource_id TEXT NOT NULL, -- A resource can have multiple items + data_link_id TEXT NOT NULL, + PRIMARY KEY (resource_id, data_link_id), + UNIQUE (data_link_id) -- ensure data is linked to single resource + FOREIGN KEY (resource_id) + REFERENCES resources(resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (data_link_id) REFERENCES linked_mrna_data(data_link_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + conn.execute( + "INSERT INTO mrna_resources " + "SELECT resource_id, data_link_id FROM mrna_resources_bkp") + conn.execute("DROP TABLE IF EXISTS mrna_resources_bkp") + +def drop_group_id_from_genotype_resources(conn): + conn.execute( + "ALTER TABLE genotype_resources RENAME TO genotype_resources_bkp") + conn.execute( + """ + CREATE TABLE IF NOT EXISTS genotype_resources + -- Link genotype data to specific resource + ( + resource_id TEXT NOT NULL, -- A resource can have multiple items + data_link_id TEXT NOT NULL, + PRIMARY KEY (resource_id, data_link_id), + UNIQUE (data_link_id) -- ensure data is linked to single resource + FOREIGN KEY (resource_id) + REFERENCES resources(resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (data_link_id) + REFERENCES linked_genotype_data(data_link_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + conn.execute( + "INSERT INTO genotype_resources " + "SELECT resource_id, data_link_id FROM genotype_resources_bkp") + conn.execute("DROP TABLE IF EXISTS genotype_resources_bkp") + +def drop_group_id_from_phenotype_resources(conn): + conn.execute( + "ALTER TABLE phenotype_resources RENAME TO phenotype_resources_bkp") + conn.execute( + """ + CREATE TABLE IF NOT EXISTS phenotype_resources + -- Link phenotype data to specific resources + ( + resource_id TEXT NOT NULL, -- A resource can have multiple data items + data_link_id TEXT NOT NULL, + PRIMARY KEY(resource_id, data_link_id), + UNIQUE (data_link_id), -- ensure data is linked to only one resource + FOREIGN KEY (resource_id) + REFERENCES resources(resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (data_link_id) + REFERENCES linked_phenotype_data(data_link_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + conn.execute( + "INSERT INTO phenotype_resources " + "SELECT resource_id, data_link_id FROM phenotype_resources_bkp") + conn.execute("DROP TABLE IF EXISTS phenotype_resources_bkp") + +def drop_group_id_from_resources_table(conn): + conn.row_factory = sqlite3.Row + conn.execute("PRAGMA foreign_keys = OFF") + conn.execute( + """ + CREATE TABLE IF NOT EXISTS resources_new( + resource_id TEXT NOT NULL, + resource_name TEXT NOT NULL UNIQUE, + resource_category_id TEXT NOT NULL, + public INTEGER NOT NULL DEFAULT 0 CHECK (public=0 or public=1), + PRIMARY KEY(resource_id), + FOREIGN KEY(resource_category_id) + REFERENCES resource_categories(resource_category_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + conn.execute( + "INSERT INTO resources_new " + "SELECT resource_id, resource_name, resource_category_id, public " + "FROM resources") + conn.execute("DROP TABLE IF EXISTS resources") + conn.execute("ALTER TABLE resources_new RENAME TO resources") + + drop_group_id_from_mrna_resources(conn) + drop_group_id_from_genotype_resources(conn) + drop_group_id_from_phenotype_resources(conn) + drop_group_id_from_group_user_roles_on_resources(conn) + + conn.execute("PRAGMA foreign_key_check") + conn.execute("PRAGMA foreign_keys = ON") + +def restore_group_id_from_group_user_roles_on_resources(conn): + conn.execute( + "ALTER TABLE group_user_roles_on_resources " + "RENAME TO group_user_roles_on_resources_bkp") + conn.execute( + """ + CREATE TABLE 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 (group_id, resource_id) + REFERENCES resources(group_id, resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + conn.execute( + "INSERT INTO group_user_roles_on_resources " + "(group_id, user_id, role_id, resource_id)" + "SELECT group_id, 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") + +def restore_group_id_from_mrna_resources(conn, resource_group_map): + conn.execute("ALTER TABLE mrna_resources RENAME TO mrna_resources_bkp") + conn.execute( + """ + CREATE TABLE IF NOT EXISTS mrna_resources + -- Link mRNA data to specific resource + ( + group_id TEXT NOT NULL, + resource_id TEXT NOT NULL, -- A resource can have multiple items + data_link_id TEXT NOT NULL, + PRIMARY KEY (resource_id, data_link_id), + UNIQUE (data_link_id) -- ensure data is linked to single resource + FOREIGN KEY (group_id, resource_id) + REFERENCES resources(group_id, resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (data_link_id) REFERENCES linked_mrna_data(data_link_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + + cursor = conn.cursor() + cursor.execute("SELECT * FROM mrna_resources_bkp") + resources = tuple({ + "group_id": resource_group_map[row["resource_id"]], + **dict(row) + } for row in cursor.fetchall()) + cursor.executemany( + "INSERT INTO mrna_resources(group_id, resource_id, data_link_id) " + "VALUES(:group_id, :resource_id, :data_link_id)", + resources) + conn.execute("DROP TABLE IF EXISTS mrna_resources_bkp") + +def restore_group_id_from_genotype_resources(conn, resource_group_map): + conn.execute( + "ALTER TABLE genotype_resources RENAME TO genotype_resources_bkp") + conn.execute( + """ + CREATE TABLE IF NOT EXISTS genotype_resources + -- Link genotype data to specific resource + ( + group_id TEXT NOT NULL, + resource_id TEXT NOT NULL, -- A resource can have multiple items + data_link_id TEXT NOT NULL, + PRIMARY KEY (group_id, resource_id, data_link_id), + UNIQUE (data_link_id) -- ensure data is linked to single resource + FOREIGN KEY (group_id, resource_id) + REFERENCES resources(group_id, resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (data_link_id) + REFERENCES linked_genotype_data(data_link_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + + cursor = conn.cursor() + cursor.execute("SELECT * FROM genotype_resources_bkp") + resources = tuple({ + "group_id": resource_group_map[row["resource_id"]], + **dict(row) + } for row in cursor.fetchall()) + cursor.executemany( + "INSERT INTO genotype_resources(group_id, resource_id, data_link_id) " + "VALUES(:group_id, :resource_id, :data_link_id)", + resources) + conn.execute("DROP TABLE IF EXISTS genotype_resources_bkp") + +def restore_group_id_from_phenotype_resources(conn, resource_group_map): + conn.execute( + "ALTER TABLE phenotype_resources RENAME TO phenotype_resources_bkp") + conn.execute( + """ + CREATE TABLE IF NOT EXISTS phenotype_resources + -- Link phenotype data to specific resources + ( + group_id TEXT NOT NULL, + resource_id TEXT NOT NULL, -- A resource can have multiple data items + data_link_id TEXT NOT NULL, + PRIMARY KEY(group_id, resource_id, data_link_id), + UNIQUE (data_link_id), -- ensure data is linked to only one resource + FOREIGN KEY (group_id, resource_id) + REFERENCES resources(group_id, resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (data_link_id) + REFERENCES linked_phenotype_data(data_link_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + + cursor = conn.cursor() + cursor.execute("SELECT * FROM phenotype_resources_bkp") + resources = tuple({ + "group_id": resource_group_map[row["resource_id"]], + **dict(row) + } for row in cursor.fetchall()) + cursor.executemany( + "INSERT INTO phenotype_resources(group_id, resource_id, data_link_id) " + "VALUES(:group_id, :resource_id, :data_link_id)", + resources) + conn.execute("DROP TABLE IF EXISTS phenotype_resources_bkp") + +def restore_group_id_to_resources_table(conn): + conn.row_factory = sqlite3.Row + conn.execute("PRAGMA foreign_keys = OFF") + + cursor = conn.cursor() + cursor.execute("ALTER TABLE resources RENAME TO resources_bkp") + cursor.execute( + "SELECT r.*, ro.group_id FROM resources_bkp AS r " + "INNER JOIN resource_ownership AS ro " + "ON r.resource_id=ro.resource_id") + group_resources = tuple(dict(row) for row in cursor.fetchall()) + cursor.execute( + """ + CREATE TABLE IF NOT EXISTS resources( + group_id TEXT NOT NULL, + resource_id TEXT NOT NULL, + resource_name TEXT NOT NULL UNIQUE, + resource_category_id TEXT NOT NULL, + public INTEGER NOT NULL DEFAULT 0 CHECK (public=0 or public=1), + PRIMARY KEY(group_id, resource_id), + FOREIGN KEY(group_id) + REFERENCES groups(group_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(resource_category_id) + REFERENCES resource_categories(resource_category_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + cursor.executemany( + "INSERT INTO resources" + "(group_id, resource_id, resource_name, resource_category_id)" + "VALUES " + "(:group_id, :resource_id, :resource_name, :resource_category_id)", + group_resources) + cursor.execute("DROP TABLE IF EXISTS resources_bkp") + + resource_group_map = { + res["resource_id"]: res["group_id"] + for res in group_resources + } + restore_group_id_from_group_user_roles_on_resources(conn) + restore_group_id_from_mrna_resources(conn, resource_group_map) + restore_group_id_from_genotype_resources(conn, resource_group_map) + restore_group_id_from_phenotype_resources(conn, resource_group_map) + + conn.execute("PRAGMA foreign_key_check") + conn.execute("PRAGMA foreign_keys = ON") + +steps = [ + step( + drop_group_id_from_resources_table, restore_group_id_to_resources_table) +] diff --git a/gn_auth/migrations/auth/20230907_04_3LnrG-refactor-create-group-resources-table.py b/gn_auth/migrations/auth/20230907_04_3LnrG-refactor-create-group-resources-table.py new file mode 100644 index 0000000..a26834a --- /dev/null +++ b/gn_auth/migrations/auth/20230907_04_3LnrG-refactor-create-group-resources-table.py @@ -0,0 +1,58 @@ +""" +refactor: create 'group_resources' table. +""" + +import uuid +import random +import string + +import sqlite3 +from yoyo import step + +__depends__ = {'20230907_03_BwAmf-refactor-drop-group-id-from-resources-table'} + +def randstr(length: int = 5): + """Generate random string.""" + return "".join(random.choices( + string.ascii_letters + string.digits, k=length)) + +def create_and_link_resources_for_existing_groups(conn): + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + cursor.execute("SELECT group_id, group_name FROM groups") + resources = tuple({ + "group_id": row["group_id"], + "resource_id": str(uuid.uuid4()), + "resource_name": f"{randstr(10)}: {row['group_name']}", + "resource_category_id": "1e0f70ee-add5-4358-8c6c-43de77fa4cce" + } for row in cursor.fetchall()) + cursor.executemany( + "INSERT INTO " + "resources(resource_id, resource_name, resource_category_id) " + "VALUES (:resource_id, :resource_name, :resource_category_id)", + resources) + cursor.executemany( + "INSERT INTO group_resources(resource_id, group_id) " + "VALUES (:resource_id, :group_id)", + resources) + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS group_resources( + -- Links groups to the resources of type 'group' that control access to + -- each group + resource_id TEXT NOT NULL, + group_id TEXT NOT NULL, + PRIMARY KEY(resource_id, group_id), + FOREIGN KEY (resource_id) + REFERENCES resources(resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (group_id) + REFERENCES groups(group_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS group_resources"), + step(create_and_link_resources_for_existing_groups) +] diff --git a/gn_auth/migrations/auth/20230912_01_BxrhE-add-system-resource.py b/gn_auth/migrations/auth/20230912_01_BxrhE-add-system-resource.py new file mode 100644 index 0000000..66c6461 --- /dev/null +++ b/gn_auth/migrations/auth/20230912_01_BxrhE-add-system-resource.py @@ -0,0 +1,39 @@ +""" +Add 'system' resource. +""" + +import uuid + +import sqlite3 +from yoyo import step + +__depends__ = {'20230907_04_3LnrG-refactor-create-group-resources-table'} + +def add_system_resource(conn): + """Add a system resource.""" + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + cursor.execute( + "SELECT resource_category_id FROM resource_categories " + "WHERE resource_category_key='system'") + category_id = cursor.fetchone()["resource_category_id"] + cursor.execute( + "INSERT INTO " + "resources(resource_id, resource_name, resource_category_id, public) " + "VALUES(?, ?, ?, ?)", + (str(uuid.uuid4()), "GeneNetwork System", category_id, "1")) + +def delete_system_resource(conn): + """Add a system resource.""" + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + cursor.execute( + "SELECT resource_category_id FROM resource_categories " + "WHERE resource_category_key='system'") + category_id = cursor.fetchone()["resource_category_id"] + cursor.execute("DELETE FROM resources WHERE resource_category_id = ?", + (category_id,)) + +steps = [ + step(add_system_resource, delete_system_resource) +] diff --git a/gn_auth/migrations/auth/20230912_02_hFmSn-drop-group-id-and-fix-foreign-key-references-on-group-user-roles-on-resources-table.py b/gn_auth/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/gn_auth/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) +] + diff --git a/gn_auth/migrations/auth/20230925_01_TWJuR-add-new-public-view-role.py b/gn_auth/migrations/auth/20230925_01_TWJuR-add-new-public-view-role.py new file mode 100644 index 0000000..1172034 --- /dev/null +++ b/gn_auth/migrations/auth/20230925_01_TWJuR-add-new-public-view-role.py @@ -0,0 +1,61 @@ +""" +Add new "public-view" role +""" + +import sqlite3 + +from yoyo import step + +__depends__ = {'20230912_02_hFmSn-drop-group-id-and-fix-foreign-key-references-on-group-user-roles-on-resources-table'} + +def grant_to_all_users_public_view_role(conn): + """Grant the `public-view` role to all existing users.""" + conn.row_factory = sqlite3.Row + conn.execute("PRAGMA foreign_keys = ON") + cursor = conn.cursor() + cursor.execute("SELECT user_id FROM users") + user_ids = tuple(row["user_id"] for row in cursor.fetchall()) + + cursor.execute("SELECT resource_id FROM resources WHERE public=1") + resource_ids = tuple(row["resource_id"] for row in cursor.fetchall()) + + params = tuple({ + "user_id": user_id, + "resource_id": resource_id, + "role_id": "fd88bfed-d869-4969-87f2-67c4e8446ecb" + } for user_id in user_ids for resource_id in resource_ids) + cursor.executemany( + "INSERT INTO user_roles(user_id, role_id, resource_id) " + "VALUES (:user_id, :role_id, :resource_id) ", + params) + +def revoke_from_all_users_public_view_role(conn): + """Revoke the `public-view` role from all existing users.""" + conn.execute("PRAGMA foreign_keys = ON") + conn.execute( + "DELETE FROM user_roles " + "WHERE role_id='fd88bfed-d869-4969-87f2-67c4e8446ecb'") + +steps = [ + step( + """ + INSERT INTO roles(role_id, role_name, user_editable) + VALUES('fd88bfed-d869-4969-87f2-67c4e8446ecb', 'public-view', 0) + """, + """ + DELETE FROM roles WHERE role_id='fd88bfed-d869-4969-87f2-67c4e8446ecb' + """), + step( + """ + INSERT INTO role_privileges(role_id, privilege_id) + VALUES( + 'fd88bfed-d869-4969-87f2-67c4e8446ecb', + 'group:resource:view-resource') + """, + """ + DELETE FROM role_privileges + WHERE role_id='fd88bfed-d869-4969-87f2-67c4e8446ecb' + """), + step(grant_to_all_users_public_view_role, + revoke_from_all_users_public_view_role) +] diff --git a/gn_auth/migrations/auth/20231002_01_tzxTf-link-inbredsets-to-auth-system.py b/gn_auth/migrations/auth/20231002_01_tzxTf-link-inbredsets-to-auth-system.py new file mode 100644 index 0000000..402e9a5 --- /dev/null +++ b/gn_auth/migrations/auth/20231002_01_tzxTf-link-inbredsets-to-auth-system.py @@ -0,0 +1,84 @@ +""" +link InbredSets to auth system +""" + +from yoyo import step + +__depends__ = {'20230925_01_TWJuR-add-new-public-view-role', '__init__'} + +steps = [ + step( + """ + INSERT INTO resource_categories + ( + resource_category_id, + resource_category_key, + resource_category_description, + resource_meta + ) + VALUES + ( + 'b3654600-4ab0-4745-8292-5849b34173a7', + 'inbredset-group', + 'A resource that controls access to a particular InbredSet group', + '{"default-access-level":"public-read"}' + ) + """, + """ + DELETE FROM resource_categories WHERE + resource_category_id = 'b3654600-4ab0-4745-8292-5849b34173a7' + """ + ), + step( + """ + CREATE TABLE IF NOT EXISTS linked_inbredset_groups + -- Link InbredSet groups in MariaDB to auth system + ( + data_link_id TEXT NOT NULL PRIMARY KEY, -- A new ID for the auth system + SpeciesId TEXT NOT NULL, -- Species ID in MariaDB + InbredSetId TEXT NOT NULL, -- The InbredSet ID in MariaDB + InbredSetName TEXT NOT NULL, -- The InbredSet group's name in MariaDB + InbredSetFullName TEXT NOT NULL, -- The InbredSet group's full name in MariaDB + UNIQUE(SpeciesId, InbredSetId) + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS linked_inbredset_groups"), + step( + """ + CREATE TABLE IF NOT EXISTS inbredset_group_resources + -- Link the InbredSet data to a specific resource + ( + resource_id TEXT NOT NULL, -- Linked resource: one-to-one + data_link_id TEXT NOT NULL, + PRIMARY KEY(resource_id, data_link_id), + UNIQUE(resource_id), -- resource is linked to only one InbredSet + UNIQUE(data_link_id), -- InbredSet is linked to only one resource + FOREIGN KEY(resource_id) + REFERENCES resources(resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(data_link_id) + REFERENCES linked_inbredset_groups(data_link_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS inbredset_group_resources"), + step( + """ + INSERT INTO privileges(privilege_id, privilege_description) VALUES + ('system:inbredset:create-case-attribute', 'Create a new case attribute for an InbredSet group.'), + ('system:inbredset:delete-case-attribute', 'Delete an existing case-attribute from an InbredSet group'), + ('system:inbredset:edit-case-attribute', 'Edit the values of case-attributes of an InbredSet group'), + ('system:inbredset:view-case-attribute', 'View the case-attributes of an InbredSet group'), + ('system:inbredset:apply-case-attribute-edit', 'Apply an edit to case-attributes performed by another user for an InbredSet group'), + ('system:inbredset:reject-case-attribute-edit', 'Reject an edit to case-attributes performed by another user for an InbredSet group') + """, + """ + DELETE FROM privileges WHERE privilege_id IN ( + 'system:inbredset:create-case-attribute', + 'system:inbredset:delete-case-attribute', + 'system:inbredset:edit-case-attribute', + 'system:inbredset:view-case-attribute', + 'system:inbredset:apply-case-attribute-edit', + 'system:inbredset:reject-case-attribute-edit') + """) +] diff --git a/gn_auth/migrations/auth/20231011_01_CS8NZ-create-new-inbredset-group-owner-role.py b/gn_auth/migrations/auth/20231011_01_CS8NZ-create-new-inbredset-group-owner-role.py new file mode 100644 index 0000000..a4238ed --- /dev/null +++ b/gn_auth/migrations/auth/20231011_01_CS8NZ-create-new-inbredset-group-owner-role.py @@ -0,0 +1,40 @@ +""" +Create new 'inbredset-group-owner' role +""" + +from yoyo import step + +__depends__ = {'20231002_01_tzxTf-link-inbredsets-to-auth-system'} + +steps = [ + step( + """ + INSERT INTO roles(role_id, role_name, user_editable) + VALUES('bde1c08b-b067-4d56-8353-462fc5928c32', 'inbredset-group-owner', 0) + """, + """ + DELETE FROM roles WHERE role_id='bde1c08b-b067-4d56-8353-462fc5928c32' + """), + step( + """ + INSERT INTO role_privileges(role_id, privilege_id) + VALUES + ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:apply-case-attribute-edit'), + ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:create-case-attribute'), + ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:delete-case-attribute'), + ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:edit-case-attribute'), + ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:reject-case-attribute-edit'), + ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:view-case-attribute') + """, + """ + DELETE FROM role_privileges + WHERE (role_id, privilege_id) + IN + (('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:apply-case-attribute-edit'), + ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:create-case-attribute'), + ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:delete-case-attribute'), + ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:edit-case-attribute'), + ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:reject-case-attribute-edit'), + ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:view-case-attribute')) + """) +] diff --git a/gn_auth/migrations/auth/20240506_01_798tW-create-jwt-refresh-tokens-table.py b/gn_auth/migrations/auth/20240506_01_798tW-create-jwt-refresh-tokens-table.py new file mode 100644 index 0000000..049ac6b --- /dev/null +++ b/gn_auth/migrations/auth/20240506_01_798tW-create-jwt-refresh-tokens-table.py @@ -0,0 +1,34 @@ +""" +Create jwt_refresh_tokens table +""" + +from yoyo import step + +__depends__ = {'20231011_01_CS8NZ-create-new-inbredset-group-owner-role'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS jwt_refresh_tokens + -- Store refresh tokens to verify refresh attempts + ( + token TEXT NOT NULL, + client_id TEXT NOT NULL, + user_id TEXT NOT NULL, + issued_with TEXT NOT NULL UNIQUE, -- JWT ID of JWT issued along with this refresh token + issued_at INTEGER NOT NULL, + expires INTEGER NOT NULL, + scope TEXT NOT NULL, + revoked INTEGER CHECK (revoked = 0 or revoked = 1), + parent_of TEXT UNIQUE, + PRIMARY KEY(token), + FOREIGN KEY (client_id) REFERENCES oauth2_clients(client_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (user_id) REFERENCES users(user_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY (parent_of) REFERENCES jwt_refresh_tokens(token) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS jwt_refresh_tokens") +] diff --git a/gn_auth/migrations/auth/20240529_01_ALNWj-update-schema-for-user-verification.py b/gn_auth/migrations/auth/20240529_01_ALNWj-update-schema-for-user-verification.py new file mode 100644 index 0000000..0cab1c3 --- /dev/null +++ b/gn_auth/migrations/auth/20240529_01_ALNWj-update-schema-for-user-verification.py @@ -0,0 +1,64 @@ +""" +update schema for user-verification +""" + +from yoyo import step + +__depends__ = {'20240506_01_798tW-create-jwt-refresh-tokens-table'} + +def add_verification_cols_to_users_table(conn): + "add verification columns to users table"; + conn.execute("PRAGMA foreign_keys = OFF") + + conn.execute( + """ + CREATE TABLE users_new( + user_id TEXT PRIMARY KEY NOT NULL, + email TEXT UNIQUE NOT NULL, + name TEXT, + created INTEGER NOT NULL DEFAULT (unixepoch()), + verified INTEGER NOT NULL DEFAULT 0 CHECK (verified=0 or verified=1) + ) WITHOUT ROWID + """) + conn.execute( + """ + INSERT INTO users_new(user_id, email, name) + SELECT user_id, email, name FROM users + """) + # the original table `users` has dependents, so we cannot simply do a + # `ALTER TABLE … RENAME TO …` since according to + # https://sqlite.org/lang_altertable.html#alter_table_rename + # from versions 3.26.0 onward, the foreign key references are **ALWAYS** + # changed. In this case, we create the new table first, do data transfers, + # drop the original and rename the new table to the same name as the + # original. + conn.execute("DROP TABLE IF EXISTS users") + conn.execute("ALTER TABLE users_new RENAME TO users") + + + print("turning foreign keys should back on.") + conn.execute("PRAGMA foreign_key_check") + conn.execute("PRAGMA foreign_keys = ON") + +def drop_verification_cols_from_users_table(conn): + "Drop verification columns from users table" + conn.execute("ALTER TABLE users DROP COLUMN created") + conn.execute("ALTER TABLE users DROP COLUMN verified") + +steps = [ + step(add_verification_cols_to_users_table, + drop_verification_cols_from_users_table), + step( + """ + CREATE TABLE IF NOT EXISTS user_verification_codes( + user_id TEXT NOT NULL, + code TEXT NOT NULL, + generated INTEGER NOT NULL, + expires INTEGER NOT NULL, + PRIMARY KEY(user_id), + FOREIGN KEY(user_id) REFERENCES users(user_id) + ON UPDATE CASCADE ON DELETE CASCADE + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS verification_codes") +] diff --git a/gn_auth/migrations/auth/20240606_01_xQDwL-move-role-manipulation-privileges-from-group-to-resources.py b/gn_auth/migrations/auth/20240606_01_xQDwL-move-role-manipulation-privileges-from-group-to-resources.py new file mode 100644 index 0000000..a45fd30 --- /dev/null +++ b/gn_auth/migrations/auth/20240606_01_xQDwL-move-role-manipulation-privileges-from-group-to-resources.py @@ -0,0 +1,94 @@ +""" +Move role-manipulation privileges from group to resources +""" +import sqlite3 +from yoyo import step + +__depends__ = {'20240529_01_ALNWj-update-schema-for-user-verification'} + +def role_by_name(cursor, role_name): + """Fetch group-admin role""" + cursor.execute("SELECT * FROM roles WHERE role_name=?", + (role_name,)) + return dict(cursor.fetchone()) + + +def move_privileges_to_resources(conn): + """Move role-manipulation privileges from group to resource.""" + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + cursor.execute( + "DELETE FROM role_privileges WHERE privilege_id IN (" + " 'group:role:create-role'," + " 'group:role:delete-role'," + " 'group:role:edit-role'," + " 'group:user:assign-role'" + ")") + cursor.execute( + "DELETE FROM privileges WHERE privilege_id IN (" + " 'group:role:create-role'," + " 'group:role:delete-role'," + " 'group:role:edit-role'," + " 'group:user:assign-role'" + ")") + + resource_owner_role = role_by_name(cursor, "resource-owner") + privileges = ( + ("resource:role:create-role", + "Create a new role on a specific resource"), + ("resource:role:delete-role", + "Delete an existing role from a specific resource"), + ("resource:role:edit-role", + "Edit an existing role on a specific resource"), + ("resource:user:assign-role", + "Assign a user to a role on a specific resource")) + cursor.executemany( + ("INSERT INTO privileges(privilege_id, privilege_description) " + "VALUES (?, ?)"), + privileges) + cursor.executemany( + ("INSERT INTO role_privileges(role_id, privilege_id) " + "VALUES(?, ?)"), + tuple((resource_owner_role["role_id"], privilege[0]) + for privilege in privileges)) + cursor.close() + +def move_privileges_to_groups(conn): + """Move role-manipulation privileges from resource to group.""" + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + cursor.execute( + "DELETE FROM role_privileges WHERE privilege_id IN (" + " 'resource:role:create-role'," + " 'resource:role:delete-role'," + " 'resource:role:edit-role'," + " 'resource:user:assign-role'" + ")") + cursor.execute( + "DELETE FROM privileges WHERE privilege_id IN (" + " 'resource:role:create-role'," + " 'resource:role:delete-role'," + " 'resource:role:edit-role'," + " 'resource:user:assign-role'" + ")") + + group_leader_role = role_by_name(cursor, "group-leader") + privileges = ( + ("group:role:create-role", "Create a new role"), + ("group:role:delete-role", "Delete an existing role"), + ("group:role:edit-role", "edit/update an existing role"), + ("group:user:assign-role", "Assign a role to an existing user")) + cursor.executemany( + ("INSERT INTO privileges(privilege_id, privilege_description) " + "VALUES (?, ?)"), + privileges) + cursor.executemany( + ("INSERT INTO role_privileges(role_id, privilege_id) " + "VALUES(?, ?)"), + tuple((group_leader_role["role_id"], privilege[0]) + for privilege in privileges)) + cursor.close() + +steps = [ + step(move_privileges_to_resources, move_privileges_to_groups) +] diff --git a/gn_auth/migrations/auth/20240606_02_ubZri-create-resource-roles-table.py b/gn_auth/migrations/auth/20240606_02_ubZri-create-resource-roles-table.py new file mode 100644 index 0000000..0695c0e --- /dev/null +++ b/gn_auth/migrations/auth/20240606_02_ubZri-create-resource-roles-table.py @@ -0,0 +1,36 @@ +""" +Create 'resource_roles' table. +""" + +from yoyo import step + +__depends__ = {'20240606_01_xQDwL-move-role-manipulation-privileges-from-group-to-resources'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS resource_roles( + resource_id TEXT NOT NULL, + role_created_by TEXT NOT NULL, + role_id TEXT NOT NULL, + PRIMARY KEY (resource_id, role_created_by, role_id), + FOREIGN KEY(resource_id) REFERENCES resources(resource_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(role_created_by) 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 + """, + "DROP TABLE IF EXISTS resource_roles"), + step( + """ + CREATE INDEX IF NOT EXISTS + tbl_resource_roles_cols_resource_id_role_created_by + ON resource_roles(resource_id, role_created_by) + """, + """ + DROP INDEX IF EXISTS + tbl_resource_roles_cols_resource_id_role_created_by + """) +] diff --git a/gn_auth/migrations/auth/20240606_03_BY7Us-drop-group-roles-table.py b/gn_auth/migrations/auth/20240606_03_BY7Us-drop-group-roles-table.py new file mode 100644 index 0000000..45d689c --- /dev/null +++ b/gn_auth/migrations/auth/20240606_03_BY7Us-drop-group-roles-table.py @@ -0,0 +1,35 @@ +""" +Drop 'group_roles' table. +""" +import sqlite3 +from yoyo import step + +__depends__ = {'20240606_02_ubZri-create-resource-roles-table'} + +def restore_group_roles(conn): + """Restore the `group_roles` table.""" + conn.row_factory = sqlite3.Row + cursor = conn.cursor() + cursor.execute( + """ + CREATE TABLE group_roles( + group_role_id TEXT PRIMARY KEY, + group_id TEXT NOT NULL, + role_id TEXT NOT NULL, + UNIQUE (group_id, role_id), + FOREIGN KEY(group_id) REFERENCES groups(group_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(role_id) REFERENCES roles(role_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + cursor.execute( + """ + CREATE INDEX idx_tbl_group_roles_cols_group_id + ON group_roles(group_id) + """) + cursor.close() + +steps = [ + step("DROP TABLE IF EXISTS group_roles", restore_group_roles) +] diff --git a/gn_auth/migrations/auth/20240819_01_p2vXR-create-forgot-password-tokens-table.py b/gn_auth/migrations/auth/20240819_01_p2vXR-create-forgot-password-tokens-table.py new file mode 100644 index 0000000..44318bd --- /dev/null +++ b/gn_auth/migrations/auth/20240819_01_p2vXR-create-forgot-password-tokens-table.py @@ -0,0 +1,26 @@ +""" +Create forgot_password_tokens table + +This will be used to enable users to validate/verify their password change +requests. +""" + +from yoyo import step + +__depends__ = {'20240606_03_BY7Us-drop-group-roles-table'} + +steps = [ + step( + """ + CREATE TABLE IF NOT EXISTS forgot_password_tokens( + user_id TEXT NOT NULL, + token TEXT NOT NULL, + generated INTEGER NOT NULL, + expires INTEGER NOT NULL, + PRIMARY KEY(user_id), + FOREIGN KEY(user_id) REFERENCES users(user_id) + ON UPDATE CASCADE ON DELETE CASCADE + ) WITHOUT ROWID + """, + "DROP TABLE IF EXISTS forgot_password_tokens") +] diff --git a/gn_auth/migrations/auth/20240924_01_thbvh-hooks-for-edu-domains.py b/gn_auth/migrations/auth/20240924_01_thbvh-hooks-for-edu-domains.py new file mode 100644 index 0000000..5c6e81d --- /dev/null +++ b/gn_auth/migrations/auth/20240924_01_thbvh-hooks-for-edu-domains.py @@ -0,0 +1,24 @@ +""" +hooks_for_edu_domains +""" + +from yoyo import step + +__depends__ = {'20240819_01_p2vXR-create-forgot-password-tokens-table'} + +steps = [ + step( + """ + INSERT INTO roles(role_id, role_name, user_editable) VALUES + ('9bb203a2-7897-4fe3-ac4a-75e6a4f96f5d', 'hook-role-from-edu-domain', '0') + """, + "DELETE FROM roles WHERE role_name='hook-role-from-edu-domain'"), + step( + """ + INSERT INTO role_privileges(role_id, privilege_id) VALUES + ('9bb203a2-7897-4fe3-ac4a-75e6a4f96f5d', 'group:resource:view-resource'), + ('9bb203a2-7897-4fe3-ac4a-75e6a4f96f5d', 'group:resource:edit-resource') + """, + "DELETE FROM role_privileges WHERE role_id='9bb203a2-7897-4fe3-ac4a-75e6a4f96f5d'" + ) +] diff --git a/gn_auth/migrations/auth/20250328_01_72EFk-add-admin-ui-privilege-to-system-administrator-role.py b/gn_auth/migrations/auth/20250328_01_72EFk-add-admin-ui-privilege-to-system-administrator-role.py new file mode 100644 index 0000000..d22ad01 --- /dev/null +++ b/gn_auth/migrations/auth/20250328_01_72EFk-add-admin-ui-privilege-to-system-administrator-role.py @@ -0,0 +1,42 @@ +""" +add admin ui privilege to system-administrator role +""" +import contextlib + +from yoyo import step + +__depends__ = {'20240924_01_thbvh-hooks-for-edu-domains'} + +def get_system_admin_id(cursor): + cursor.execute( + "SELECT role_id FROM roles WHERE role_name='system-administrator'") + return cursor.fetchone()[0] + +def add_admin_ui_privilege(conn): + with contextlib.closing(conn.cursor()) as cursor: + # Create admin-ui privilege + cursor.execute( + "INSERT INTO privileges (privilege_id, privilege_description) " + "VALUES(?, ?)", + ("system:user:admin-ui", "View UI elements that should only be visible to system administrators")) + + # Add UI privilege to system-administrator role + cursor.execute( + "INSERT INTO role_privileges (role_id, privilege_id) " + "VALUES(?, ?)", + (get_system_admin_id(cursor), "system:user:admin-ui") + ) + +def remove_admin_ui_privilege(conn): + with contextlib.closing(conn.cursor()) as cursor: + # Remove UI privilege from system-administrator role + cursor.execute( + "DELETE FROM role_privileges WHERE privilege_id='system:user:admin-ui'") + + # Remove UI privilege from privileges table + cursor.execute( + "DELETE FROM privileges WHERE privilege_id='system:user:admin-ui'") + +steps = [ + step(add_admin_ui_privilege, remove_admin_ui_privilege) +] diff --git a/gn_auth/migrations/auth/20250609_01_LB60X-add-batch-edit-privileges.py b/gn_auth/migrations/auth/20250609_01_LB60X-add-batch-edit-privileges.py new file mode 100644 index 0000000..73a4880 --- /dev/null +++ b/gn_auth/migrations/auth/20250609_01_LB60X-add-batch-edit-privileges.py @@ -0,0 +1,49 @@ +""" +Add Batch Edit privileges +""" + +import contextlib + +from yoyo import step + +__depends__ = {'20250328_01_72EFk-add-admin-ui-privilege-to-system-administrator-role'} + +def add_batch_edit_privilege_and_role(conn): + with contextlib.closing(conn.cursor()) as cursor: + # Create batch edit privilege + cursor.execute( + "INSERT INTO privileges (privilege_id, privilege_description) " + "VALUES(?, ?)", + ("system:data:batch-edit", "Batch Edit")) + + # Create batch editor role + cursor.execute( + "INSERT INTO roles (role_id, role_name, user_editable) " + "VALUES(?, ?, ?)", + ("0f391910-5225-476a-bb8d-9c0adc9d81cc", "Batch Editors", 0)) + + # Link role/privilege + cursor.execute( + "INSERT INTO role_privileges (role_id, privilege_id) " + "VALUES(?, ?)", + ("0f391910-5225-476a-bb8d-9c0adc9d81cc", "system:data:batch-edit") + ) + +def remove_batch_edit_privilege_and_role(conn): + with contextlib.closing(conn.cursor()) as cursor: + # Remove batch edit role/privilege link + cursor.execute( + "DELETE FROM role_privileges WHERE privilege_id='system:data:batch-edit'") + + # Remove Batch Editor role + cursor.execute( + "DELETE FROM roles WHERE role_id='0f391910-5225-476a-bb8d-9c0adc9d81cc'") + + # Remove Batch Edit privilege + cursor.execute( + "DELETE FROM privileges WHERE privilege_id='system:data:batch-edit'") + + +steps = [ + step(add_batch_edit_privilege_and_role, remove_batch_edit_privilege_and_role) +] diff --git a/gn_auth/migrations/auth/20250609_01_bj9Pl-add-new-group-data-link-to-group-privilege.py b/gn_auth/migrations/auth/20250609_01_bj9Pl-add-new-group-data-link-to-group-privilege.py new file mode 100644 index 0000000..3b9e928 --- /dev/null +++ b/gn_auth/migrations/auth/20250609_01_bj9Pl-add-new-group-data-link-to-group-privilege.py @@ -0,0 +1,19 @@ +""" +Add new 'group:data:link-to-group' privilege. +""" + +from yoyo import step + +__depends__ = {'20240924_01_thbvh-hooks-for-edu-domains'} + +steps = [ + step( + """ + INSERT INTO privileges(privilege_id, privilege_description) + VALUES( + 'group:data:link-to-group', + 'Allow linking data to only one specific group.' + ) + """, + "DELETE FROM privileges WHERE privilege_id='group:data:link-to-group'") +] diff --git a/gn_auth/migrations/auth/20250609_02_9UBPl-assign-group-data-link-to-group-privilege-to-group-leader.py b/gn_auth/migrations/auth/20250609_02_9UBPl-assign-group-data-link-to-group-privilege-to-group-leader.py new file mode 100644 index 0000000..5d9c306 --- /dev/null +++ b/gn_auth/migrations/auth/20250609_02_9UBPl-assign-group-data-link-to-group-privilege-to-group-leader.py @@ -0,0 +1,23 @@ +""" +Assign 'group:data:link-to-group' privilege to group leader. +""" + +from yoyo import step + +__depends__ = {'20250609_01_bj9Pl-add-new-group-data-link-to-group-privilege'} + +steps = [ + step( + """ + INSERT INTO role_privileges(role_id, privilege_id) + VALUES( + 'a0e67630-d502-4b9f-b23f-6805d0f30e30', + 'group:data:link-to-group' + ) + """, + """ + DELETE FROM role_privileges + WHERE role_id='a0e67630-d502-4b9f-b23f-6805d0f30e30' + AND privilege_id='group:data:link-to-group' + """) +] diff --git a/gn_auth/migrations/auth/20250703_01_aDVwP-add-role-management-privileges-to-group-leader-role.py b/gn_auth/migrations/auth/20250703_01_aDVwP-add-role-management-privileges-to-group-leader-role.py new file mode 100644 index 0000000..6335152 --- /dev/null +++ b/gn_auth/migrations/auth/20250703_01_aDVwP-add-role-management-privileges-to-group-leader-role.py @@ -0,0 +1,27 @@ +""" +Add role management privileges to group-leader role +""" + +from yoyo import step + +__depends__ = {'20250609_01_LB60X-add-batch-edit-privileges', '20250609_02_9UBPl-assign-group-data-link-to-group-privilege-to-group-leader'} + +steps = [ + step( + """ + INSERT INTO role_privileges(role_id, privilege_id) + VALUES + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', 'resource:role:create-role'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', 'resource:role:delete-role'), + ('a0e67630-d502-4b9f-b23f-6805d0f30e30', 'resource:role:edit-role') + """, + """ + DELETE FROM role_privileges + WHERE role_id='a0e67630-d502-4b9f-b23f-6805d0f30e30' + AND privilege_id IN ( + 'resource:role:create-role', + 'resource:role:delete-role', + 'resource:role:edit-role' + ) + """) +] diff --git a/gn_auth/migrations/auth/20250722_01_7Gro7-create-new-system-user-edit-privilege.py b/gn_auth/migrations/auth/20250722_01_7Gro7-create-new-system-user-edit-privilege.py new file mode 100644 index 0000000..f00ab11 --- /dev/null +++ b/gn_auth/migrations/auth/20250722_01_7Gro7-create-new-system-user-edit-privilege.py @@ -0,0 +1,18 @@ +""" +Create new 'system:user:edit' privilege. +""" + +from yoyo import step + +__depends__ = {'20250703_01_aDVwP-add-role-management-privileges-to-group-leader-role'} + +steps = [ + step( + """ + INSERT INTO privileges(privilege_id, privilege_description) + VALUES( + 'system:user:edit', + 'Allow general user-information edit.') + """, + "DELETE FROM privileges WHERE privilege_id='system:user:edit'") +] diff --git a/gn_auth/migrations/auth/20250722_02_M8TXv-add-system-user-edit-privilege-to-system-admin-role.py b/gn_auth/migrations/auth/20250722_02_M8TXv-add-system-user-edit-privilege-to-system-admin-role.py new file mode 100644 index 0000000..b956bef --- /dev/null +++ b/gn_auth/migrations/auth/20250722_02_M8TXv-add-system-user-edit-privilege-to-system-admin-role.py @@ -0,0 +1,36 @@ +""" +Add 'system:user:edit' privilege to 'system-admin' role. +""" +import contextlib + +from yoyo import step + +__depends__ = {'20250722_01_7Gro7-create-new-system-user-edit-privilege'} + + +def system_administrator_role_id(cursor): + """Fetch ID for role 'system-administrator'.""" + cursor.execute( + "SELECT role_id FROM roles WHERE role_name='system-administrator'") + return cursor.fetchone()[0] + + +def add_system_user_edit_privilege(conn): + """Add the 'system:user:edit' to the 'system-administrator' role.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "INSERT INTO role_privileges(role_id, privilege_id) " + "VALUES(?, ?)", + (system_administrator_role_id(cursor), 'system:user:edit')) + + +def remove_system_user_edit_privilege(conn): + """Remove the 'system:user:edit' from the 'system-administrator' role.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "DELETE FROM role_privileges WHERE role_id=? AND privilege_id=?", + (system_administrator_role_id(cursor), 'system:user:edit')) + +steps = [ + step(add_system_user_edit_privilege, remove_system_user_edit_privilege) +] diff --git a/gn_auth/migrations/auth/20250729_01_CNn2p-create-initial-system-wide-resources-access-privileges.py b/gn_auth/migrations/auth/20250729_01_CNn2p-create-initial-system-wide-resources-access-privileges.py new file mode 100644 index 0000000..be0d022 --- /dev/null +++ b/gn_auth/migrations/auth/20250729_01_CNn2p-create-initial-system-wide-resources-access-privileges.py @@ -0,0 +1,31 @@ +""" +Create initial system-wide resources access privileges +""" + +from yoyo import step + +__depends__ = {'20250722_02_M8TXv-add-system-user-edit-privilege-to-system-admin-role'} + +steps = [ + step( + """ + INSERT INTO privileges(privilege_id, privilege_description) + VALUES + ("system:resource:view", + "View the wrapper resource object (not attached data). This is mostly for administration purposes."), + ("system:resource:edit", + "Edit/update the wrapper resource object (not attached data). This is mostly for administration purposes."), + ("system:resource:delete", + "Delete the wrapper resource object (not attached data). This is mostly for administration purposes."), + ("system:resource:reassign-group", + "Reassign the resource, and its data, to a different user group."), + ("system:resource:assign-owner", + "Assign ownership of any resource to any user.") + """, + """ + DELETE FROM privileges WHERE privilege_id IN + ("system:resource:view", "system:resource:edit", + "system:resource:delete", "system:resource:reassign-group", + "system:resource:assign-owner") + """) +] diff --git a/gn_auth/migrations/auth/20250729_02_7ycSm-assign-initial-system-wide-resources-access-privileges-to-sys-admins.py b/gn_auth/migrations/auth/20250729_02_7ycSm-assign-initial-system-wide-resources-access-privileges-to-sys-admins.py new file mode 100644 index 0000000..e79ab1c --- /dev/null +++ b/gn_auth/migrations/auth/20250729_02_7ycSm-assign-initial-system-wide-resources-access-privileges-to-sys-admins.py @@ -0,0 +1,53 @@ +""" +Assign initial system-wide resources-access privileges to sys-admins. +""" +import contextlib + +from yoyo import step + +def system_administrator_role_id(cursor): + """Fetch ID for role 'system-administrator'.""" + cursor.execute( + "SELECT role_id FROM roles WHERE role_name='system-administrator'") + return cursor.fetchone()[0] + + +def assign_system_wide_resource_access_to_sysadmin(conn): + """ + Assign initial system-wide resources-access privileges to + `system-administrator` role. + """ + with contextlib.closing(conn.cursor()) as cursor: + sysadmin_role_id = system_administrator_role_id(cursor) + cursor.executemany( + "INSERT INTO role_privileges(role_id, privilege_id) " + "VALUES(?, ?)", + ((sysadmin_role_id, "system:resource:view"), + (sysadmin_role_id, "system:resource:edit"), + (sysadmin_role_id, "system:resource:delete"), + (sysadmin_role_id, "system:resource:reassign-group"), + (sysadmin_role_id, "system:resource:assign-owner"))) + + +def revoke_system_wide_resource_access_from_sysadmin(conn): + """ + Revoke initial system-wide resources-access privileges from + `system-administrator` role. + """ + with contextlib.closing(conn.cursor()) as cursor: + sysadmin_role_id = system_administrator_role_id(cursor) + cursor.executemany( + "DELETE FROM role_privileges " + "WHERE role_id=? AND privilege_id=?", + ((sysadmin_role_id, "system:resource:view"), + (sysadmin_role_id, "system:resource:edit"), + (sysadmin_role_id, "system:resource:delete"), + (sysadmin_role_id, "system:resource:reassign-group"), + (sysadmin_role_id, "system:resource:assign-owner"))) + +__depends__ = {'20250729_01_CNn2p-create-initial-system-wide-resources-access-privileges'} + +steps = [ + step(assign_system_wide_resource_access_to_sysadmin, + revoke_system_wide_resource_access_from_sysadmin) +] diff --git a/gn_auth/migrations/auth/20250729_03_oCvvq-grant-role-to-all-resources-to-sys-admin-users.py b/gn_auth/migrations/auth/20250729_03_oCvvq-grant-role-to-all-resources-to-sys-admin-users.py new file mode 100644 index 0000000..e3bdc8f --- /dev/null +++ b/gn_auth/migrations/auth/20250729_03_oCvvq-grant-role-to-all-resources-to-sys-admin-users.py @@ -0,0 +1,75 @@ +""" +Grant role to ALL resources to sys-admin users. +""" +import itertools +import contextlib + +from yoyo import step + +__depends__ = {'20250729_02_7ycSm-assign-initial-system-wide-resources-access-privileges-to-sys-admins'} + + +def system_administrator_role_id(cursor): + """Fetch ID for role 'system-administrator'.""" + cursor.execute( + "SELECT role_id FROM roles WHERE role_name='system-administrator'") + return cursor.fetchone()[0] + + +def system_resource_id(cursor): + cursor.execute( + "SELECT resources.resource_id FROM resource_categories " + "INNER JOIN resources ON resource_categories.resource_category_id=resources.resource_category_id " + "WHERE resource_category_key = 'system'") + return cursor.fetchone()[0] + + +def fetch_ids_for_sysadmin_users(cursor): + """Fetch all sysadmin users' IDs.""" + cursor.execute( + "SELECT user_roles.user_id FROM roles INNER JOIN user_roles " + "ON roles.role_id=user_roles.role_id " + "WHERE role_name='system-administrator' AND resource_id=?", + (system_resource_id(cursor),)) + return tuple(row[0] for row in cursor.fetchall()) + + +def fetch_non_system_resources(cursor): + """Fetch IDs for all resources that are not of the 'system' category.""" + cursor.execute( + "SELECT resources.resource_id FROM resource_categories " + "INNER JOIN resources " + "ON resource_categories.resource_category_id=resources.resource_category_id " + "WHERE resource_category_key != 'system'") + return tuple(row[0] for row in cursor.fetchall()) + + +def assign_sysadmin_role_on_non_system_resources(conn): + """Assign sysadmins the sysadmin role on all non-system resources.""" + with contextlib.closing(conn.cursor()) as cursor: + sysadminroleid = system_administrator_role_id(cursor) + cursor.executemany( + "INSERT INTO user_roles(user_id, resource_id, role_id) " + "VALUES (?, ?, ?)", + tuple(item + (sysadminroleid,) + for item in itertools.product( + fetch_ids_for_sysadmin_users(cursor), + fetch_non_system_resources(cursor)))) + + +def revoke_sysadmin_role_on_non_system_resources(conn): + """Revoke sysadmins the sysadmin role on all non-system resources.""" + with contextlib.closing(conn.cursor()) as cursor: + sysadminroleid = system_administrator_role_id(cursor) + cursor.executemany( + "DELETE FROM user_roles " + "WHERE user_id=? AND resource_id=? AND role_id=?", + tuple(item + (sysadminroleid,) + for item in itertools.product( + fetch_ids_for_sysadmin_users(cursor), + fetch_non_system_resources(cursor)))) + +steps = [ + step(assign_sysadmin_role_on_non_system_resources, + revoke_sysadmin_role_on_non_system_resources) +] diff --git a/gn_auth/migrations/auth/20250731_01_Ke1us-add-sysadmin-privileges-for-acting-on-groups-members.py b/gn_auth/migrations/auth/20250731_01_Ke1us-add-sysadmin-privileges-for-acting-on-groups-members.py new file mode 100644 index 0000000..95a6fbb --- /dev/null +++ b/gn_auth/migrations/auth/20250731_01_Ke1us-add-sysadmin-privileges-for-acting-on-groups-members.py @@ -0,0 +1,70 @@ +""" +Add sysadmin privileges for acting on groups: mostly handling user management. +""" +import itertools +import contextlib + +from yoyo import step + +__depends__ = {'20250729_03_oCvvq-grant-role-to-all-resources-to-sys-admin-users'} + + +def system_administrator_role_id(cursor): + """Fetch ID for role 'system-administrator'.""" + cursor.execute( + "SELECT role_id FROM roles WHERE role_name='system-administrator'") + return cursor.fetchone()[0] + + +def add_group_privileges_to_sysadmin_role(conn): + """Add group-management privileges to sysadmin role.""" + with contextlib.closing(conn.cursor()) as cursor: + sysadminroleid = system_administrator_role_id(cursor) + cursor.executemany( + "INSERT INTO role_privileges(role_id, privilege_id) VALUES (?, ?)", + tuple(itertools.product( + (sysadminroleid,), + ('system:group:add-group-member', + 'system:group:remove-group-member', + 'system:group:assign-group-leader', + 'system:group:revoke-group-leader')))) + + +def remove_group_privileges_to_sysadmin_role(conn): + """Remove group-management privileges from sysadmin role.""" + with contextlib.closing(conn.cursor()) as cursor: + sysadminroleid = system_administrator_role_id(cursor) + cursor.executemany( + "DELETE FROM role_privileges WHERE role_id=? AND privilege_id=?", + tuple(itertools.product( + (sysadminroleid,), + ('system:group:add-group-member', + 'system:group:remove-group-member', + 'system:group:assign-group-leader', + 'system:group:revoke-group-leader')))) + + +steps = [ + step( + """ + INSERT INTO privileges(privilege_id, privilege_description) + VALUES + ('system:group:add-group-member', + 'Make an existing user a member of a group.'), + ('system:group:remove-group-member', + 'Remove a member user from a group.'), + ('system:group:assign-group-leader', + 'Assign an existing group member the group-leader role'), + ('system:group:revoke-group-leader', + 'Revoke the group-leader role from a group member with the role.') + """, + """ + DELETE FROM privileges WHERE privilege_id IN + ('system:group:add-group-member', + 'system:group:remove-group-member', + 'system:group:assign-group-leader', + 'system:group:revoke-group-leader') + """), + step(add_group_privileges_to_sysadmin_role, + remove_group_privileges_to_sysadmin_role) +] diff --git a/gn_auth/migrations/auth/20260206_01_v3f4P-add-role-systemwide-data-curator.py b/gn_auth/migrations/auth/20260206_01_v3f4P-add-role-systemwide-data-curator.py new file mode 100644 index 0000000..63e807a --- /dev/null +++ b/gn_auth/migrations/auth/20260206_01_v3f4P-add-role-systemwide-data-curator.py @@ -0,0 +1,61 @@ +""" +add role systemwide-data-curator. +""" +import uuid +import contextlib + +from yoyo import step + +__depends__ = {'20250731_01_Ke1us-add-sysadmin-privileges-for-acting-on-groups-members'} + + +def create_systemwide_data_curator_role(conn): + """Create a new 'systemwide-data-curator' role.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "INSERT INTO roles(role_id, role_name, user_editable) " + "VALUES (?, 'systemwide-data-curator', 0)", + (str(uuid.uuid4()),)) + + +def link_privileges_to_role(conn): + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute("SELECT role_id FROM roles " + "WHERE role_name='systemwide-data-curator'") + role_id = cursor.fetchone()[0] + cursor.executemany("INSERT INTO role_privileges(role_id, privilege_id) " + "VALUES (?, ?)", + tuple((role_id, priv) for priv in + ("system:system-wide:data:edit", + "system:system-wide:data:delete"))) + + +def unlink_privileges_from_role(conn): + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute("SELECT role_id FROM roles " + "WHERE role_name='systemwide-data-curator'") + role_id = cursor.fetchone()[0] + cursor.executemany("DELETE FROM role_privileges " + "WHERE role_id=? AND privilege_id=?", + tuple((role_id, priv) for priv in + ("system:system-wide:data:edit", + "system:system-wide:data:delete"))) + + +steps = [ + step(# Add new privileges + """ + INSERT INTO privileges (privilege_id, privilege_description) + VALUES + ('system:system-wide:data:edit', + 'A user with this privilege can edit any data on the entire system.'), + ('system:system-wide:data:delete', + 'A user with this privilege can delete any data from the system.') + """, + """ + DELETE FROM privileges WHERE privilege_id IN + ('system:system-wide:data:edit', 'system:system-wide:data:delete')"""), + step(create_systemwide_data_curator_role, + "DELETE FROM roles WHERE role_name='systemwide-data-curator'"), + step(link_privileges_to_role, unlink_privileges_from_role) +] diff --git a/gn_auth/migrations/auth/20260311_01_TfRlV-add-privilege-for-gn-docs-documentation-editing.py b/gn_auth/migrations/auth/20260311_01_TfRlV-add-privilege-for-gn-docs-documentation-editing.py new file mode 100644 index 0000000..d618f14 --- /dev/null +++ b/gn_auth/migrations/auth/20260311_01_TfRlV-add-privilege-for-gn-docs-documentation-editing.py @@ -0,0 +1,62 @@ +""" +add privilege for gn-docs documentation editing +""" +import uuid +import contextlib + +from yoyo import step + +__depends__ = {'20260206_01_v3f4P-add-role-systemwide-data-curator'} + +ROLE_NAME = 'systemwide-docs-editor' + + +def create_systemwide_docs_editor_role(conn): + """Create a new 'systemwide-data-curator' role.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "INSERT INTO roles(role_id, role_name, user_editable) " + "VALUES (?, ?, 0)", + (str(uuid.uuid4()), ROLE_NAME)) + + +def delete_systemwide_docs_editor_role(conn): + """Create a new 'systemwide-data-curator' role.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute("DELETE FROM roles WHERE role_name=?", (ROLE_NAME,)) + + +def assign_edit_priv_to_docs_editor(conn): + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute("SELECT role_id FROM roles WHERE role_name=?", + (ROLE_NAME,)) + role_id = cursor.fetchone()[0] + + cursor.execute( + "INSERT INTO role_privileges(role_id, privilege_id) " + "VALUES (?, ?)", + (role_id, "system:documentation:edit")) + + +def revoke_edit_priv_to_docs_editor(conn): + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute("SELECT role_id FROM roles WHERE role_name=?", + (ROLE_NAME,)) + role_id = cursor.fetchone()[0] + + cursor.execute( + "DELETE FROM role_privileges WHERE role_id=? AND privilege_id=?", + (role_id, "system:documentation:edit")) + + +steps = [ + step( + """INSERT INTO privileges(privilege_id, privilege_description) + VALUES( + 'system:documentation:edit', + 'Allows the holder to edit documentation presented with the Genenetwork system.' + )""", + "DELETE FROM privileges WHERE privilege_id='system:documentation:edit'"), + step(create_systemwide_docs_editor_role, delete_systemwide_docs_editor_role), + step(assign_edit_priv_to_docs_editor, revoke_edit_priv_to_docs_editor) +] diff --git a/gn_auth/migrations/auth/20260311_02_v3EFQ-assign-systemwide-docs-editor-role-to-sysadmins.py b/gn_auth/migrations/auth/20260311_02_v3EFQ-assign-systemwide-docs-editor-role-to-sysadmins.py new file mode 100644 index 0000000..e79ef6a --- /dev/null +++ b/gn_auth/migrations/auth/20260311_02_v3EFQ-assign-systemwide-docs-editor-role-to-sysadmins.py @@ -0,0 +1,66 @@ +""" +Assign 'systemwide-docs-editor' role to sysadmins +""" +import uuid +import contextlib + +from yoyo import step + +__depends__ = {'20260311_01_TfRlV-add-privilege-for-gn-docs-documentation-editing'} + + +def fetch_docs_editor_role_id(cursor): + """Fetch ID of systemwide-docs-editor role""" + cursor.execute( + "SELECT role_id FROM roles WHERE role_name='systemwide-docs-editor'") + return cursor.fetchone()[0] + + +def fetch_sys_resource_id(cursor): + """Fetch the resource ID of the system.""" + cursor.execute("SELECT resource_id FROM resources " + "WHERE resource_name='GeneNetwork System'") + return cursor.fetchone()[0] + + +def fetch_sys_admin_ids(cursor): + """Fetch the sysadmins' IDs.""" + cursor.execute( + "SELECT user_roles.user_id FROM resources INNER JOIN user_roles " + "ON resources.resource_id=user_roles.resource_id INNER JOIN roles " + "ON user_roles.role_id=roles.role_id " + "WHERE resources.resource_name='GeneNetwork System' " + "AND roles.role_name='system-administrator'") + return tuple(row[0] for row in cursor.fetchall()) + + +def __build_params__(cursor): + sysresourceid = fetch_sys_resource_id(cursor) + sysadminids = fetch_sys_admin_ids(cursor) + roleid = fetch_docs_editor_role_id(cursor) + return tuple({ + "user_id": userid, + "role_id": roleid, + "resource_id": sysresourceid + } for userid in sysadminids) + + +def assign_systemwide_docs_editor_role_to_sysadmins(conn): + with contextlib.closing(conn.cursor()) as cursor: + cursor.executemany( + "INSERT INTO user_roles(user_id, role_id, resource_id) " + "VALUES(:user_id, :role_id, :resource_id)", + __build_params__(cursor)) + + +def revoke_systemwide_docs_editor_role_from_sysadmins(conn): + with contextlib.closing(conn.cursor()) as cursor: + cursor.executemany( + "DELETE FROM user_roles WHERE user_id=:user_id " + "AND role_id=:role_id AND resource_id=:resource_id", + __build_params__(cursor)) + +steps = [ + step(assign_systemwide_docs_editor_role_to_sysadmins, + revoke_systemwide_docs_editor_role_from_sysadmins) +] diff --git a/gn_auth/migrations/auth/20260311_03_vxBCX-restrict-access-to-resources-make-public-feature.py b/gn_auth/migrations/auth/20260311_03_vxBCX-restrict-access-to-resources-make-public-feature.py new file mode 100644 index 0000000..bdf8a56 --- /dev/null +++ b/gn_auth/migrations/auth/20260311_03_vxBCX-restrict-access-to-resources-make-public-feature.py @@ -0,0 +1,49 @@ +""" +Restrict access to resources' 'Make Public' feature. +""" +import contextlib + +from yoyo import step + +__depends__ = {'20260311_02_v3EFQ-assign-systemwide-docs-editor-role-to-sysadmins'} + + +def fetch_systemwide_data_curator_role_id(cursor): + "Fetch the role's ID." + cursor.execute("SELECT role_id FROM roles " + "WHERE role_name='systemwide-data-curator'") + return cursor.fetchone()[0] + + +def assign_make_public_to_systemwide_data_curator(conn): + """Assign privilege to 'systemwide-data-curator' role.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "INSERT INTO role_privileges(role_id, privilege_id) " + "VALUES(?, 'system:resource:make-public')", + (fetch_systemwide_data_curator_role_id(cursor),)) + + +def revoke_make_public_from_systemwide_data_curator(conn): + """Revoke privilege from 'systemwide-data-curator' role.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "DELETE FROM role_privileges " + "WHERE role_id=? AND privilege_id='system:resource:make-public'", + (fetch_systemwide_data_curator_role_id(cursor),)) + + +steps = [ + step( + """ + INSERT INTO privileges(privilege_id, privilege_description) + VALUES( + 'system:resource:make-public', + 'Allow user to make a resource publicly accessible.') + """, + """ + DELETE FROM privileges WHERE privilege_id='system:resource:make-public' + """), + step(assign_make_public_to_systemwide_data_curator, + revoke_make_public_from_systemwide_data_curator), +] diff --git a/gn_auth/migrations/auth/20260331_01_FV1sL-add-privileges-to-role-systemwide-data-curator.py b/gn_auth/migrations/auth/20260331_01_FV1sL-add-privileges-to-role-systemwide-data-curator.py new file mode 100644 index 0000000..22863ae --- /dev/null +++ b/gn_auth/migrations/auth/20260331_01_FV1sL-add-privileges-to-role-systemwide-data-curator.py @@ -0,0 +1,69 @@ +""" +Add privileges to role systemwide-data-curator +""" +import contextlib + +from yoyo import step + +__depends__ = {'20260311_03_vxBCX-restrict-access-to-resources-make-public-feature'} + + +__new_privileges__ = ( + ("system:system-wide:inbredset:view-case-attribute", + "Enable view of any and all inbredset case attributes system-wide."), + ("system:system-wide:inbredset:edit-case-attribute", + "Enable edit of any and all inbredset case attributes system-wide."), + ("system:system-wide:inbredset:delete-case-attribute", + "Enable deletion of any and all inbredset case attributes system-wide."), + ("system:system-wide:inbredset:apply-case-attribute-edit", + "Enable applying changes to any and all inbredset case attributes system-wide."), + ("system:system-wide:inbredset:reject-case-attribute-edit", + "Enable rejecting changes to any and all inbredset case attributes system-wide.")) + + +def fetch_systemwide_data_curator_role_id(cursor): + "Fetch the role's ID." + cursor.execute("SELECT role_id FROM roles " + "WHERE role_name='systemwide-data-curator'") + return cursor.fetchone()[0] + + +def create_new_privileges(conn): + """Create new privileges for the system.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.executemany( + "INSERT INTO privileges(privilege_id, privilege_description) " + "VALUES (?, ?)", + __new_privileges__) + + +def delete_new_privileges(conn): + """Delete these new privileges from the system.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.executemany("DELETE FROM privileges WHERE privilege_id=?", + tuple((priv[0],) for priv in __new_privileges__)) + + +def assign_new_privileges(conn): + """Assign the new privileges to the `systemwide-data-curator` role.""" + with contextlib.closing(conn.cursor()) as cursor: + role_id = fetch_systemwide_data_curator_role_id(cursor) + cursor.executemany( + "INSERT INTO role_privileges(role_id, privilege_id) VALUES (?, ?)", + tuple((role_id, privilege[0]) for privilege in __new_privileges__)) + + +def revoke_new_privileges(conn): + """Revoke the new privileges from the `systemwide-data-curator` role.""" + with contextlib.closing(conn.cursor()) as cursor: + role_id = fetch_systemwide_data_curator_role_id(cursor) + cursor.executemany( + "DELETE FROM role_privileges WHERE role_id=? AND privilege_id=?", + tuple((role_id, privilege[0]) for privilege in __new_privileges__)) + + + +steps = [ + step(create_new_privileges, delete_new_privileges), + step(assign_new_privileges, revoke_new_privileges) +] diff --git a/gn_auth/migrations/auth/20260402_01_Bf8nm-add-user-and-time-tracking-to-resources-table.py b/gn_auth/migrations/auth/20260402_01_Bf8nm-add-user-and-time-tracking-to-resources-table.py new file mode 100644 index 0000000..702c418 --- /dev/null +++ b/gn_auth/migrations/auth/20260402_01_Bf8nm-add-user-and-time-tracking-to-resources-table.py @@ -0,0 +1,185 @@ +""" +Add user and time tracking to resources table +""" +import random +import contextlib +from datetime import datetime + +from yoyo import step + +__depends__ = {'20260331_01_FV1sL-add-privileges-to-role-systemwide-data-curator'} + +GN_AUTH_INIT_TIMESTAMP = 1691130509.0 +__admin_id__ = "" + + +def fetch_acentenos_id(conn): + """Fetch the default resource creator.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute("SELECT user_id FROM users WHERE email=?", + (("acent" "eno@" "uthsc" "." "edu"),)) + res = cursor.fetchone() + return res[0] if bool(res) else None + + +def fetch_a_sysadmin_id(conn, resources_table): + """Fetch one ID out of all system administrator users.""" + global __admin_id__ + + def __fetch__(): + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + f"SELECT ur.user_id FROM {resources_table} AS rsc " + "INNER JOIN user_roles AS ur ON rsc.resource_id=ur.resource_id " + "INNER JOIN roles AS r ON ur.role_id=r.role_id " + "WHERE resource_name='GeneNetwork System' " + "AND r.role_name='system-administrator'" + ) + return tuple(row[0] for row in cursor.fetchall()) + + if not bool(__admin_id__): + __admins__ = __fetch__() + if len(__admins__) > 0: + __admin_id__ = random.choice(__admins__) + + return __admin_id__ + + +def add_user_and_time_tracking_columns(conn): + """Add user and time tracking columns.""" + conn.execute( + """ + CREATE TABLE resources_new( + resource_id TEXT NOT NULL, + resource_name TEXT NOT NULL UNIQUE, + resource_category_id TEXT NOT NULL, + public INTEGER NOT NULL DEFAULT 0 CHECK (public=0 or public=1), + created_by TEXT NOT NULL, + created_at REAL NOT NULL DEFAULT '1691130509.0', + PRIMARY KEY(resource_id), + FOREIGN KEY(resource_category_id) + REFERENCES resource_categories(resource_category_id) + ON UPDATE CASCADE ON DELETE RESTRICT, + FOREIGN KEY(created_by) + REFERENCES users(user_id) ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + + +def drop_user_and_time_tracking_columns(conn): + """Drop user and time tracking columns.""" + conn.execute("PRAGMA foreign_keys = OFF") + conn.execute("DROP TABLE IF EXISTS resources") + conn.execute("ALTER TABLE resources_old RENAME TO resources") + conn.execute("PRAGMA foreign_key_check") + conn.execute("PRAGMA foreign_keys = ON") + + +def update_data_for_new_resources_table(conn): + """Add creator and time to original data.""" + __creator__ = ( + fetch_acentenos_id(conn) or fetch_a_sysadmin_id(conn, "resources")) + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute("SELECT * FROM resources") + cursor.executemany( + "INSERT INTO resources_new(" + " resource_id," + " resource_name," + " resource_category_id," + " public," + " created_by," + " created_at" + ") VALUES (?, ?, ?, ?, ?, ?)", + tuple( + tuple(row) + (__creator__, GN_AUTH_INIT_TIMESTAMP) + for row in cursor.fetchall())) + + +def restore_data_for_old_resources_table(conn): + """Remove creator and time from data.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute("SELECT * FROM resources") + cursor.executemany( + "INSERT INTO resources_old(" + " resource_id," + " resource_name," + " resource_category_id," + " public" + ") VALUES (?, ?, ?, ?)", + tuple(tuple(row)[0:4] for row in cursor.fetchall())) + + +def replace_old_table_with_new_table(conn): + """Restore old resources table with the new resources table.""" + conn.execute("PRAGMA foreign_keys = OFF") + conn.execute("DROP TABLE resources") + conn.execute("ALTER TABLE resources_new RENAME TO resources") + conn.execute("PRAGMA foreign_key_check") + conn.execute("PRAGMA foreign_keys = ON") + + +def restore_old_table(conn): + """Restore old 'resources' table schema.""" + conn.execute( + """ + CREATE TABLE resources_old( + resource_id TEXT NOT NULL, + resource_name TEXT NOT NULL UNIQUE, + resource_category_id TEXT NOT NULL, + public INTEGER NOT NULL DEFAULT 0 CHECK (public=0 or public=1), + PRIMARY KEY(resource_id), + FOREIGN KEY(resource_category_id) + REFERENCES resource_categories(resource_category_id) + ON UPDATE CASCADE ON DELETE RESTRICT + ) WITHOUT ROWID + """) + + +def parse_creator_and_time(cursor, row): + __return__ = None + + __name_parts__ = row[1].split("—") + if len(__name_parts__) == 4: + __email__, __inbredsetname__, __datetimestr__, count = __name_parts__ + cursor.execute("SELECT user_id FROM users WHERE email=?", + (__email__.strip(),)) + results = cursor.fetchone() + if bool(results): + __return__ = { + "resource_id": row[0], + "creator": results[0], + "created": datetime.fromisoformat(__datetimestr__).timestamp() + } + + return __return__ + + +def update_creators_and_time(conn): + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute("SELECT resource_id, resource_name FROM resources") + cursor.executemany( + "UPDATE resources SET created_by=:creator, created_at=:created " + "WHERE resource_id=:resource_id", + tuple(item for item in + (parse_creator_and_time(cursor, row) + for row in cursor.fetchall()) + if item is not None)) + + + +def restore_default_creators_and_time(conn): + with contextlib.closing(conn.cursor()) as cursor: + __creator__ = ( + fetch_acentenos_id(conn) or fetch_a_sysadmin_id(conn, "resources")) + cursor.execute("UPDATE resources SET created_by=?, created_at=?", + (__creator__, GN_AUTH_INIT_TIMESTAMP)) + + +steps = [ + step(add_user_and_time_tracking_columns, + drop_user_and_time_tracking_columns), + step(update_data_for_new_resources_table, + restore_data_for_old_resources_table), + step(replace_old_table_with_new_table, restore_old_table), + step(update_creators_and_time, restore_default_creators_and_time) +] diff --git a/gn_auth/migrations/auth/20260428_01_Tak6O-new-privilege-system-system-wide-data-view.py b/gn_auth/migrations/auth/20260428_01_Tak6O-new-privilege-system-system-wide-data-view.py new file mode 100644 index 0000000..2dddc56 --- /dev/null +++ b/gn_auth/migrations/auth/20260428_01_Tak6O-new-privilege-system-system-wide-data-view.py @@ -0,0 +1,19 @@ +""" +New privilege: system:system-wide:data:view +""" + +from yoyo import step + +__depends__ = {'20260402_01_Bf8nm-add-user-and-time-tracking-to-resources-table'} + +steps = [ + step( + """ + INSERT INTO privileges(privilege_id, privilege_description) + VALUES('system:system-wide:data:view', + 'A user with this privilege can view any data on the entire system.') + """, + """ + DELETE FROM privileges WHERE privilege_id='system:system-wide:data:view' + """) +] diff --git a/gn_auth/migrations/auth/20260428_02_L6zIV-add-privileges-to-batch-editors-role.py b/gn_auth/migrations/auth/20260428_02_L6zIV-add-privileges-to-batch-editors-role.py new file mode 100644 index 0000000..537bf9b --- /dev/null +++ b/gn_auth/migrations/auth/20260428_02_L6zIV-add-privileges-to-batch-editors-role.py @@ -0,0 +1,62 @@ +""" +Add privileges to batch-editors role +""" +import contextlib + +from yoyo import step + +__depends__ = {'20260428_01_Tak6O-new-privilege-system-system-wide-data-view'} + + +def fetch_batch_editors_role_id(cursor): + """Fetch the ID of the batch-editors role.""" + cursor.execute("SELECT role_id FROM roles WHERE role_name='Batch Editors'") + res = cursor.fetchone() + if not bool(res): + cursor.execute( + "SELECT role_id FROM roles WHERE role_name='batch-editors'") + res = cursor.fetchone() + + return res[0] if bool(res) else None + + +def rename_role(conn): + """Rename role from 'Batch Editors' to 'batch-editors'.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "UPDATE roles SET role_name='batch-editors' WHERE role_id=?", + (fetch_batch_editors_role_id(cursor),)) + + +def restore_old_role_name(conn): + """Rename role from 'batch-editors' to 'Batch Editors'.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "UPDATE roles SET role_name='Batch Editors' WHERE role_id=?", + (fetch_batch_editors_role_id(cursor),)) + + +def add_new_privileges(conn): + """Add new privileges to 'batch-editors' role.""" + with contextlib.closing(conn.cursor()) as cursor: + role_id = fetch_batch_editors_role_id(cursor) + cursor.executemany( + "INSERT INTO role_privileges(role_id, privilege_id) VALUES(?, ?)", + tuple((role_id, priv) for priv in ( + "system:system-wide:data:view", + "system:system-wide:data:edit"))) + + +def remove_new_privileges(conn): + """Remove new privileges from 'batch-editors' role.""" + with contextlib.closing(conn.cursor()) as cursor: + cursor.execute( + "DELETE FROM role_privileges WHERE role_id=? AND privilege_id IN " + "('system:system-wide:data:view', 'system:system-wide:data:edit')", + (fetch_batch_editors_role_id(cursor),)) + + +steps = [ + step(rename_role, restore_old_role_name), + step(add_new_privileges, remove_new_privileges) +] diff --git a/gn_auth/migrations/auth/__init__.py b/gn_auth/migrations/auth/__init__.py new file mode 100644 index 0000000..1358c9a --- /dev/null +++ b/gn_auth/migrations/auth/__init__.py @@ -0,0 +1 @@ +"Auth(entic|oris)ation package." diff --git a/gn_auth/settings.py b/gn_auth/settings.py index fe0ac92..f903553 100644 --- a/gn_auth/settings.py +++ b/gn_auth/settings.py @@ -14,7 +14,7 @@ SESSION_EXPIRY_MINUTES = 10 # Database settings SQL_URI = "mysql://webqtlout:webqtlout@localhost/db_webqtl" AUTH_DB = f"{os.environ.get('HOME')}/genenetwork/gn3_files/db/auth.db" -AUTH_MIGRATIONS = "migrations/auth" +AUTH_MIGRATIONS = "gn_auth/migrations/auth" # Redis settings REDIS_URI = "redis://localhost:6379/0" diff --git a/migrations/__init__.py b/migrations/__init__.py deleted file mode 100644 index cedf48d..0000000 --- a/migrations/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Migrations package""" diff --git a/migrations/auth/20221103_01_js9ub-initialise-the-auth-entic-oris-ation-database.py b/migrations/auth/20221103_01_js9ub-initialise-the-auth-entic-oris-ation-database.py deleted file mode 100644 index d511f5d..0000000 --- a/migrations/auth/20221103_01_js9ub-initialise-the-auth-entic-oris-ation-database.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Initialise the auth(entic|oris)ation database. -""" - -from yoyo import step - -__depends__ = {} # type: ignore[var-annotated] - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS users( - user_id TEXT PRIMARY KEY NOT NULL, - email TEXT UNIQUE NOT NULL, - name TEXT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS users") -] diff --git a/migrations/auth/20221103_02_sGrIs-create-user-credentials-table.py b/migrations/auth/20221103_02_sGrIs-create-user-credentials-table.py deleted file mode 100644 index 48bd663..0000000 --- a/migrations/auth/20221103_02_sGrIs-create-user-credentials-table.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -create user_credentials table -""" - -from yoyo import step - -__depends__ = {'20221103_01_js9ub-initialise-the-auth-entic-oris-ation-database'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS user_credentials( - user_id TEXT PRIMARY KEY, - password TEXT NOT NULL, - FOREIGN KEY(user_id) REFERENCES users(user_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS user_credentials") -] diff --git a/migrations/auth/20221108_01_CoxYh-create-the-groups-table.py b/migrations/auth/20221108_01_CoxYh-create-the-groups-table.py deleted file mode 100644 index 29f92d4..0000000 --- a/migrations/auth/20221108_01_CoxYh-create-the-groups-table.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Create the groups table -""" - -from yoyo import step - -__depends__ = {'20221103_02_sGrIs-create-user-credentials-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS groups( - group_id TEXT PRIMARY KEY NOT NULL, - group_name TEXT NOT NULL, - group_metadata TEXT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS groups") -] diff --git a/migrations/auth/20221108_02_wxTr9-create-privileges-table.py b/migrations/auth/20221108_02_wxTr9-create-privileges-table.py deleted file mode 100644 index 67720b2..0000000 --- a/migrations/auth/20221108_02_wxTr9-create-privileges-table.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Create privileges table -""" - -from yoyo import step - -__depends__ = {'20221108_01_CoxYh-create-the-groups-table'} - -steps = [ - step( - """ - CREATE TABLE privileges( - privilege_id TEXT PRIMARY KEY, - privilege_name TEXT NOT NULL - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS privileges") -] diff --git a/migrations/auth/20221108_03_Pbhb1-create-resource-categories-table.py b/migrations/auth/20221108_03_Pbhb1-create-resource-categories-table.py deleted file mode 100644 index ce752ef..0000000 --- a/migrations/auth/20221108_03_Pbhb1-create-resource-categories-table.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Create resource_categories table -""" - -from yoyo import step - -__depends__ = {'20221108_02_wxTr9-create-privileges-table'} - -steps = [ - step( - """ - CREATE TABLE resource_categories( - resource_category_id TEXT PRIMARY KEY, - resource_category_key TEXT NOT NULL, - resource_category_description TEXT NOT NULL - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS resource_categories") -] diff --git a/migrations/auth/20221108_04_CKcSL-init-data-in-resource-categories-table.py b/migrations/auth/20221108_04_CKcSL-init-data-in-resource-categories-table.py deleted file mode 100644 index 76ffbef..0000000 --- a/migrations/auth/20221108_04_CKcSL-init-data-in-resource-categories-table.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Init data in resource_categories table -""" - -from yoyo import step - -__depends__ = {'20221108_03_Pbhb1-create-resource-categories-table'} - -steps = [ - step( - """ - INSERT INTO resource_categories VALUES - ('fad071a3-2fc8-40b8-992b-cdefe7dcac79', 'mrna', 'mRNA Dataset'), - ('548d684b-d4d1-46fb-a6d3-51a56b7da1b3', 'phenotype', 'Phenotype (Publish) Dataset'), - ('48056f84-a2a6-41ac-8319-0e1e212cba2a', 'genotype', 'Genotype Dataset') - """, - """ - DELETE FROM resource_categories WHERE resource_category_id IN - ( - 'fad071a3-2fc8-40b8-992b-cdefe7dcac79', - '548d684b-d4d1-46fb-a6d3-51a56b7da1b3', - '48056f84-a2a6-41ac-8319-0e1e212cba2a' - ) - """) -] diff --git a/migrations/auth/20221109_01_HbD5F-add-resource-meta-field-to-resource-categories-field.py b/migrations/auth/20221109_01_HbD5F-add-resource-meta-field-to-resource-categories-field.py deleted file mode 100644 index 6c829b1..0000000 --- a/migrations/auth/20221109_01_HbD5F-add-resource-meta-field-to-resource-categories-field.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -Add 'resource_meta' field to 'resource_categories' field. -""" - -from yoyo import step - -__depends__ = {'20221108_04_CKcSL-init-data-in-resource-categories-table'} - -steps = [ - step( - """ - ALTER TABLE resource_categories - ADD COLUMN - resource_meta TEXT NOT NULL DEFAULT '[]' - """, - "ALTER TABLE resource_categories DROP COLUMN resource_meta") -] diff --git a/migrations/auth/20221110_01_WtZ1I-create-resources-table.py b/migrations/auth/20221110_01_WtZ1I-create-resources-table.py deleted file mode 100644 index abc8895..0000000 --- a/migrations/auth/20221110_01_WtZ1I-create-resources-table.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Create 'resources' table -""" - -from yoyo import step - -__depends__ = {'20221109_01_HbD5F-add-resource-meta-field-to-resource-categories-field'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS resources( - group_id TEXT NOT NULL, - resource_id TEXT NOT NULL, - resource_name TEXT NOT NULL UNIQUE, - resource_category_id TEXT NOT NULL, - PRIMARY KEY(group_id, resource_id), - FOREIGN KEY(group_id) REFERENCES groups(group_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(resource_category_id) - REFERENCES resource_categories(resource_category_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS resources") -] diff --git a/migrations/auth/20221110_05_BaNtL-create-roles-table.py b/migrations/auth/20221110_05_BaNtL-create-roles-table.py deleted file mode 100644 index 51e19e8..0000000 --- a/migrations/auth/20221110_05_BaNtL-create-roles-table.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Create 'roles' table -""" - -from yoyo import step - -__depends__ = {'20221110_01_WtZ1I-create-resources-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS roles( - role_id TEXT NOT NULL PRIMARY KEY, - role_name TEXT NOT NULL, - user_editable INTEGER NOT NULL DEFAULT 1 CHECK (user_editable=0 or user_editable=1) - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS roles") -] diff --git a/migrations/auth/20221110_06_Pq2kT-create-generic-roles-table.py b/migrations/auth/20221110_06_Pq2kT-create-generic-roles-table.py deleted file mode 100644 index 2b55c2b..0000000 --- a/migrations/auth/20221110_06_Pq2kT-create-generic-roles-table.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Create 'generic_roles' table - -The roles in this table will be template roles, defining some common roles that -can be used within the groups. - -They could also be used to define system-level roles, though those will not be -provided to the "common" users. -""" - -from yoyo import step - -__depends__ = {'20221110_05_BaNtL-create-roles-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS generic_roles( - role_id TEXT PRIMARY KEY, - role_name TEXT NOT NULL - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS generic_roles") -] diff --git a/migrations/auth/20221110_07_7WGa1-create-role-privileges-table.py b/migrations/auth/20221110_07_7WGa1-create-role-privileges-table.py deleted file mode 100644 index 0d0eeb9..0000000 --- a/migrations/auth/20221110_07_7WGa1-create-role-privileges-table.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Create 'role_privileges' table -""" - -from yoyo import step - -__depends__ = {'20221110_06_Pq2kT-create-generic-roles-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS role_privileges( - role_id TEXT NOT NULL, - privilege_id TEXT NOT NULL, - PRIMARY KEY(role_id, privilege_id), - FOREIGN KEY(role_id) REFERENCES roles(role_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(privilege_id) REFERENCES privileges(privilege_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS role_privileges"), - step( - """ - CREATE INDEX IF NOT EXISTS idx_tbl_role_privileges_cols_role_id - ON role_privileges(role_id) - """, - "DROP INDEX IF EXISTS idx_tbl_role_privileges_cols_role_id") -] diff --git a/migrations/auth/20221110_08_23psB-add-privilege-category-and-privilege-description-columns-to-privileges-table.py b/migrations/auth/20221110_08_23psB-add-privilege-category-and-privilege-description-columns-to-privileges-table.py deleted file mode 100644 index 077182b..0000000 --- a/migrations/auth/20221110_08_23psB-add-privilege-category-and-privilege-description-columns-to-privileges-table.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -Add 'privilege_category' and 'privilege_description' columns to 'privileges' table -""" - -from yoyo import step - -__depends__ = {'20221110_07_7WGa1-create-role-privileges-table'} - -steps = [ - step( - """ - ALTER TABLE privileges ADD COLUMN - privilege_category TEXT NOT NULL DEFAULT 'common' - """, - "ALTER TABLE privileges DROP COLUMN privilege_category"), - step( - """ - ALTER TABLE privileges ADD COLUMN - privilege_description TEXT - """, - "ALTER TABLE privileges DROP COLUMN privilege_description") -] diff --git a/migrations/auth/20221113_01_7M0hv-enumerate-initial-privileges.py b/migrations/auth/20221113_01_7M0hv-enumerate-initial-privileges.py deleted file mode 100644 index 072f226..0000000 --- a/migrations/auth/20221113_01_7M0hv-enumerate-initial-privileges.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -Enumerate initial privileges -""" - -from yoyo import step - -__depends__ = {'20221110_08_23psB-add-privilege-category-and-privilege-description-columns-to-privileges-table'} - -steps = [ - step( - """ - INSERT INTO - privileges(privilege_id, privilege_name, privilege_category, - privilege_description) - VALUES - -- group-management privileges - ('4842e2aa-38b9-4349-805e-0a99a9cf8bff', 'create-group', - 'group-management', 'Create a group'), - ('3ebfe79c-d159-4629-8b38-772cf4bc2261', 'view-group', - 'group-management', 'View the details of a group'), - ('52576370-b3c7-4e6a-9f7e-90e9dbe24d8f', 'edit-group', - 'group-management', 'Edit the details of a group'), - ('13ec2a94-4f1a-442d-aad2-936ad6dd5c57', 'delete-group', - 'group-management', 'Delete a group'), - ('ae4add8c-789a-4d11-a6e9-a306470d83d9', 'add-group-member', - 'group-management', 'Add a user to a group'), - ('f1bd3f42-567e-4965-9643-6d1a52ddee64', 'remove-group-member', - 'group-management', 'Remove a user from a group'), - ('80f11285-5079-4ec0-907c-06509f88a364', 'assign-group-leader', - 'group-management', 'Assign user group-leader privileges'), - ('d4afe2b3-4ca0-4edd-b37d-966535b5e5bd', - 'transfer-group-leadership', 'group-management', - 'Transfer leadership of the group to some other member'), - - -- resource-management privileges - ('aa25b32a-bff2-418d-b0a2-e26b4a8f089b', 'create-resource', - 'resource-management', 'Create a resource object'), - ('7f261757-3211-4f28-a43f-a09b800b164d', 'view-resource', - 'resource-management', 'view a resource and use it in computations'), - ('2f980855-959b-4339-b80e-25d1ec286e21', 'edit-resource', - 'resource-management', 'edit/update a resource'), - ('d2a070fd-e031-42fb-ba41-d60cf19e5d6d', 'delete-resource', - 'resource-management', 'Delete a resource'), - - -- role-management privileges - ('221660b1-df05-4be1-b639-f010269dbda9', 'create-role', - 'role-management', 'Create a new role'), - ('7bcca363-cba9-4169-9e31-26bdc6179b28', 'edit-role', - 'role-management', 'edit/update an existing role'), - ('5103cc68-96f8-4ebb-83a4-a31692402c9b', 'assign-role', - 'role-management', 'Assign a role to an existing user'), - ('1c59eff5-9336-4ed2-a166-8f70d4cb012e', 'delete-role', - 'role-management', 'Delete an existing role'), - - -- user-management privileges - ('e7252301-6ee0-43ba-93ef-73b607cf06f6', 'reset-any-password', - 'user-management', 'Reset the password for any user'), - ('1fe61370-cae9-4983-bd6c-ce61050c510f', 'delete-any-user', - 'user-management', 'Delete any user from the system'), - - -- sytem-admin privileges - ('519db546-d44e-4fdc-9e4e-25aa67548ab3', 'masquerade', - 'system-admin', 'Masquerade as some other user') - """, - "DELETE FROM privileges") -] diff --git a/migrations/auth/20221114_01_n8gsF-create-generic-role-privileges-table.py b/migrations/auth/20221114_01_n8gsF-create-generic-role-privileges-table.py deleted file mode 100644 index 2048f4a..0000000 --- a/migrations/auth/20221114_01_n8gsF-create-generic-role-privileges-table.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Create 'generic_role_privileges' table - -This table links the generic_roles to the privileges they provide -""" - -from yoyo import step - -__depends__ = {'20221113_01_7M0hv-enumerate-initial-privileges'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS generic_role_privileges( - generic_role_id TEXT NOT NULL, - privilege_id TEXT NOT NULL, - PRIMARY KEY(generic_role_id, privilege_id), - FOREIGN KEY(generic_role_id) REFERENCES generic_roles(role_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(privilege_id) REFERENCES privileges(privilege_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS generic_role_privileges"), - step( - """ - CREATE INDEX IF NOT EXISTS - idx_tbl_generic_role_privileges_cols_generic_role_id - ON generic_role_privileges(generic_role_id) - """, - """ - DROP INDEX IF EXISTS - idx_tbl_generic_role_privileges_cols_generic_role_id - """) -] diff --git a/migrations/auth/20221114_02_DKKjn-drop-generic-role-tables.py b/migrations/auth/20221114_02_DKKjn-drop-generic-role-tables.py deleted file mode 100644 index 6bd101b..0000000 --- a/migrations/auth/20221114_02_DKKjn-drop-generic-role-tables.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -Drop 'generic_role*' tables -""" - -from yoyo import step - -__depends__ = {'20221114_01_n8gsF-create-generic-role-privileges-table'} - -steps = [ - step( - """ - DROP INDEX IF EXISTS - idx_tbl_generic_role_privileges_cols_generic_role_id - """, - """ - CREATE INDEX IF NOT EXISTS - idx_tbl_generic_role_privileges_cols_generic_role_id - ON generic_role_privileges(generic_role_id) - """), - step( - "DROP TABLE IF EXISTS generic_role_privileges", - """ - CREATE TABLE IF NOT EXISTS generic_role_privileges( - generic_role_id TEXT NOT NULL, - privilege_id TEXT NOT NULL, - PRIMARY KEY(generic_role_id, privilege_id), - FOREIGN KEY(generic_role_id) REFERENCES generic_roles(role_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(privilege_id) REFERENCES privileges(privilege_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """), - step( - "DROP TABLE IF EXISTS generic_roles", - """ - CREATE TABLE IF NOT EXISTS generic_roles( - role_id TEXT PRIMARY KEY, - role_name TEXT NOT NULL - ) WITHOUT ROWID - """) -] diff --git a/migrations/auth/20221114_03_PtWjc-create-group-roles-table.py b/migrations/auth/20221114_03_PtWjc-create-group-roles-table.py deleted file mode 100644 index a7e7b45..0000000 --- a/migrations/auth/20221114_03_PtWjc-create-group-roles-table.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Create 'group_roles' table -""" - -from yoyo import step - -__depends__ = {'20221114_02_DKKjn-drop-generic-role-tables'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS group_roles( - group_id TEXT NOT NULL, - role_id TEXT NOT NULL, - PRIMARY KEY(group_id, role_id), - FOREIGN KEY(group_id) REFERENCES groups(group_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(role_id) REFERENCES roles(role_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS group_roles"), - step( - """ - CREATE INDEX IF NOT EXISTS idx_tbl_group_roles_cols_group_id - ON group_roles(group_id) - """, - "DROP INDEX IF EXISTS idx_tbl_group_roles_cols_group_id") -] diff --git a/migrations/auth/20221114_04_tLUzB-initialise-basic-roles.py b/migrations/auth/20221114_04_tLUzB-initialise-basic-roles.py deleted file mode 100644 index 386f481..0000000 --- a/migrations/auth/20221114_04_tLUzB-initialise-basic-roles.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Initialise basic roles -""" - -from yoyo import step - -__depends__ = {'20221114_03_PtWjc-create-group-roles-table'} - -steps = [ - step( - """ - INSERT INTO roles(role_id, role_name, user_editable) VALUES - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', 'group-leader', '0'), - ('522e4d40-aefc-4a64-b7e0-768b8be517ee', 'resource-owner', '0') - """, - "DELETE FROM roles"), - step( - """ - INSERT INTO role_privileges(role_id, privilege_id) - VALUES - -- group-management - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - '4842e2aa-38b9-4349-805e-0a99a9cf8bff'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - '3ebfe79c-d159-4629-8b38-772cf4bc2261'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - '52576370-b3c7-4e6a-9f7e-90e9dbe24d8f'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - '13ec2a94-4f1a-442d-aad2-936ad6dd5c57'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - 'ae4add8c-789a-4d11-a6e9-a306470d83d9'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - 'f1bd3f42-567e-4965-9643-6d1a52ddee64'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - 'd4afe2b3-4ca0-4edd-b37d-966535b5e5bd'), - - -- resource-management - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - 'aa25b32a-bff2-418d-b0a2-e26b4a8f089b'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - '7f261757-3211-4f28-a43f-a09b800b164d'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - '2f980855-959b-4339-b80e-25d1ec286e21'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - 'd2a070fd-e031-42fb-ba41-d60cf19e5d6d'), - ('522e4d40-aefc-4a64-b7e0-768b8be517ee', - 'aa25b32a-bff2-418d-b0a2-e26b4a8f089b'), - ('522e4d40-aefc-4a64-b7e0-768b8be517ee', - '7f261757-3211-4f28-a43f-a09b800b164d'), - ('522e4d40-aefc-4a64-b7e0-768b8be517ee', - '2f980855-959b-4339-b80e-25d1ec286e21'), - ('522e4d40-aefc-4a64-b7e0-768b8be517ee', - 'd2a070fd-e031-42fb-ba41-d60cf19e5d6d') - """, - "DELETE FROM role_privileges") -] diff --git a/migrations/auth/20221114_05_hQun6-create-user-roles-table.py b/migrations/auth/20221114_05_hQun6-create-user-roles-table.py deleted file mode 100644 index e0de751..0000000 --- a/migrations/auth/20221114_05_hQun6-create-user-roles-table.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Create 'user_roles' table. -""" - -from yoyo import step - -__depends__ = {'20221114_04_tLUzB-initialise-basic-roles'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS 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 - """, - "DROP TABLE IF EXISTS user_roles"), - step( - """ - CREATE INDEX IF NOT EXISTS idx_tbl_user_roles_cols_user_id - ON user_roles(user_id) - """, - "DROP INDEX IF EXISTS idx_tbl_user_roles_cols_user_id") -] diff --git a/migrations/auth/20221116_01_nKUmX-add-privileges-to-group-leader-role.py b/migrations/auth/20221116_01_nKUmX-add-privileges-to-group-leader-role.py deleted file mode 100644 index 2e4ae28..0000000 --- a/migrations/auth/20221116_01_nKUmX-add-privileges-to-group-leader-role.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Add privileges to 'group-leader' role. -""" - -from yoyo import step - -__depends__ = {'20221114_05_hQun6-create-user-roles-table'} - -steps = [ - step( - """ - INSERT INTO role_privileges(role_id, privilege_id) - VALUES - -- role management - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - '221660b1-df05-4be1-b639-f010269dbda9'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - '7bcca363-cba9-4169-9e31-26bdc6179b28'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - '5103cc68-96f8-4ebb-83a4-a31692402c9b'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - '1c59eff5-9336-4ed2-a166-8f70d4cb012e') - """, - """ - DELETE FROM role_privileges - WHERE - role_id='a0e67630-d502-4b9f-b23f-6805d0f30e30' - AND privilege_id IN ( - '221660b1-df05-4be1-b639-f010269dbda9', - '7bcca363-cba9-4169-9e31-26bdc6179b28', - '5103cc68-96f8-4ebb-83a4-a31692402c9b', - '1c59eff5-9336-4ed2-a166-8f70d4cb012e' - ) - """) -] diff --git a/migrations/auth/20221117_01_RDlfx-modify-group-roles-add-group-role-id.py b/migrations/auth/20221117_01_RDlfx-modify-group-roles-add-group-role-id.py deleted file mode 100644 index a4d7806..0000000 --- a/migrations/auth/20221117_01_RDlfx-modify-group-roles-add-group-role-id.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -Modify 'group_roles': add 'group_role_id' - -At this point, there is no data in the `group_roles` table and therefore, it -should be safe to simply recreate it. -""" - -from yoyo import step - -__depends__ = {'20221116_01_nKUmX-add-privileges-to-group-leader-role'} - -steps = [ - step( - "DROP INDEX IF EXISTS idx_tbl_group_roles_cols_group_id", - """ - CREATE INDEX IF NOT EXISTS idx_tbl_group_roles_cols_group_id - ON group_roles(group_id) - """), - step( - "DROP TABLE IF EXISTS group_roles", - """ - CREATE TABLE IF NOT EXISTS group_roles( - group_id TEXT NOT NULL, - role_id TEXT NOT NULL, - PRIMARY KEY(group_id, role_id), - FOREIGN KEY(group_id) REFERENCES groups(group_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(role_id) REFERENCES roles(role_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """), - step( - """ - CREATE TABLE IF NOT EXISTS group_roles( - group_role_id TEXT PRIMARY KEY, - group_id TEXT NOT NULL, - role_id TEXT NOT NULL, - UNIQUE (group_id, role_id), - FOREIGN KEY(group_id) REFERENCES groups(group_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(role_id) REFERENCES roles(role_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS group_roles"), - step( - """ - CREATE INDEX IF NOT EXISTS idx_tbl_group_roles_cols_group_id - ON group_roles(group_id) - """, - "DROP INDEX IF EXISTS idx_tbl_group_roles_cols_group_id") -] diff --git a/migrations/auth/20221117_02_fmuZh-create-group-users-table.py b/migrations/auth/20221117_02_fmuZh-create-group-users-table.py deleted file mode 100644 index 92885ef..0000000 --- a/migrations/auth/20221117_02_fmuZh-create-group-users-table.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Create 'group_users' table. -""" - -from yoyo import step - -__depends__ = {'20221117_01_RDlfx-modify-group-roles-add-group-role-id'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS group_users( - group_id TEXT NOT NULL, - user_id TEXT NOT NULL UNIQUE, -- user can only be in one group - PRIMARY KEY(group_id, user_id) - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS group_users"), - step( - """ - CREATE INDEX IF NOT EXISTS tbl_group_users_cols_group_id - ON group_users(group_id) - """, - "DROP INDEX IF EXISTS tbl_group_users_cols_group_id") -] diff --git a/migrations/auth/20221206_01_BbeF9-create-group-user-roles-on-resources-table.py b/migrations/auth/20221206_01_BbeF9-create-group-user-roles-on-resources-table.py deleted file mode 100644 index 9aa3667..0000000 --- a/migrations/auth/20221206_01_BbeF9-create-group-user-roles-on-resources-table.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Create 'group_user_roles_on_resources' table -""" - -from yoyo import step - -__depends__ = {'20221117_02_fmuZh-create-group-users-table'} - -steps = [ - step( - """ - CREATE TABLE 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 (group_id, resource_id) - REFERENCES resources(group_id, resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS group_user_roles_on_resources"), - step( - """ - CREATE INDEX IF NOT EXISTS - idx_tbl_group_user_roles_on_resources_group_user_resource - ON group_user_roles_on_resources(group_id, user_id, resource_id) - """, - """ - DROP INDEX IF EXISTS - idx_tbl_group_user_roles_on_resources_group_user_resource""") -] diff --git a/migrations/auth/20221208_01_sSdHz-add-public-column-to-resources-table.py b/migrations/auth/20221208_01_sSdHz-add-public-column-to-resources-table.py deleted file mode 100644 index 2238069..0000000 --- a/migrations/auth/20221208_01_sSdHz-add-public-column-to-resources-table.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Add 'public' column to 'resources' table -""" - -from yoyo import step - -__depends__ = {'20221206_01_BbeF9-create-group-user-roles-on-resources-table'} - -steps = [ - step( - """ - ALTER TABLE resources ADD COLUMN - public INTEGER NOT NULL DEFAULT 0 CHECK (public=0 or public=1) - """, - "ALTER TABLE resources DROP COLUMN public") -] diff --git a/migrations/auth/20221219_01_CI3tN-create-oauth2-clients-table.py b/migrations/auth/20221219_01_CI3tN-create-oauth2-clients-table.py deleted file mode 100644 index 475be01..0000000 --- a/migrations/auth/20221219_01_CI3tN-create-oauth2-clients-table.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -create oauth2_clients table -""" - -from yoyo import step - -__depends__ = {'20221208_01_sSdHz-add-public-column-to-resources-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS oauth2_clients( - client_id TEXT NOT NULL, - client_secret TEXT NOT NULL, - client_id_issued_at INTEGER NOT NULL, - client_secret_expires_at INTEGER NOT NULL, - client_metadata TEXT, - user_id TEXT NOT NULL, - PRIMARY KEY(client_id), - FOREIGN KEY(user_id) REFERENCES users(user_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS oauth2_clients") -] diff --git a/migrations/auth/20221219_02_buSEU-create-oauth2-tokens-table.py b/migrations/auth/20221219_02_buSEU-create-oauth2-tokens-table.py deleted file mode 100644 index 778282b..0000000 --- a/migrations/auth/20221219_02_buSEU-create-oauth2-tokens-table.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -create oauth2_tokens table -""" - -from yoyo import step - -__depends__ = {'20221219_01_CI3tN-create-oauth2-clients-table'} - -steps = [ - step( - """ - CREATE TABLE oauth2_tokens( - token_id TEXT NOT NULL, - client_id TEXT NOT NULL, - token_type TEXT NOT NULL, - access_token TEXT UNIQUE NOT NULL, - refresh_token TEXT, - scope TEXT, - revoked INTEGER CHECK (revoked = 0 or revoked = 1), - issued_at INTEGER NOT NULL, - expires_in INTEGER NOT NULL, - user_id TEXT NOT NULL, - PRIMARY KEY(token_id), - FOREIGN KEY (client_id) REFERENCES oauth2_clients(client_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (user_id) REFERENCES users(user_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS oauth2_tokens") -] diff --git a/migrations/auth/20221219_03_PcTrb-create-authorisation-code-table.py b/migrations/auth/20221219_03_PcTrb-create-authorisation-code-table.py deleted file mode 100644 index 1683f87..0000000 --- a/migrations/auth/20221219_03_PcTrb-create-authorisation-code-table.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -create authorisation_code table -""" - -from yoyo import step - -__depends__ = {'20221219_02_buSEU-create-oauth2-tokens-table'} - -steps = [ - step( - """ - CREATE TABLE authorisation_code ( - code_id TEXT NOT NULL, - code TEXT UNIQUE NOT NULL, - client_id NOT NULL, - redirect_uri TEXT, - scope TEXT, - nonce TEXT, - auth_time INTEGER NOT NULL, - code_challenge TEXT, - code_challenge_method TEXT, - user_id TEXT NOT NULL, - PRIMARY KEY (code_id), - FOREIGN KEY (client_id) REFERENCES oauth2_clients(client_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (user_id) REFERENCES users(user_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS authorisation_code") -] diff --git a/migrations/auth/20230111_01_Wd6IZ-remove-create-group-privilege-from-group-leader.py b/migrations/auth/20230111_01_Wd6IZ-remove-create-group-privilege-from-group-leader.py deleted file mode 100644 index 7e7fda2..0000000 --- a/migrations/auth/20230111_01_Wd6IZ-remove-create-group-privilege-from-group-leader.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -remove 'create-group' privilege from group-leader. -""" - -from yoyo import step - -__depends__ = {'20221219_03_PcTrb-create-authorisation-code-table'} - -steps = [ - step( - """ - DELETE FROM role_privileges - WHERE role_id='a0e67630-d502-4b9f-b23f-6805d0f30e30' - AND privilege_id='4842e2aa-38b9-4349-805e-0a99a9cf8bff' - """, - """ - INSERT INTO role_privileges VALUES - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', - '4842e2aa-38b9-4349-805e-0a99a9cf8bff') - """), - step( - """ - INSERT INTO roles(role_id, role_name, user_editable) VALUES - ('ade7e6b0-ba9c-4b51-87d0-2af7fe39a347', 'group-creator', '0') - """, - """ - DELETE FROM roles WHERE role_id='ade7e6b0-ba9c-4b51-87d0-2af7fe39a347' - """), - step( - """ - INSERT INTO role_privileges VALUES - ('ade7e6b0-ba9c-4b51-87d0-2af7fe39a347', - '4842e2aa-38b9-4349-805e-0a99a9cf8bff') - """, - """ - DELETE FROM role_privileges - WHERE role_id='ade7e6b0-ba9c-4b51-87d0-2af7fe39a347' - AND privilege_id='4842e2aa-38b9-4349-805e-0a99a9cf8bff' - """) -] diff --git a/migrations/auth/20230116_01_KwuJ3-rework-privileges-schema.py b/migrations/auth/20230116_01_KwuJ3-rework-privileges-schema.py deleted file mode 100644 index 1ef5ab0..0000000 --- a/migrations/auth/20230116_01_KwuJ3-rework-privileges-schema.py +++ /dev/null @@ -1,111 +0,0 @@ -""" -rework privileges schema -""" -import contextlib - -from yoyo import step - -__depends__ = {'20230111_01_Wd6IZ-remove-create-group-privilege-from-group-leader'} - -privileges = ( # format: (original_id, original_name, new_id, category) - ("13ec2a94-4f1a-442d-aad2-936ad6dd5c57", "delete-group", - "system:group:delete-group", "group-management"), - ("1c59eff5-9336-4ed2-a166-8f70d4cb012e", "delete-role", - "group:role:delete-role", "role-management"), - ("1fe61370-cae9-4983-bd6c-ce61050c510f", "delete-any-user", - "system:user:delete-user", "user-management"), - ("221660b1-df05-4be1-b639-f010269dbda9", "create-role", - "group:role:create-role", "role-management"), - ("2f980855-959b-4339-b80e-25d1ec286e21", "edit-resource", - "group:resource:edit-resource", "resource-management"), - ("3ebfe79c-d159-4629-8b38-772cf4bc2261", "view-group", - "system:group:view-group", "group-management"), - ("4842e2aa-38b9-4349-805e-0a99a9cf8bff", "create-group", - "system:group:create-group", "group-management"), - ("5103cc68-96f8-4ebb-83a4-a31692402c9b", "assign-role", - "group:user:assign-role", "role-management"), - ("519db546-d44e-4fdc-9e4e-25aa67548ab3", "masquerade", - "system:user:masquerade", "system-admin"), - ("52576370-b3c7-4e6a-9f7e-90e9dbe24d8f", "edit-group", - "system:group:edit-group", "group-management"), - ("7bcca363-cba9-4169-9e31-26bdc6179b28", "edit-role", - "group:role:edit-role", "role-management"), - ("7f261757-3211-4f28-a43f-a09b800b164d", "view-resource", - "group:resource:view-resource", "resource-management"), - ("80f11285-5079-4ec0-907c-06509f88a364", "assign-group-leader", - "system:user:assign-group-leader", "group-management"), - ("aa25b32a-bff2-418d-b0a2-e26b4a8f089b", "create-resource", - "group:resource:create-resource", "resource-management"), - ("ae4add8c-789a-4d11-a6e9-a306470d83d9", "add-group-member", - "group:user:add-group-member", "group-management"), - ("d2a070fd-e031-42fb-ba41-d60cf19e5d6d", "delete-resource", - "group:resource:delete-resource", "resource-management"), - ("d4afe2b3-4ca0-4edd-b37d-966535b5e5bd", "transfer-group-leadership", - "system:group:transfer-group-leader", "group-management"), - ("e7252301-6ee0-43ba-93ef-73b607cf06f6", "reset-any-password", - "system:user:reset-password", "user-management"), - ("f1bd3f42-567e-4965-9643-6d1a52ddee64", "remove-group-member", - "group:user:remove-group-member", "group-management")) - -def rework_privileges_table(cursor): - "rework the schema" - cursor.executemany( - ("UPDATE privileges SET privilege_id=:id " - "WHERE privilege_id=:old_id"), - ({"id": row[2], "old_id": row[0]} for row in privileges)) - cursor.execute("ALTER TABLE privileges DROP COLUMN privilege_category") - cursor.execute("ALTER TABLE privileges DROP COLUMN privilege_name") - -def restore_privileges_table(cursor): - "restore the schema" - cursor.execute(( - "CREATE TABLE privileges_restore (" - " privilege_id TEXT PRIMARY KEY," - " privilege_name TEXT NOT NULL," - " privilege_category TEXT NOT NULL DEFAULT 'common'," - " privilege_description TEXT" - ")")) - id_dict = {row[2]: {"id": row[0], "name": row[1], "cat": row[3]} - for row in privileges} - cursor.execute( - "SELECT privilege_id, privilege_description FROM privileges") - params = ({**id_dict[row[0]], "desc": row[1]} for row in cursor.fetchall()) - cursor.executemany( - "INSERT INTO privileges_restore VALUES (:id, :name, :cat, :desc)", - params) - cursor.execute("DROP TABLE privileges") - cursor.execute("ALTER TABLE privileges_restore RENAME TO privileges") - -def update_privilege_ids_in_role_privileges(cursor): - """Update the ids to new form.""" - cursor.executemany( - ("UPDATE role_privileges SET privilege_id=:new_id " - "WHERE privilege_id=:old_id"), - ({"new_id": row[2], "old_id": row[0]} for row in privileges)) - -def restore_privilege_ids_in_role_privileges(cursor): - """Restore original ids""" - cursor.executemany( - ("UPDATE role_privileges SET privilege_id=:old_id " - "WHERE privilege_id=:new_id"), - ({"new_id": row[2], "old_id": row[0]} for row in privileges)) - -def change_schema(conn): - """Change the privileges schema and IDs""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute("PRAGMA foreign_keys=OFF") - rework_privileges_table(cursor) - update_privilege_ids_in_role_privileges(cursor) - cursor.execute("PRAGMA foreign_keys=ON") - -def restore_schema(conn): - """Change the privileges schema and IDs""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute("PRAGMA foreign_keys=OFF") - restore_privilege_ids_in_role_privileges(cursor) - restore_privileges_table(cursor) - cursor.execute("PRAGMA foreign_keys=ON") - -steps = [ - step(change_schema, restore_schema) -] diff --git a/migrations/auth/20230207_01_r0bkZ-create-group-join-requests-table.py b/migrations/auth/20230207_01_r0bkZ-create-group-join-requests-table.py deleted file mode 100644 index ceae5ea..0000000 --- a/migrations/auth/20230207_01_r0bkZ-create-group-join-requests-table.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Create group_requests table -""" - -from yoyo import step - -__depends__ = {'20230116_01_KwuJ3-rework-privileges-schema'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS group_join_requests( - request_id TEXT NOT NULL, - group_id TEXT NOT NULL, - requester_id TEXT NOT NULL, - timestamp REAL NOT NULL, - status TEXT NOT NULL DEFAULT 'PENDING', - message TEXT, - PRIMARY KEY(request_id, group_id), - FOREIGN KEY(group_id) REFERENCES groups(group_id) - ON UPDATE CASCADE ON DELETE CASCADE, - FOREIGN KEY (requester_id) REFERENCES users(user_id) - ON UPDATE CASCADE ON DELETE CASCADE, - UNIQUE(group_id, requester_id), - CHECK (status IN ('PENDING', 'ACCEPTED', 'REJECTED')) - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS group_join_requests") -] diff --git a/migrations/auth/20230210_01_8xMa1-system-admin-privileges-for-data-distribution.py b/migrations/auth/20230210_01_8xMa1-system-admin-privileges-for-data-distribution.py deleted file mode 100644 index 8b406a6..0000000 --- a/migrations/auth/20230210_01_8xMa1-system-admin-privileges-for-data-distribution.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -System admin privileges for data distribution - -These privileges are focussed on allowing the system administrator to link the -datasets and traits in the main database to specific groups in the auth system. -""" - -from yoyo import step - -__depends__ = {'20230207_01_r0bkZ-create-group-join-requests-table'} - -steps = [ - step( - """ - INSERT INTO privileges VALUES - ('system:data:link-to-group', 'Link a dataset or trait to a group.') - """, - """ - DELETE FROM privileges WHERE privilege_id IN - ('system:data:link-to-group') - """) -] diff --git a/migrations/auth/20230210_02_lDK14-create-system-admin-role.py b/migrations/auth/20230210_02_lDK14-create-system-admin-role.py deleted file mode 100644 index 9b3fc2b..0000000 --- a/migrations/auth/20230210_02_lDK14-create-system-admin-role.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -Create system-admin role -""" -import uuid -from contextlib import closing - -from yoyo import step - -__depends__ = {'20230210_01_8xMa1-system-admin-privileges-for-data-distribution'} - -def create_sys_admin_role(conn): - with closing(conn.cursor()) as cursor: - role_id = uuid.uuid4() - cursor.execute( - "INSERT INTO roles VALUES (?, 'system-administrator', '0')", - (str(role_id),)) - - cursor.executemany( - "INSERT INTO role_privileges VALUES (:role_id, :privilege_id)", - ({"role_id": f"{role_id}", "privilege_id": priv} - for priv in ( - "system:data:link-to-group", - "system:group:create-group", - "system:group:delete-group", - "system:group:edit-group", - "system:group:transfer-group-leader", - "system:group:view-group", - "system:user:assign-group-leader", - "system:user:delete-user", - "system:user:masquerade", - "system:user:reset-password"))) - -def drop_sys_admin_role(conn): - pass - -steps = [ - step(create_sys_admin_role, drop_sys_admin_role) -] diff --git a/migrations/auth/20230306_01_pRfxl-add-system-user-list-privilege.py b/migrations/auth/20230306_01_pRfxl-add-system-user-list-privilege.py deleted file mode 100644 index 84bbd49..0000000 --- a/migrations/auth/20230306_01_pRfxl-add-system-user-list-privilege.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Add system:user:list privilege -""" -import contextlib - -from yoyo import step - -__depends__ = {'20230210_02_lDK14-create-system-admin-role'} - -def insert_users_list_priv(conn): - """Create a new 'system:user:list' privilege.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "INSERT INTO privileges(privilege_id, privilege_description) " - "VALUES('system:user:list', 'List users in the system') " - "ON CONFLICT (privilege_id) DO NOTHING") - -def delete_users_list_priv(conn): - """Delete the new 'system:user:list' privilege.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "DELETE FROM privileges WHERE privilege_id='system:user:list'") - -steps = [ - step(insert_users_list_priv, delete_users_list_priv) -] diff --git a/migrations/auth/20230306_02_7GnRY-add-system-user-list-privilege-to-system-administrator-and-group-leader-roles.py b/migrations/auth/20230306_02_7GnRY-add-system-user-list-privilege-to-system-administrator-and-group-leader-roles.py deleted file mode 100644 index 3caad55..0000000 --- a/migrations/auth/20230306_02_7GnRY-add-system-user-list-privilege-to-system-administrator-and-group-leader-roles.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -Add system:user:list privilege to system-administrator and group-leader roles. -""" -import uuid -import contextlib - -from yoyo import step - -__depends__ = {'20230306_01_pRfxl-add-system-user-list-privilege'} - -def role_ids(cursor): - """Get role ids from names""" - cursor.execute( - "SELECT * FROM roles WHERE role_name IN " - "('system-administrator', 'group-leader')") - return (uuid.UUID(row[0]) for row in cursor.fetchall()) - -def add_privilege_to_roles(conn): - """ - Add 'system:user:list' privilege to 'system-administrator' and - 'group-leader' roles.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.executemany( - "INSERT INTO role_privileges(role_id,privilege_id) " - "VALUES(?, ?)", - tuple((str(role_id), "system:user:list") - for role_id in role_ids(cursor))) - -def del_privilege_from_roles(conn): - """ - Delete 'system:user:list' privilege to 'system-administrator' and - 'group-leader' roles. - """ - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "DELETE FROM role_privileges WHERE " - "role_id IN (?, ?) AND privilege_id='system:user:list'", - tuple(str(role_id) for role_id in role_ids(cursor))) - -steps = [ - step(add_privilege_to_roles, del_privilege_from_roles) -] diff --git a/migrations/auth/20230322_01_0dDZR-create-linked-phenotype-data-table.py b/migrations/auth/20230322_01_0dDZR-create-linked-phenotype-data-table.py deleted file mode 100644 index 647325f..0000000 --- a/migrations/auth/20230322_01_0dDZR-create-linked-phenotype-data-table.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -Create linked-phenotype-data table -""" - -from yoyo import step - -__depends__ = {'20230306_02_7GnRY-add-system-user-list-privilege-to-system-administrator-and-group-leader-roles'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS linked_phenotype_data - -- Link the data in MariaDB to user groups in the auth system - ( - data_link_id TEXT NOT NULL PRIMARY KEY, -- A new ID for the auth system - group_id TEXT NOT NULL, -- The user group the data is linked to - SpeciesId TEXT NOT NULL, -- The species in MariaDB - InbredSetId TEXT NOT NULL, -- The traits group in MariaDB - PublishFreezeId TEXT NOT NULL, -- The dataset Id in MariaDB - dataset_name TEXT, -- dataset Name in MariaDB - dataset_fullname, -- dataset FullName in MariaDB - dataset_shortname, -- dataset ShortName in MariaDB - PublishXRefId TEXT NOT NULL, -- The trait's ID in MariaDB - FOREIGN KEY (group_id) - REFERENCES groups(group_id) ON UPDATE CASCADE ON DELETE RESTRICT - UNIQUE (SpeciesId, InbredSetId, PublishFreezeId, PublishXRefId) - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS linked_phenotype_data") -] diff --git a/migrations/auth/20230322_02_Ll854-create-phenotype-resources-table.py b/migrations/auth/20230322_02_Ll854-create-phenotype-resources-table.py deleted file mode 100644 index 7c9e986..0000000 --- a/migrations/auth/20230322_02_Ll854-create-phenotype-resources-table.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Create phenotype_resources table -""" - -from yoyo import step - -__depends__ = {'20230322_01_0dDZR-create-linked-phenotype-data-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS phenotype_resources - -- Link phenotype data to specific resources - ( - group_id TEXT NOT NULL, - resource_id TEXT NOT NULL, -- A resource can have multiple data items - data_link_id TEXT NOT NULL, - PRIMARY KEY(group_id, resource_id, data_link_id), - UNIQUE (data_link_id), -- ensure data is linked to only one resource - FOREIGN KEY (group_id, resource_id) - REFERENCES resources(group_id, resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (data_link_id) - REFERENCES linked_phenotype_data(data_link_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS phenotype_resources") -] diff --git a/migrations/auth/20230404_01_VKxXg-create-linked-genotype-data-table.py b/migrations/auth/20230404_01_VKxXg-create-linked-genotype-data-table.py deleted file mode 100644 index 02e8718..0000000 --- a/migrations/auth/20230404_01_VKxXg-create-linked-genotype-data-table.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Create linked genotype data table -""" - -from yoyo import step - -__depends__ = {'20230322_02_Ll854-create-phenotype-resources-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS linked_genotype_data - -- Link genotype data in MariaDB to user groups in auth system - ( - data_link_id TEXT NOT NULL PRIMARY KEY, -- A new ID for the auth system - group_id TEXT NOT NULL, -- The user group the data is linked to - SpeciesId TEXT NOT NULL, -- The species in MariaDB - InbredSetId TEXT NOT NULL, -- The traits group in MariaDB - GenoFreezeId TEXT NOT NULL, -- The dataset Id in MariaDB - dataset_name TEXT, -- dataset Name in MariaDB - dataset_fullname, -- dataset FullName in MariaDB - dataset_shortname, -- dataset ShortName in MariaDB - FOREIGN KEY (group_id) - REFERENCES groups(group_id) ON UPDATE CASCADE ON DELETE RESTRICT - UNIQUE (SpeciesId, InbredSetId, GenoFreezeId) - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS linked_genotype_data") -] diff --git a/migrations/auth/20230404_02_la33P-create-genotype-resources-table.py b/migrations/auth/20230404_02_la33P-create-genotype-resources-table.py deleted file mode 100644 index 1a865e0..0000000 --- a/migrations/auth/20230404_02_la33P-create-genotype-resources-table.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -Create genotype resources table -""" - -from yoyo import step - -__depends__ = {'20230404_01_VKxXg-create-linked-genotype-data-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS genotype_resources - -- Link genotype data to specific resource - ( - group_id TEXT NOT NULL, - resource_id TEXT NOT NULL, -- A resource can have multiple items - data_link_id TEXT NOT NULL, - PRIMARY KEY (group_id, resource_id, data_link_id), - UNIQUE (data_link_id) -- ensure data is linked to single resource - FOREIGN KEY (group_id, resource_id) - REFERENCES resources(group_id, resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (data_link_id) - REFERENCES linked_genotype_data(data_link_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS genotype_resources") -] diff --git a/migrations/auth/20230410_01_8mwaf-create-linked-mrna-data-table.py b/migrations/auth/20230410_01_8mwaf-create-linked-mrna-data-table.py deleted file mode 100644 index db9a6bf..0000000 --- a/migrations/auth/20230410_01_8mwaf-create-linked-mrna-data-table.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -Create linked mrna data table -""" - -from yoyo import step - -__depends__ = {'20230404_02_la33P-create-genotype-resources-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS linked_mrna_data - -- Link mRNA Assay data in MariaDB to user groups in auth system - ( - data_link_id TEXT NOT NULL PRIMARY KEY, -- A new ID for the auth system - group_id TEXT NOT NULL, -- The user group the data is linked to - SpeciesId TEXT NOT NULL, -- The species in MariaDB - InbredSetId TEXT NOT NULL, -- The traits group in MariaDB - ProbeFreezeId TEXT NOT NULL, -- The study ID in MariaDB - ProbeSetFreezeId TEXT NOT NULL, -- The dataset Id in MariaDB - dataset_name TEXT, -- dataset Name in MariaDB - dataset_fullname, -- dataset FullName in MariaDB - dataset_shortname, -- dataset ShortName in MariaDB - FOREIGN KEY (group_id) - REFERENCES groups(group_id) ON UPDATE CASCADE ON DELETE RESTRICT - UNIQUE (SpeciesId, InbredSetId, ProbeFreezeId, ProbeSetFreezeId) - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS linked_mrna_data") -] diff --git a/migrations/auth/20230410_02_WZqSf-create-mrna-resources-table.py b/migrations/auth/20230410_02_WZqSf-create-mrna-resources-table.py deleted file mode 100644 index 2ad1056..0000000 --- a/migrations/auth/20230410_02_WZqSf-create-mrna-resources-table.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Create mRNA resources table -""" - -from yoyo import step - -__depends__ = {'20230410_01_8mwaf-create-linked-mrna-data-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS mrna_resources - -- Link mRNA data to specific resource - ( - group_id TEXT NOT NULL, - resource_id TEXT NOT NULL, -- A resource can have multiple items - data_link_id TEXT NOT NULL, - PRIMARY KEY (resource_id, data_link_id), - UNIQUE (data_link_id) -- ensure data is linked to single resource - FOREIGN KEY (group_id, resource_id) - REFERENCES resources(group_id, resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (data_link_id) REFERENCES linked_mrna_data(data_link_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS mrna_resources") -] diff --git a/migrations/auth/20230907_01_pjnxz-refactor-add-resource-ownership-table.py b/migrations/auth/20230907_01_pjnxz-refactor-add-resource-ownership-table.py deleted file mode 100644 index 37fcfe7..0000000 --- a/migrations/auth/20230907_01_pjnxz-refactor-add-resource-ownership-table.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -refactor: add resource_ownership table -""" - -from yoyo import step - -__depends__ = {'20230410_02_WZqSf-create-mrna-resources-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS resource_ownership( - -- This table links resources to groups, where relevant - group_id TEXT NOT NULL, - resource_id TEXT NOT NULL, - PRIMARY KEY(group_id, resource_id), - FOREIGN KEY(group_id) - REFERENCES groups(group_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(resource_id) - REFERENCES resources(resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS resource_ownership"), - step(# Copy over data - """ - INSERT INTO resource_ownership - SELECT group_id, resource_id FROM resources - """ - ) -] diff --git a/migrations/auth/20230907_02_Enicg-refactor-add-system-and-group-resource-categories.py b/migrations/auth/20230907_02_Enicg-refactor-add-system-and-group-resource-categories.py deleted file mode 100644 index c4397c9..0000000 --- a/migrations/auth/20230907_02_Enicg-refactor-add-system-and-group-resource-categories.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -refactor: add 'system' and 'group' resource categories -""" - -from yoyo import step - -__depends__ = {'20230907_01_pjnxz-refactor-add-resource-ownership-table'} - -steps = [ - step( - """ - INSERT INTO resource_categories VALUES - ('aa3d787f-af6a-44fa-9b0b-c82d40e54ad2', - 'system', - 'The overall system.', - '{"default-access-level": "public-read"}'), - ('1e0f70ee-add5-4358-8c6c-43de77fa4cce', - 'group', - 'A group resource.', - '{}') - """, - """ - DELETE FROM resource_categories - WHERE resource_category_id IN ( - 'aa3d787f-af6a-44fa-9b0b-c82d40e54ad2', - '1e0f70ee-add5-4358-8c6c-43de77fa4cce' - ) - """) -] diff --git a/migrations/auth/20230907_03_BwAmf-refactor-drop-group-id-from-resources-table.py b/migrations/auth/20230907_03_BwAmf-refactor-drop-group-id-from-resources-table.py deleted file mode 100644 index 0f491c2..0000000 --- a/migrations/auth/20230907_03_BwAmf-refactor-drop-group-id-from-resources-table.py +++ /dev/null @@ -1,325 +0,0 @@ -""" -refactor: drop 'group_id' from 'resources' table. -""" - -import sqlite3 -from yoyo import step - -__depends__ = {'20230907_02_Enicg-refactor-add-system-and-group-resource-categories'} - -def drop_group_id_from_group_user_roles_on_resources(conn): - conn.execute( - "ALTER TABLE group_user_roles_on_resources " - "RENAME TO group_user_roles_on_resources_bkp") - conn.execute( - """ - CREATE TABLE 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 - """) - conn.execute( - "INSERT INTO group_user_roles_on_resources " - "(group_id, user_id, role_id, resource_id)" - "SELECT group_id, 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") - -def drop_group_id_from_mrna_resources(conn): - conn.execute("ALTER TABLE mrna_resources RENAME TO mrna_resources_bkp") - conn.execute( - """ - CREATE TABLE IF NOT EXISTS mrna_resources - -- Link mRNA data to specific resource - ( - resource_id TEXT NOT NULL, -- A resource can have multiple items - data_link_id TEXT NOT NULL, - PRIMARY KEY (resource_id, data_link_id), - UNIQUE (data_link_id) -- ensure data is linked to single resource - FOREIGN KEY (resource_id) - REFERENCES resources(resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (data_link_id) REFERENCES linked_mrna_data(data_link_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - conn.execute( - "INSERT INTO mrna_resources " - "SELECT resource_id, data_link_id FROM mrna_resources_bkp") - conn.execute("DROP TABLE IF EXISTS mrna_resources_bkp") - -def drop_group_id_from_genotype_resources(conn): - conn.execute( - "ALTER TABLE genotype_resources RENAME TO genotype_resources_bkp") - conn.execute( - """ - CREATE TABLE IF NOT EXISTS genotype_resources - -- Link genotype data to specific resource - ( - resource_id TEXT NOT NULL, -- A resource can have multiple items - data_link_id TEXT NOT NULL, - PRIMARY KEY (resource_id, data_link_id), - UNIQUE (data_link_id) -- ensure data is linked to single resource - FOREIGN KEY (resource_id) - REFERENCES resources(resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (data_link_id) - REFERENCES linked_genotype_data(data_link_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - conn.execute( - "INSERT INTO genotype_resources " - "SELECT resource_id, data_link_id FROM genotype_resources_bkp") - conn.execute("DROP TABLE IF EXISTS genotype_resources_bkp") - -def drop_group_id_from_phenotype_resources(conn): - conn.execute( - "ALTER TABLE phenotype_resources RENAME TO phenotype_resources_bkp") - conn.execute( - """ - CREATE TABLE IF NOT EXISTS phenotype_resources - -- Link phenotype data to specific resources - ( - resource_id TEXT NOT NULL, -- A resource can have multiple data items - data_link_id TEXT NOT NULL, - PRIMARY KEY(resource_id, data_link_id), - UNIQUE (data_link_id), -- ensure data is linked to only one resource - FOREIGN KEY (resource_id) - REFERENCES resources(resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (data_link_id) - REFERENCES linked_phenotype_data(data_link_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - conn.execute( - "INSERT INTO phenotype_resources " - "SELECT resource_id, data_link_id FROM phenotype_resources_bkp") - conn.execute("DROP TABLE IF EXISTS phenotype_resources_bkp") - -def drop_group_id_from_resources_table(conn): - conn.row_factory = sqlite3.Row - conn.execute("PRAGMA foreign_keys = OFF") - conn.execute( - """ - CREATE TABLE IF NOT EXISTS resources_new( - resource_id TEXT NOT NULL, - resource_name TEXT NOT NULL UNIQUE, - resource_category_id TEXT NOT NULL, - public INTEGER NOT NULL DEFAULT 0 CHECK (public=0 or public=1), - PRIMARY KEY(resource_id), - FOREIGN KEY(resource_category_id) - REFERENCES resource_categories(resource_category_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - conn.execute( - "INSERT INTO resources_new " - "SELECT resource_id, resource_name, resource_category_id, public " - "FROM resources") - conn.execute("DROP TABLE IF EXISTS resources") - conn.execute("ALTER TABLE resources_new RENAME TO resources") - - drop_group_id_from_mrna_resources(conn) - drop_group_id_from_genotype_resources(conn) - drop_group_id_from_phenotype_resources(conn) - drop_group_id_from_group_user_roles_on_resources(conn) - - conn.execute("PRAGMA foreign_key_check") - conn.execute("PRAGMA foreign_keys = ON") - -def restore_group_id_from_group_user_roles_on_resources(conn): - conn.execute( - "ALTER TABLE group_user_roles_on_resources " - "RENAME TO group_user_roles_on_resources_bkp") - conn.execute( - """ - CREATE TABLE 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 (group_id, resource_id) - REFERENCES resources(group_id, resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - conn.execute( - "INSERT INTO group_user_roles_on_resources " - "(group_id, user_id, role_id, resource_id)" - "SELECT group_id, 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") - -def restore_group_id_from_mrna_resources(conn, resource_group_map): - conn.execute("ALTER TABLE mrna_resources RENAME TO mrna_resources_bkp") - conn.execute( - """ - CREATE TABLE IF NOT EXISTS mrna_resources - -- Link mRNA data to specific resource - ( - group_id TEXT NOT NULL, - resource_id TEXT NOT NULL, -- A resource can have multiple items - data_link_id TEXT NOT NULL, - PRIMARY KEY (resource_id, data_link_id), - UNIQUE (data_link_id) -- ensure data is linked to single resource - FOREIGN KEY (group_id, resource_id) - REFERENCES resources(group_id, resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (data_link_id) REFERENCES linked_mrna_data(data_link_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - - cursor = conn.cursor() - cursor.execute("SELECT * FROM mrna_resources_bkp") - resources = tuple({ - "group_id": resource_group_map[row["resource_id"]], - **dict(row) - } for row in cursor.fetchall()) - cursor.executemany( - "INSERT INTO mrna_resources(group_id, resource_id, data_link_id) " - "VALUES(:group_id, :resource_id, :data_link_id)", - resources) - conn.execute("DROP TABLE IF EXISTS mrna_resources_bkp") - -def restore_group_id_from_genotype_resources(conn, resource_group_map): - conn.execute( - "ALTER TABLE genotype_resources RENAME TO genotype_resources_bkp") - conn.execute( - """ - CREATE TABLE IF NOT EXISTS genotype_resources - -- Link genotype data to specific resource - ( - group_id TEXT NOT NULL, - resource_id TEXT NOT NULL, -- A resource can have multiple items - data_link_id TEXT NOT NULL, - PRIMARY KEY (group_id, resource_id, data_link_id), - UNIQUE (data_link_id) -- ensure data is linked to single resource - FOREIGN KEY (group_id, resource_id) - REFERENCES resources(group_id, resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (data_link_id) - REFERENCES linked_genotype_data(data_link_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - - cursor = conn.cursor() - cursor.execute("SELECT * FROM genotype_resources_bkp") - resources = tuple({ - "group_id": resource_group_map[row["resource_id"]], - **dict(row) - } for row in cursor.fetchall()) - cursor.executemany( - "INSERT INTO genotype_resources(group_id, resource_id, data_link_id) " - "VALUES(:group_id, :resource_id, :data_link_id)", - resources) - conn.execute("DROP TABLE IF EXISTS genotype_resources_bkp") - -def restore_group_id_from_phenotype_resources(conn, resource_group_map): - conn.execute( - "ALTER TABLE phenotype_resources RENAME TO phenotype_resources_bkp") - conn.execute( - """ - CREATE TABLE IF NOT EXISTS phenotype_resources - -- Link phenotype data to specific resources - ( - group_id TEXT NOT NULL, - resource_id TEXT NOT NULL, -- A resource can have multiple data items - data_link_id TEXT NOT NULL, - PRIMARY KEY(group_id, resource_id, data_link_id), - UNIQUE (data_link_id), -- ensure data is linked to only one resource - FOREIGN KEY (group_id, resource_id) - REFERENCES resources(group_id, resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (data_link_id) - REFERENCES linked_phenotype_data(data_link_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - - cursor = conn.cursor() - cursor.execute("SELECT * FROM phenotype_resources_bkp") - resources = tuple({ - "group_id": resource_group_map[row["resource_id"]], - **dict(row) - } for row in cursor.fetchall()) - cursor.executemany( - "INSERT INTO phenotype_resources(group_id, resource_id, data_link_id) " - "VALUES(:group_id, :resource_id, :data_link_id)", - resources) - conn.execute("DROP TABLE IF EXISTS phenotype_resources_bkp") - -def restore_group_id_to_resources_table(conn): - conn.row_factory = sqlite3.Row - conn.execute("PRAGMA foreign_keys = OFF") - - cursor = conn.cursor() - cursor.execute("ALTER TABLE resources RENAME TO resources_bkp") - cursor.execute( - "SELECT r.*, ro.group_id FROM resources_bkp AS r " - "INNER JOIN resource_ownership AS ro " - "ON r.resource_id=ro.resource_id") - group_resources = tuple(dict(row) for row in cursor.fetchall()) - cursor.execute( - """ - CREATE TABLE IF NOT EXISTS resources( - group_id TEXT NOT NULL, - resource_id TEXT NOT NULL, - resource_name TEXT NOT NULL UNIQUE, - resource_category_id TEXT NOT NULL, - public INTEGER NOT NULL DEFAULT 0 CHECK (public=0 or public=1), - PRIMARY KEY(group_id, resource_id), - FOREIGN KEY(group_id) - REFERENCES groups(group_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(resource_category_id) - REFERENCES resource_categories(resource_category_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - cursor.executemany( - "INSERT INTO resources" - "(group_id, resource_id, resource_name, resource_category_id)" - "VALUES " - "(:group_id, :resource_id, :resource_name, :resource_category_id)", - group_resources) - cursor.execute("DROP TABLE IF EXISTS resources_bkp") - - resource_group_map = { - res["resource_id"]: res["group_id"] - for res in group_resources - } - restore_group_id_from_group_user_roles_on_resources(conn) - restore_group_id_from_mrna_resources(conn, resource_group_map) - restore_group_id_from_genotype_resources(conn, resource_group_map) - restore_group_id_from_phenotype_resources(conn, resource_group_map) - - conn.execute("PRAGMA foreign_key_check") - conn.execute("PRAGMA foreign_keys = ON") - -steps = [ - step( - drop_group_id_from_resources_table, restore_group_id_to_resources_table) -] diff --git a/migrations/auth/20230907_04_3LnrG-refactor-create-group-resources-table.py b/migrations/auth/20230907_04_3LnrG-refactor-create-group-resources-table.py deleted file mode 100644 index a26834a..0000000 --- a/migrations/auth/20230907_04_3LnrG-refactor-create-group-resources-table.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -refactor: create 'group_resources' table. -""" - -import uuid -import random -import string - -import sqlite3 -from yoyo import step - -__depends__ = {'20230907_03_BwAmf-refactor-drop-group-id-from-resources-table'} - -def randstr(length: int = 5): - """Generate random string.""" - return "".join(random.choices( - string.ascii_letters + string.digits, k=length)) - -def create_and_link_resources_for_existing_groups(conn): - conn.row_factory = sqlite3.Row - cursor = conn.cursor() - cursor.execute("SELECT group_id, group_name FROM groups") - resources = tuple({ - "group_id": row["group_id"], - "resource_id": str(uuid.uuid4()), - "resource_name": f"{randstr(10)}: {row['group_name']}", - "resource_category_id": "1e0f70ee-add5-4358-8c6c-43de77fa4cce" - } for row in cursor.fetchall()) - cursor.executemany( - "INSERT INTO " - "resources(resource_id, resource_name, resource_category_id) " - "VALUES (:resource_id, :resource_name, :resource_category_id)", - resources) - cursor.executemany( - "INSERT INTO group_resources(resource_id, group_id) " - "VALUES (:resource_id, :group_id)", - resources) - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS group_resources( - -- Links groups to the resources of type 'group' that control access to - -- each group - resource_id TEXT NOT NULL, - group_id TEXT NOT NULL, - PRIMARY KEY(resource_id, group_id), - FOREIGN KEY (resource_id) - REFERENCES resources(resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (group_id) - REFERENCES groups(group_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS group_resources"), - step(create_and_link_resources_for_existing_groups) -] diff --git a/migrations/auth/20230912_01_BxrhE-add-system-resource.py b/migrations/auth/20230912_01_BxrhE-add-system-resource.py deleted file mode 100644 index 66c6461..0000000 --- a/migrations/auth/20230912_01_BxrhE-add-system-resource.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Add 'system' resource. -""" - -import uuid - -import sqlite3 -from yoyo import step - -__depends__ = {'20230907_04_3LnrG-refactor-create-group-resources-table'} - -def add_system_resource(conn): - """Add a system resource.""" - conn.row_factory = sqlite3.Row - cursor = conn.cursor() - cursor.execute( - "SELECT resource_category_id FROM resource_categories " - "WHERE resource_category_key='system'") - category_id = cursor.fetchone()["resource_category_id"] - cursor.execute( - "INSERT INTO " - "resources(resource_id, resource_name, resource_category_id, public) " - "VALUES(?, ?, ?, ?)", - (str(uuid.uuid4()), "GeneNetwork System", category_id, "1")) - -def delete_system_resource(conn): - """Add a system resource.""" - conn.row_factory = sqlite3.Row - cursor = conn.cursor() - cursor.execute( - "SELECT resource_category_id FROM resource_categories " - "WHERE resource_category_key='system'") - category_id = cursor.fetchone()["resource_category_id"] - cursor.execute("DELETE FROM resources WHERE resource_category_id = ?", - (category_id,)) - -steps = [ - step(add_system_resource, delete_system_resource) -] 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 deleted file mode 100644 index 1b3f0b1..0000000 --- a/migrations/auth/20230912_02_hFmSn-drop-group-id-and-fix-foreign-key-references-on-group-user-roles-on-resources-table.py +++ /dev/null @@ -1,227 +0,0 @@ -""" -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) -] - diff --git a/migrations/auth/20230925_01_TWJuR-add-new-public-view-role.py b/migrations/auth/20230925_01_TWJuR-add-new-public-view-role.py deleted file mode 100644 index 1172034..0000000 --- a/migrations/auth/20230925_01_TWJuR-add-new-public-view-role.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Add new "public-view" role -""" - -import sqlite3 - -from yoyo import step - -__depends__ = {'20230912_02_hFmSn-drop-group-id-and-fix-foreign-key-references-on-group-user-roles-on-resources-table'} - -def grant_to_all_users_public_view_role(conn): - """Grant the `public-view` role to all existing users.""" - conn.row_factory = sqlite3.Row - conn.execute("PRAGMA foreign_keys = ON") - cursor = conn.cursor() - cursor.execute("SELECT user_id FROM users") - user_ids = tuple(row["user_id"] for row in cursor.fetchall()) - - cursor.execute("SELECT resource_id FROM resources WHERE public=1") - resource_ids = tuple(row["resource_id"] for row in cursor.fetchall()) - - params = tuple({ - "user_id": user_id, - "resource_id": resource_id, - "role_id": "fd88bfed-d869-4969-87f2-67c4e8446ecb" - } for user_id in user_ids for resource_id in resource_ids) - cursor.executemany( - "INSERT INTO user_roles(user_id, role_id, resource_id) " - "VALUES (:user_id, :role_id, :resource_id) ", - params) - -def revoke_from_all_users_public_view_role(conn): - """Revoke the `public-view` role from all existing users.""" - conn.execute("PRAGMA foreign_keys = ON") - conn.execute( - "DELETE FROM user_roles " - "WHERE role_id='fd88bfed-d869-4969-87f2-67c4e8446ecb'") - -steps = [ - step( - """ - INSERT INTO roles(role_id, role_name, user_editable) - VALUES('fd88bfed-d869-4969-87f2-67c4e8446ecb', 'public-view', 0) - """, - """ - DELETE FROM roles WHERE role_id='fd88bfed-d869-4969-87f2-67c4e8446ecb' - """), - step( - """ - INSERT INTO role_privileges(role_id, privilege_id) - VALUES( - 'fd88bfed-d869-4969-87f2-67c4e8446ecb', - 'group:resource:view-resource') - """, - """ - DELETE FROM role_privileges - WHERE role_id='fd88bfed-d869-4969-87f2-67c4e8446ecb' - """), - step(grant_to_all_users_public_view_role, - revoke_from_all_users_public_view_role) -] diff --git a/migrations/auth/20231002_01_tzxTf-link-inbredsets-to-auth-system.py b/migrations/auth/20231002_01_tzxTf-link-inbredsets-to-auth-system.py deleted file mode 100644 index 402e9a5..0000000 --- a/migrations/auth/20231002_01_tzxTf-link-inbredsets-to-auth-system.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -link InbredSets to auth system -""" - -from yoyo import step - -__depends__ = {'20230925_01_TWJuR-add-new-public-view-role', '__init__'} - -steps = [ - step( - """ - INSERT INTO resource_categories - ( - resource_category_id, - resource_category_key, - resource_category_description, - resource_meta - ) - VALUES - ( - 'b3654600-4ab0-4745-8292-5849b34173a7', - 'inbredset-group', - 'A resource that controls access to a particular InbredSet group', - '{"default-access-level":"public-read"}' - ) - """, - """ - DELETE FROM resource_categories WHERE - resource_category_id = 'b3654600-4ab0-4745-8292-5849b34173a7' - """ - ), - step( - """ - CREATE TABLE IF NOT EXISTS linked_inbredset_groups - -- Link InbredSet groups in MariaDB to auth system - ( - data_link_id TEXT NOT NULL PRIMARY KEY, -- A new ID for the auth system - SpeciesId TEXT NOT NULL, -- Species ID in MariaDB - InbredSetId TEXT NOT NULL, -- The InbredSet ID in MariaDB - InbredSetName TEXT NOT NULL, -- The InbredSet group's name in MariaDB - InbredSetFullName TEXT NOT NULL, -- The InbredSet group's full name in MariaDB - UNIQUE(SpeciesId, InbredSetId) - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS linked_inbredset_groups"), - step( - """ - CREATE TABLE IF NOT EXISTS inbredset_group_resources - -- Link the InbredSet data to a specific resource - ( - resource_id TEXT NOT NULL, -- Linked resource: one-to-one - data_link_id TEXT NOT NULL, - PRIMARY KEY(resource_id, data_link_id), - UNIQUE(resource_id), -- resource is linked to only one InbredSet - UNIQUE(data_link_id), -- InbredSet is linked to only one resource - FOREIGN KEY(resource_id) - REFERENCES resources(resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(data_link_id) - REFERENCES linked_inbredset_groups(data_link_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS inbredset_group_resources"), - step( - """ - INSERT INTO privileges(privilege_id, privilege_description) VALUES - ('system:inbredset:create-case-attribute', 'Create a new case attribute for an InbredSet group.'), - ('system:inbredset:delete-case-attribute', 'Delete an existing case-attribute from an InbredSet group'), - ('system:inbredset:edit-case-attribute', 'Edit the values of case-attributes of an InbredSet group'), - ('system:inbredset:view-case-attribute', 'View the case-attributes of an InbredSet group'), - ('system:inbredset:apply-case-attribute-edit', 'Apply an edit to case-attributes performed by another user for an InbredSet group'), - ('system:inbredset:reject-case-attribute-edit', 'Reject an edit to case-attributes performed by another user for an InbredSet group') - """, - """ - DELETE FROM privileges WHERE privilege_id IN ( - 'system:inbredset:create-case-attribute', - 'system:inbredset:delete-case-attribute', - 'system:inbredset:edit-case-attribute', - 'system:inbredset:view-case-attribute', - 'system:inbredset:apply-case-attribute-edit', - 'system:inbredset:reject-case-attribute-edit') - """) -] diff --git a/migrations/auth/20231011_01_CS8NZ-create-new-inbredset-group-owner-role.py b/migrations/auth/20231011_01_CS8NZ-create-new-inbredset-group-owner-role.py deleted file mode 100644 index a4238ed..0000000 --- a/migrations/auth/20231011_01_CS8NZ-create-new-inbredset-group-owner-role.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Create new 'inbredset-group-owner' role -""" - -from yoyo import step - -__depends__ = {'20231002_01_tzxTf-link-inbredsets-to-auth-system'} - -steps = [ - step( - """ - INSERT INTO roles(role_id, role_name, user_editable) - VALUES('bde1c08b-b067-4d56-8353-462fc5928c32', 'inbredset-group-owner', 0) - """, - """ - DELETE FROM roles WHERE role_id='bde1c08b-b067-4d56-8353-462fc5928c32' - """), - step( - """ - INSERT INTO role_privileges(role_id, privilege_id) - VALUES - ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:apply-case-attribute-edit'), - ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:create-case-attribute'), - ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:delete-case-attribute'), - ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:edit-case-attribute'), - ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:reject-case-attribute-edit'), - ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:view-case-attribute') - """, - """ - DELETE FROM role_privileges - WHERE (role_id, privilege_id) - IN - (('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:apply-case-attribute-edit'), - ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:create-case-attribute'), - ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:delete-case-attribute'), - ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:edit-case-attribute'), - ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:reject-case-attribute-edit'), - ('bde1c08b-b067-4d56-8353-462fc5928c32', 'system:inbredset:view-case-attribute')) - """) -] diff --git a/migrations/auth/20240506_01_798tW-create-jwt-refresh-tokens-table.py b/migrations/auth/20240506_01_798tW-create-jwt-refresh-tokens-table.py deleted file mode 100644 index 049ac6b..0000000 --- a/migrations/auth/20240506_01_798tW-create-jwt-refresh-tokens-table.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Create jwt_refresh_tokens table -""" - -from yoyo import step - -__depends__ = {'20231011_01_CS8NZ-create-new-inbredset-group-owner-role'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS jwt_refresh_tokens - -- Store refresh tokens to verify refresh attempts - ( - token TEXT NOT NULL, - client_id TEXT NOT NULL, - user_id TEXT NOT NULL, - issued_with TEXT NOT NULL UNIQUE, -- JWT ID of JWT issued along with this refresh token - issued_at INTEGER NOT NULL, - expires INTEGER NOT NULL, - scope TEXT NOT NULL, - revoked INTEGER CHECK (revoked = 0 or revoked = 1), - parent_of TEXT UNIQUE, - PRIMARY KEY(token), - FOREIGN KEY (client_id) REFERENCES oauth2_clients(client_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (user_id) REFERENCES users(user_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY (parent_of) REFERENCES jwt_refresh_tokens(token) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS jwt_refresh_tokens") -] diff --git a/migrations/auth/20240529_01_ALNWj-update-schema-for-user-verification.py b/migrations/auth/20240529_01_ALNWj-update-schema-for-user-verification.py deleted file mode 100644 index 0cab1c3..0000000 --- a/migrations/auth/20240529_01_ALNWj-update-schema-for-user-verification.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -update schema for user-verification -""" - -from yoyo import step - -__depends__ = {'20240506_01_798tW-create-jwt-refresh-tokens-table'} - -def add_verification_cols_to_users_table(conn): - "add verification columns to users table"; - conn.execute("PRAGMA foreign_keys = OFF") - - conn.execute( - """ - CREATE TABLE users_new( - user_id TEXT PRIMARY KEY NOT NULL, - email TEXT UNIQUE NOT NULL, - name TEXT, - created INTEGER NOT NULL DEFAULT (unixepoch()), - verified INTEGER NOT NULL DEFAULT 0 CHECK (verified=0 or verified=1) - ) WITHOUT ROWID - """) - conn.execute( - """ - INSERT INTO users_new(user_id, email, name) - SELECT user_id, email, name FROM users - """) - # the original table `users` has dependents, so we cannot simply do a - # `ALTER TABLE … RENAME TO …` since according to - # https://sqlite.org/lang_altertable.html#alter_table_rename - # from versions 3.26.0 onward, the foreign key references are **ALWAYS** - # changed. In this case, we create the new table first, do data transfers, - # drop the original and rename the new table to the same name as the - # original. - conn.execute("DROP TABLE IF EXISTS users") - conn.execute("ALTER TABLE users_new RENAME TO users") - - - print("turning foreign keys should back on.") - conn.execute("PRAGMA foreign_key_check") - conn.execute("PRAGMA foreign_keys = ON") - -def drop_verification_cols_from_users_table(conn): - "Drop verification columns from users table" - conn.execute("ALTER TABLE users DROP COLUMN created") - conn.execute("ALTER TABLE users DROP COLUMN verified") - -steps = [ - step(add_verification_cols_to_users_table, - drop_verification_cols_from_users_table), - step( - """ - CREATE TABLE IF NOT EXISTS user_verification_codes( - user_id TEXT NOT NULL, - code TEXT NOT NULL, - generated INTEGER NOT NULL, - expires INTEGER NOT NULL, - PRIMARY KEY(user_id), - FOREIGN KEY(user_id) REFERENCES users(user_id) - ON UPDATE CASCADE ON DELETE CASCADE - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS verification_codes") -] diff --git a/migrations/auth/20240606_01_xQDwL-move-role-manipulation-privileges-from-group-to-resources.py b/migrations/auth/20240606_01_xQDwL-move-role-manipulation-privileges-from-group-to-resources.py deleted file mode 100644 index a45fd30..0000000 --- a/migrations/auth/20240606_01_xQDwL-move-role-manipulation-privileges-from-group-to-resources.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -Move role-manipulation privileges from group to resources -""" -import sqlite3 -from yoyo import step - -__depends__ = {'20240529_01_ALNWj-update-schema-for-user-verification'} - -def role_by_name(cursor, role_name): - """Fetch group-admin role""" - cursor.execute("SELECT * FROM roles WHERE role_name=?", - (role_name,)) - return dict(cursor.fetchone()) - - -def move_privileges_to_resources(conn): - """Move role-manipulation privileges from group to resource.""" - conn.row_factory = sqlite3.Row - cursor = conn.cursor() - cursor.execute( - "DELETE FROM role_privileges WHERE privilege_id IN (" - " 'group:role:create-role'," - " 'group:role:delete-role'," - " 'group:role:edit-role'," - " 'group:user:assign-role'" - ")") - cursor.execute( - "DELETE FROM privileges WHERE privilege_id IN (" - " 'group:role:create-role'," - " 'group:role:delete-role'," - " 'group:role:edit-role'," - " 'group:user:assign-role'" - ")") - - resource_owner_role = role_by_name(cursor, "resource-owner") - privileges = ( - ("resource:role:create-role", - "Create a new role on a specific resource"), - ("resource:role:delete-role", - "Delete an existing role from a specific resource"), - ("resource:role:edit-role", - "Edit an existing role on a specific resource"), - ("resource:user:assign-role", - "Assign a user to a role on a specific resource")) - cursor.executemany( - ("INSERT INTO privileges(privilege_id, privilege_description) " - "VALUES (?, ?)"), - privileges) - cursor.executemany( - ("INSERT INTO role_privileges(role_id, privilege_id) " - "VALUES(?, ?)"), - tuple((resource_owner_role["role_id"], privilege[0]) - for privilege in privileges)) - cursor.close() - -def move_privileges_to_groups(conn): - """Move role-manipulation privileges from resource to group.""" - conn.row_factory = sqlite3.Row - cursor = conn.cursor() - cursor.execute( - "DELETE FROM role_privileges WHERE privilege_id IN (" - " 'resource:role:create-role'," - " 'resource:role:delete-role'," - " 'resource:role:edit-role'," - " 'resource:user:assign-role'" - ")") - cursor.execute( - "DELETE FROM privileges WHERE privilege_id IN (" - " 'resource:role:create-role'," - " 'resource:role:delete-role'," - " 'resource:role:edit-role'," - " 'resource:user:assign-role'" - ")") - - group_leader_role = role_by_name(cursor, "group-leader") - privileges = ( - ("group:role:create-role", "Create a new role"), - ("group:role:delete-role", "Delete an existing role"), - ("group:role:edit-role", "edit/update an existing role"), - ("group:user:assign-role", "Assign a role to an existing user")) - cursor.executemany( - ("INSERT INTO privileges(privilege_id, privilege_description) " - "VALUES (?, ?)"), - privileges) - cursor.executemany( - ("INSERT INTO role_privileges(role_id, privilege_id) " - "VALUES(?, ?)"), - tuple((group_leader_role["role_id"], privilege[0]) - for privilege in privileges)) - cursor.close() - -steps = [ - step(move_privileges_to_resources, move_privileges_to_groups) -] diff --git a/migrations/auth/20240606_02_ubZri-create-resource-roles-table.py b/migrations/auth/20240606_02_ubZri-create-resource-roles-table.py deleted file mode 100644 index 0695c0e..0000000 --- a/migrations/auth/20240606_02_ubZri-create-resource-roles-table.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -Create 'resource_roles' table. -""" - -from yoyo import step - -__depends__ = {'20240606_01_xQDwL-move-role-manipulation-privileges-from-group-to-resources'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS resource_roles( - resource_id TEXT NOT NULL, - role_created_by TEXT NOT NULL, - role_id TEXT NOT NULL, - PRIMARY KEY (resource_id, role_created_by, role_id), - FOREIGN KEY(resource_id) REFERENCES resources(resource_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(role_created_by) 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 - """, - "DROP TABLE IF EXISTS resource_roles"), - step( - """ - CREATE INDEX IF NOT EXISTS - tbl_resource_roles_cols_resource_id_role_created_by - ON resource_roles(resource_id, role_created_by) - """, - """ - DROP INDEX IF EXISTS - tbl_resource_roles_cols_resource_id_role_created_by - """) -] diff --git a/migrations/auth/20240606_03_BY7Us-drop-group-roles-table.py b/migrations/auth/20240606_03_BY7Us-drop-group-roles-table.py deleted file mode 100644 index 45d689c..0000000 --- a/migrations/auth/20240606_03_BY7Us-drop-group-roles-table.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Drop 'group_roles' table. -""" -import sqlite3 -from yoyo import step - -__depends__ = {'20240606_02_ubZri-create-resource-roles-table'} - -def restore_group_roles(conn): - """Restore the `group_roles` table.""" - conn.row_factory = sqlite3.Row - cursor = conn.cursor() - cursor.execute( - """ - CREATE TABLE group_roles( - group_role_id TEXT PRIMARY KEY, - group_id TEXT NOT NULL, - role_id TEXT NOT NULL, - UNIQUE (group_id, role_id), - FOREIGN KEY(group_id) REFERENCES groups(group_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(role_id) REFERENCES roles(role_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - cursor.execute( - """ - CREATE INDEX idx_tbl_group_roles_cols_group_id - ON group_roles(group_id) - """) - cursor.close() - -steps = [ - step("DROP TABLE IF EXISTS group_roles", restore_group_roles) -] diff --git a/migrations/auth/20240819_01_p2vXR-create-forgot-password-tokens-table.py b/migrations/auth/20240819_01_p2vXR-create-forgot-password-tokens-table.py deleted file mode 100644 index 44318bd..0000000 --- a/migrations/auth/20240819_01_p2vXR-create-forgot-password-tokens-table.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Create forgot_password_tokens table - -This will be used to enable users to validate/verify their password change -requests. -""" - -from yoyo import step - -__depends__ = {'20240606_03_BY7Us-drop-group-roles-table'} - -steps = [ - step( - """ - CREATE TABLE IF NOT EXISTS forgot_password_tokens( - user_id TEXT NOT NULL, - token TEXT NOT NULL, - generated INTEGER NOT NULL, - expires INTEGER NOT NULL, - PRIMARY KEY(user_id), - FOREIGN KEY(user_id) REFERENCES users(user_id) - ON UPDATE CASCADE ON DELETE CASCADE - ) WITHOUT ROWID - """, - "DROP TABLE IF EXISTS forgot_password_tokens") -] diff --git a/migrations/auth/20240924_01_thbvh-hooks-for-edu-domains.py b/migrations/auth/20240924_01_thbvh-hooks-for-edu-domains.py deleted file mode 100644 index 5c6e81d..0000000 --- a/migrations/auth/20240924_01_thbvh-hooks-for-edu-domains.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -hooks_for_edu_domains -""" - -from yoyo import step - -__depends__ = {'20240819_01_p2vXR-create-forgot-password-tokens-table'} - -steps = [ - step( - """ - INSERT INTO roles(role_id, role_name, user_editable) VALUES - ('9bb203a2-7897-4fe3-ac4a-75e6a4f96f5d', 'hook-role-from-edu-domain', '0') - """, - "DELETE FROM roles WHERE role_name='hook-role-from-edu-domain'"), - step( - """ - INSERT INTO role_privileges(role_id, privilege_id) VALUES - ('9bb203a2-7897-4fe3-ac4a-75e6a4f96f5d', 'group:resource:view-resource'), - ('9bb203a2-7897-4fe3-ac4a-75e6a4f96f5d', 'group:resource:edit-resource') - """, - "DELETE FROM role_privileges WHERE role_id='9bb203a2-7897-4fe3-ac4a-75e6a4f96f5d'" - ) -] diff --git a/migrations/auth/20250328_01_72EFk-add-admin-ui-privilege-to-system-administrator-role.py b/migrations/auth/20250328_01_72EFk-add-admin-ui-privilege-to-system-administrator-role.py deleted file mode 100644 index d22ad01..0000000 --- a/migrations/auth/20250328_01_72EFk-add-admin-ui-privilege-to-system-administrator-role.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -add admin ui privilege to system-administrator role -""" -import contextlib - -from yoyo import step - -__depends__ = {'20240924_01_thbvh-hooks-for-edu-domains'} - -def get_system_admin_id(cursor): - cursor.execute( - "SELECT role_id FROM roles WHERE role_name='system-administrator'") - return cursor.fetchone()[0] - -def add_admin_ui_privilege(conn): - with contextlib.closing(conn.cursor()) as cursor: - # Create admin-ui privilege - cursor.execute( - "INSERT INTO privileges (privilege_id, privilege_description) " - "VALUES(?, ?)", - ("system:user:admin-ui", "View UI elements that should only be visible to system administrators")) - - # Add UI privilege to system-administrator role - cursor.execute( - "INSERT INTO role_privileges (role_id, privilege_id) " - "VALUES(?, ?)", - (get_system_admin_id(cursor), "system:user:admin-ui") - ) - -def remove_admin_ui_privilege(conn): - with contextlib.closing(conn.cursor()) as cursor: - # Remove UI privilege from system-administrator role - cursor.execute( - "DELETE FROM role_privileges WHERE privilege_id='system:user:admin-ui'") - - # Remove UI privilege from privileges table - cursor.execute( - "DELETE FROM privileges WHERE privilege_id='system:user:admin-ui'") - -steps = [ - step(add_admin_ui_privilege, remove_admin_ui_privilege) -] diff --git a/migrations/auth/20250609_01_LB60X-add-batch-edit-privileges.py b/migrations/auth/20250609_01_LB60X-add-batch-edit-privileges.py deleted file mode 100644 index 73a4880..0000000 --- a/migrations/auth/20250609_01_LB60X-add-batch-edit-privileges.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Add Batch Edit privileges -""" - -import contextlib - -from yoyo import step - -__depends__ = {'20250328_01_72EFk-add-admin-ui-privilege-to-system-administrator-role'} - -def add_batch_edit_privilege_and_role(conn): - with contextlib.closing(conn.cursor()) as cursor: - # Create batch edit privilege - cursor.execute( - "INSERT INTO privileges (privilege_id, privilege_description) " - "VALUES(?, ?)", - ("system:data:batch-edit", "Batch Edit")) - - # Create batch editor role - cursor.execute( - "INSERT INTO roles (role_id, role_name, user_editable) " - "VALUES(?, ?, ?)", - ("0f391910-5225-476a-bb8d-9c0adc9d81cc", "Batch Editors", 0)) - - # Link role/privilege - cursor.execute( - "INSERT INTO role_privileges (role_id, privilege_id) " - "VALUES(?, ?)", - ("0f391910-5225-476a-bb8d-9c0adc9d81cc", "system:data:batch-edit") - ) - -def remove_batch_edit_privilege_and_role(conn): - with contextlib.closing(conn.cursor()) as cursor: - # Remove batch edit role/privilege link - cursor.execute( - "DELETE FROM role_privileges WHERE privilege_id='system:data:batch-edit'") - - # Remove Batch Editor role - cursor.execute( - "DELETE FROM roles WHERE role_id='0f391910-5225-476a-bb8d-9c0adc9d81cc'") - - # Remove Batch Edit privilege - cursor.execute( - "DELETE FROM privileges WHERE privilege_id='system:data:batch-edit'") - - -steps = [ - step(add_batch_edit_privilege_and_role, remove_batch_edit_privilege_and_role) -] diff --git a/migrations/auth/20250609_01_bj9Pl-add-new-group-data-link-to-group-privilege.py b/migrations/auth/20250609_01_bj9Pl-add-new-group-data-link-to-group-privilege.py deleted file mode 100644 index 3b9e928..0000000 --- a/migrations/auth/20250609_01_bj9Pl-add-new-group-data-link-to-group-privilege.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Add new 'group:data:link-to-group' privilege. -""" - -from yoyo import step - -__depends__ = {'20240924_01_thbvh-hooks-for-edu-domains'} - -steps = [ - step( - """ - INSERT INTO privileges(privilege_id, privilege_description) - VALUES( - 'group:data:link-to-group', - 'Allow linking data to only one specific group.' - ) - """, - "DELETE FROM privileges WHERE privilege_id='group:data:link-to-group'") -] diff --git a/migrations/auth/20250609_02_9UBPl-assign-group-data-link-to-group-privilege-to-group-leader.py b/migrations/auth/20250609_02_9UBPl-assign-group-data-link-to-group-privilege-to-group-leader.py deleted file mode 100644 index 5d9c306..0000000 --- a/migrations/auth/20250609_02_9UBPl-assign-group-data-link-to-group-privilege-to-group-leader.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -Assign 'group:data:link-to-group' privilege to group leader. -""" - -from yoyo import step - -__depends__ = {'20250609_01_bj9Pl-add-new-group-data-link-to-group-privilege'} - -steps = [ - step( - """ - INSERT INTO role_privileges(role_id, privilege_id) - VALUES( - 'a0e67630-d502-4b9f-b23f-6805d0f30e30', - 'group:data:link-to-group' - ) - """, - """ - DELETE FROM role_privileges - WHERE role_id='a0e67630-d502-4b9f-b23f-6805d0f30e30' - AND privilege_id='group:data:link-to-group' - """) -] diff --git a/migrations/auth/20250703_01_aDVwP-add-role-management-privileges-to-group-leader-role.py b/migrations/auth/20250703_01_aDVwP-add-role-management-privileges-to-group-leader-role.py deleted file mode 100644 index 6335152..0000000 --- a/migrations/auth/20250703_01_aDVwP-add-role-management-privileges-to-group-leader-role.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -Add role management privileges to group-leader role -""" - -from yoyo import step - -__depends__ = {'20250609_01_LB60X-add-batch-edit-privileges', '20250609_02_9UBPl-assign-group-data-link-to-group-privilege-to-group-leader'} - -steps = [ - step( - """ - INSERT INTO role_privileges(role_id, privilege_id) - VALUES - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', 'resource:role:create-role'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', 'resource:role:delete-role'), - ('a0e67630-d502-4b9f-b23f-6805d0f30e30', 'resource:role:edit-role') - """, - """ - DELETE FROM role_privileges - WHERE role_id='a0e67630-d502-4b9f-b23f-6805d0f30e30' - AND privilege_id IN ( - 'resource:role:create-role', - 'resource:role:delete-role', - 'resource:role:edit-role' - ) - """) -] diff --git a/migrations/auth/20250722_01_7Gro7-create-new-system-user-edit-privilege.py b/migrations/auth/20250722_01_7Gro7-create-new-system-user-edit-privilege.py deleted file mode 100644 index f00ab11..0000000 --- a/migrations/auth/20250722_01_7Gro7-create-new-system-user-edit-privilege.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Create new 'system:user:edit' privilege. -""" - -from yoyo import step - -__depends__ = {'20250703_01_aDVwP-add-role-management-privileges-to-group-leader-role'} - -steps = [ - step( - """ - INSERT INTO privileges(privilege_id, privilege_description) - VALUES( - 'system:user:edit', - 'Allow general user-information edit.') - """, - "DELETE FROM privileges WHERE privilege_id='system:user:edit'") -] diff --git a/migrations/auth/20250722_02_M8TXv-add-system-user-edit-privilege-to-system-admin-role.py b/migrations/auth/20250722_02_M8TXv-add-system-user-edit-privilege-to-system-admin-role.py deleted file mode 100644 index b956bef..0000000 --- a/migrations/auth/20250722_02_M8TXv-add-system-user-edit-privilege-to-system-admin-role.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -Add 'system:user:edit' privilege to 'system-admin' role. -""" -import contextlib - -from yoyo import step - -__depends__ = {'20250722_01_7Gro7-create-new-system-user-edit-privilege'} - - -def system_administrator_role_id(cursor): - """Fetch ID for role 'system-administrator'.""" - cursor.execute( - "SELECT role_id FROM roles WHERE role_name='system-administrator'") - return cursor.fetchone()[0] - - -def add_system_user_edit_privilege(conn): - """Add the 'system:user:edit' to the 'system-administrator' role.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "INSERT INTO role_privileges(role_id, privilege_id) " - "VALUES(?, ?)", - (system_administrator_role_id(cursor), 'system:user:edit')) - - -def remove_system_user_edit_privilege(conn): - """Remove the 'system:user:edit' from the 'system-administrator' role.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "DELETE FROM role_privileges WHERE role_id=? AND privilege_id=?", - (system_administrator_role_id(cursor), 'system:user:edit')) - -steps = [ - step(add_system_user_edit_privilege, remove_system_user_edit_privilege) -] diff --git a/migrations/auth/20250729_01_CNn2p-create-initial-system-wide-resources-access-privileges.py b/migrations/auth/20250729_01_CNn2p-create-initial-system-wide-resources-access-privileges.py deleted file mode 100644 index be0d022..0000000 --- a/migrations/auth/20250729_01_CNn2p-create-initial-system-wide-resources-access-privileges.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -Create initial system-wide resources access privileges -""" - -from yoyo import step - -__depends__ = {'20250722_02_M8TXv-add-system-user-edit-privilege-to-system-admin-role'} - -steps = [ - step( - """ - INSERT INTO privileges(privilege_id, privilege_description) - VALUES - ("system:resource:view", - "View the wrapper resource object (not attached data). This is mostly for administration purposes."), - ("system:resource:edit", - "Edit/update the wrapper resource object (not attached data). This is mostly for administration purposes."), - ("system:resource:delete", - "Delete the wrapper resource object (not attached data). This is mostly for administration purposes."), - ("system:resource:reassign-group", - "Reassign the resource, and its data, to a different user group."), - ("system:resource:assign-owner", - "Assign ownership of any resource to any user.") - """, - """ - DELETE FROM privileges WHERE privilege_id IN - ("system:resource:view", "system:resource:edit", - "system:resource:delete", "system:resource:reassign-group", - "system:resource:assign-owner") - """) -] diff --git a/migrations/auth/20250729_02_7ycSm-assign-initial-system-wide-resources-access-privileges-to-sys-admins.py b/migrations/auth/20250729_02_7ycSm-assign-initial-system-wide-resources-access-privileges-to-sys-admins.py deleted file mode 100644 index e79ab1c..0000000 --- a/migrations/auth/20250729_02_7ycSm-assign-initial-system-wide-resources-access-privileges-to-sys-admins.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -Assign initial system-wide resources-access privileges to sys-admins. -""" -import contextlib - -from yoyo import step - -def system_administrator_role_id(cursor): - """Fetch ID for role 'system-administrator'.""" - cursor.execute( - "SELECT role_id FROM roles WHERE role_name='system-administrator'") - return cursor.fetchone()[0] - - -def assign_system_wide_resource_access_to_sysadmin(conn): - """ - Assign initial system-wide resources-access privileges to - `system-administrator` role. - """ - with contextlib.closing(conn.cursor()) as cursor: - sysadmin_role_id = system_administrator_role_id(cursor) - cursor.executemany( - "INSERT INTO role_privileges(role_id, privilege_id) " - "VALUES(?, ?)", - ((sysadmin_role_id, "system:resource:view"), - (sysadmin_role_id, "system:resource:edit"), - (sysadmin_role_id, "system:resource:delete"), - (sysadmin_role_id, "system:resource:reassign-group"), - (sysadmin_role_id, "system:resource:assign-owner"))) - - -def revoke_system_wide_resource_access_from_sysadmin(conn): - """ - Revoke initial system-wide resources-access privileges from - `system-administrator` role. - """ - with contextlib.closing(conn.cursor()) as cursor: - sysadmin_role_id = system_administrator_role_id(cursor) - cursor.executemany( - "DELETE FROM role_privileges " - "WHERE role_id=? AND privilege_id=?", - ((sysadmin_role_id, "system:resource:view"), - (sysadmin_role_id, "system:resource:edit"), - (sysadmin_role_id, "system:resource:delete"), - (sysadmin_role_id, "system:resource:reassign-group"), - (sysadmin_role_id, "system:resource:assign-owner"))) - -__depends__ = {'20250729_01_CNn2p-create-initial-system-wide-resources-access-privileges'} - -steps = [ - step(assign_system_wide_resource_access_to_sysadmin, - revoke_system_wide_resource_access_from_sysadmin) -] diff --git a/migrations/auth/20250729_03_oCvvq-grant-role-to-all-resources-to-sys-admin-users.py b/migrations/auth/20250729_03_oCvvq-grant-role-to-all-resources-to-sys-admin-users.py deleted file mode 100644 index e3bdc8f..0000000 --- a/migrations/auth/20250729_03_oCvvq-grant-role-to-all-resources-to-sys-admin-users.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Grant role to ALL resources to sys-admin users. -""" -import itertools -import contextlib - -from yoyo import step - -__depends__ = {'20250729_02_7ycSm-assign-initial-system-wide-resources-access-privileges-to-sys-admins'} - - -def system_administrator_role_id(cursor): - """Fetch ID for role 'system-administrator'.""" - cursor.execute( - "SELECT role_id FROM roles WHERE role_name='system-administrator'") - return cursor.fetchone()[0] - - -def system_resource_id(cursor): - cursor.execute( - "SELECT resources.resource_id FROM resource_categories " - "INNER JOIN resources ON resource_categories.resource_category_id=resources.resource_category_id " - "WHERE resource_category_key = 'system'") - return cursor.fetchone()[0] - - -def fetch_ids_for_sysadmin_users(cursor): - """Fetch all sysadmin users' IDs.""" - cursor.execute( - "SELECT user_roles.user_id FROM roles INNER JOIN user_roles " - "ON roles.role_id=user_roles.role_id " - "WHERE role_name='system-administrator' AND resource_id=?", - (system_resource_id(cursor),)) - return tuple(row[0] for row in cursor.fetchall()) - - -def fetch_non_system_resources(cursor): - """Fetch IDs for all resources that are not of the 'system' category.""" - cursor.execute( - "SELECT resources.resource_id FROM resource_categories " - "INNER JOIN resources " - "ON resource_categories.resource_category_id=resources.resource_category_id " - "WHERE resource_category_key != 'system'") - return tuple(row[0] for row in cursor.fetchall()) - - -def assign_sysadmin_role_on_non_system_resources(conn): - """Assign sysadmins the sysadmin role on all non-system resources.""" - with contextlib.closing(conn.cursor()) as cursor: - sysadminroleid = system_administrator_role_id(cursor) - cursor.executemany( - "INSERT INTO user_roles(user_id, resource_id, role_id) " - "VALUES (?, ?, ?)", - tuple(item + (sysadminroleid,) - for item in itertools.product( - fetch_ids_for_sysadmin_users(cursor), - fetch_non_system_resources(cursor)))) - - -def revoke_sysadmin_role_on_non_system_resources(conn): - """Revoke sysadmins the sysadmin role on all non-system resources.""" - with contextlib.closing(conn.cursor()) as cursor: - sysadminroleid = system_administrator_role_id(cursor) - cursor.executemany( - "DELETE FROM user_roles " - "WHERE user_id=? AND resource_id=? AND role_id=?", - tuple(item + (sysadminroleid,) - for item in itertools.product( - fetch_ids_for_sysadmin_users(cursor), - fetch_non_system_resources(cursor)))) - -steps = [ - step(assign_sysadmin_role_on_non_system_resources, - revoke_sysadmin_role_on_non_system_resources) -] diff --git a/migrations/auth/20250731_01_Ke1us-add-sysadmin-privileges-for-acting-on-groups-members.py b/migrations/auth/20250731_01_Ke1us-add-sysadmin-privileges-for-acting-on-groups-members.py deleted file mode 100644 index 95a6fbb..0000000 --- a/migrations/auth/20250731_01_Ke1us-add-sysadmin-privileges-for-acting-on-groups-members.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -Add sysadmin privileges for acting on groups: mostly handling user management. -""" -import itertools -import contextlib - -from yoyo import step - -__depends__ = {'20250729_03_oCvvq-grant-role-to-all-resources-to-sys-admin-users'} - - -def system_administrator_role_id(cursor): - """Fetch ID for role 'system-administrator'.""" - cursor.execute( - "SELECT role_id FROM roles WHERE role_name='system-administrator'") - return cursor.fetchone()[0] - - -def add_group_privileges_to_sysadmin_role(conn): - """Add group-management privileges to sysadmin role.""" - with contextlib.closing(conn.cursor()) as cursor: - sysadminroleid = system_administrator_role_id(cursor) - cursor.executemany( - "INSERT INTO role_privileges(role_id, privilege_id) VALUES (?, ?)", - tuple(itertools.product( - (sysadminroleid,), - ('system:group:add-group-member', - 'system:group:remove-group-member', - 'system:group:assign-group-leader', - 'system:group:revoke-group-leader')))) - - -def remove_group_privileges_to_sysadmin_role(conn): - """Remove group-management privileges from sysadmin role.""" - with contextlib.closing(conn.cursor()) as cursor: - sysadminroleid = system_administrator_role_id(cursor) - cursor.executemany( - "DELETE FROM role_privileges WHERE role_id=? AND privilege_id=?", - tuple(itertools.product( - (sysadminroleid,), - ('system:group:add-group-member', - 'system:group:remove-group-member', - 'system:group:assign-group-leader', - 'system:group:revoke-group-leader')))) - - -steps = [ - step( - """ - INSERT INTO privileges(privilege_id, privilege_description) - VALUES - ('system:group:add-group-member', - 'Make an existing user a member of a group.'), - ('system:group:remove-group-member', - 'Remove a member user from a group.'), - ('system:group:assign-group-leader', - 'Assign an existing group member the group-leader role'), - ('system:group:revoke-group-leader', - 'Revoke the group-leader role from a group member with the role.') - """, - """ - DELETE FROM privileges WHERE privilege_id IN - ('system:group:add-group-member', - 'system:group:remove-group-member', - 'system:group:assign-group-leader', - 'system:group:revoke-group-leader') - """), - step(add_group_privileges_to_sysadmin_role, - remove_group_privileges_to_sysadmin_role) -] diff --git a/migrations/auth/20260206_01_v3f4P-add-role-systemwide-data-curator.py b/migrations/auth/20260206_01_v3f4P-add-role-systemwide-data-curator.py deleted file mode 100644 index 63e807a..0000000 --- a/migrations/auth/20260206_01_v3f4P-add-role-systemwide-data-curator.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -add role systemwide-data-curator. -""" -import uuid -import contextlib - -from yoyo import step - -__depends__ = {'20250731_01_Ke1us-add-sysadmin-privileges-for-acting-on-groups-members'} - - -def create_systemwide_data_curator_role(conn): - """Create a new 'systemwide-data-curator' role.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "INSERT INTO roles(role_id, role_name, user_editable) " - "VALUES (?, 'systemwide-data-curator', 0)", - (str(uuid.uuid4()),)) - - -def link_privileges_to_role(conn): - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute("SELECT role_id FROM roles " - "WHERE role_name='systemwide-data-curator'") - role_id = cursor.fetchone()[0] - cursor.executemany("INSERT INTO role_privileges(role_id, privilege_id) " - "VALUES (?, ?)", - tuple((role_id, priv) for priv in - ("system:system-wide:data:edit", - "system:system-wide:data:delete"))) - - -def unlink_privileges_from_role(conn): - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute("SELECT role_id FROM roles " - "WHERE role_name='systemwide-data-curator'") - role_id = cursor.fetchone()[0] - cursor.executemany("DELETE FROM role_privileges " - "WHERE role_id=? AND privilege_id=?", - tuple((role_id, priv) for priv in - ("system:system-wide:data:edit", - "system:system-wide:data:delete"))) - - -steps = [ - step(# Add new privileges - """ - INSERT INTO privileges (privilege_id, privilege_description) - VALUES - ('system:system-wide:data:edit', - 'A user with this privilege can edit any data on the entire system.'), - ('system:system-wide:data:delete', - 'A user with this privilege can delete any data from the system.') - """, - """ - DELETE FROM privileges WHERE privilege_id IN - ('system:system-wide:data:edit', 'system:system-wide:data:delete')"""), - step(create_systemwide_data_curator_role, - "DELETE FROM roles WHERE role_name='systemwide-data-curator'"), - step(link_privileges_to_role, unlink_privileges_from_role) -] diff --git a/migrations/auth/20260311_01_TfRlV-add-privilege-for-gn-docs-documentation-editing.py b/migrations/auth/20260311_01_TfRlV-add-privilege-for-gn-docs-documentation-editing.py deleted file mode 100644 index d618f14..0000000 --- a/migrations/auth/20260311_01_TfRlV-add-privilege-for-gn-docs-documentation-editing.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -add privilege for gn-docs documentation editing -""" -import uuid -import contextlib - -from yoyo import step - -__depends__ = {'20260206_01_v3f4P-add-role-systemwide-data-curator'} - -ROLE_NAME = 'systemwide-docs-editor' - - -def create_systemwide_docs_editor_role(conn): - """Create a new 'systemwide-data-curator' role.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "INSERT INTO roles(role_id, role_name, user_editable) " - "VALUES (?, ?, 0)", - (str(uuid.uuid4()), ROLE_NAME)) - - -def delete_systemwide_docs_editor_role(conn): - """Create a new 'systemwide-data-curator' role.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute("DELETE FROM roles WHERE role_name=?", (ROLE_NAME,)) - - -def assign_edit_priv_to_docs_editor(conn): - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute("SELECT role_id FROM roles WHERE role_name=?", - (ROLE_NAME,)) - role_id = cursor.fetchone()[0] - - cursor.execute( - "INSERT INTO role_privileges(role_id, privilege_id) " - "VALUES (?, ?)", - (role_id, "system:documentation:edit")) - - -def revoke_edit_priv_to_docs_editor(conn): - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute("SELECT role_id FROM roles WHERE role_name=?", - (ROLE_NAME,)) - role_id = cursor.fetchone()[0] - - cursor.execute( - "DELETE FROM role_privileges WHERE role_id=? AND privilege_id=?", - (role_id, "system:documentation:edit")) - - -steps = [ - step( - """INSERT INTO privileges(privilege_id, privilege_description) - VALUES( - 'system:documentation:edit', - 'Allows the holder to edit documentation presented with the Genenetwork system.' - )""", - "DELETE FROM privileges WHERE privilege_id='system:documentation:edit'"), - step(create_systemwide_docs_editor_role, delete_systemwide_docs_editor_role), - step(assign_edit_priv_to_docs_editor, revoke_edit_priv_to_docs_editor) -] diff --git a/migrations/auth/20260311_02_v3EFQ-assign-systemwide-docs-editor-role-to-sysadmins.py b/migrations/auth/20260311_02_v3EFQ-assign-systemwide-docs-editor-role-to-sysadmins.py deleted file mode 100644 index e79ef6a..0000000 --- a/migrations/auth/20260311_02_v3EFQ-assign-systemwide-docs-editor-role-to-sysadmins.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -Assign 'systemwide-docs-editor' role to sysadmins -""" -import uuid -import contextlib - -from yoyo import step - -__depends__ = {'20260311_01_TfRlV-add-privilege-for-gn-docs-documentation-editing'} - - -def fetch_docs_editor_role_id(cursor): - """Fetch ID of systemwide-docs-editor role""" - cursor.execute( - "SELECT role_id FROM roles WHERE role_name='systemwide-docs-editor'") - return cursor.fetchone()[0] - - -def fetch_sys_resource_id(cursor): - """Fetch the resource ID of the system.""" - cursor.execute("SELECT resource_id FROM resources " - "WHERE resource_name='GeneNetwork System'") - return cursor.fetchone()[0] - - -def fetch_sys_admin_ids(cursor): - """Fetch the sysadmins' IDs.""" - cursor.execute( - "SELECT user_roles.user_id FROM resources INNER JOIN user_roles " - "ON resources.resource_id=user_roles.resource_id INNER JOIN roles " - "ON user_roles.role_id=roles.role_id " - "WHERE resources.resource_name='GeneNetwork System' " - "AND roles.role_name='system-administrator'") - return tuple(row[0] for row in cursor.fetchall()) - - -def __build_params__(cursor): - sysresourceid = fetch_sys_resource_id(cursor) - sysadminids = fetch_sys_admin_ids(cursor) - roleid = fetch_docs_editor_role_id(cursor) - return tuple({ - "user_id": userid, - "role_id": roleid, - "resource_id": sysresourceid - } for userid in sysadminids) - - -def assign_systemwide_docs_editor_role_to_sysadmins(conn): - with contextlib.closing(conn.cursor()) as cursor: - cursor.executemany( - "INSERT INTO user_roles(user_id, role_id, resource_id) " - "VALUES(:user_id, :role_id, :resource_id)", - __build_params__(cursor)) - - -def revoke_systemwide_docs_editor_role_from_sysadmins(conn): - with contextlib.closing(conn.cursor()) as cursor: - cursor.executemany( - "DELETE FROM user_roles WHERE user_id=:user_id " - "AND role_id=:role_id AND resource_id=:resource_id", - __build_params__(cursor)) - -steps = [ - step(assign_systemwide_docs_editor_role_to_sysadmins, - revoke_systemwide_docs_editor_role_from_sysadmins) -] diff --git a/migrations/auth/20260311_03_vxBCX-restrict-access-to-resources-make-public-feature.py b/migrations/auth/20260311_03_vxBCX-restrict-access-to-resources-make-public-feature.py deleted file mode 100644 index bdf8a56..0000000 --- a/migrations/auth/20260311_03_vxBCX-restrict-access-to-resources-make-public-feature.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Restrict access to resources' 'Make Public' feature. -""" -import contextlib - -from yoyo import step - -__depends__ = {'20260311_02_v3EFQ-assign-systemwide-docs-editor-role-to-sysadmins'} - - -def fetch_systemwide_data_curator_role_id(cursor): - "Fetch the role's ID." - cursor.execute("SELECT role_id FROM roles " - "WHERE role_name='systemwide-data-curator'") - return cursor.fetchone()[0] - - -def assign_make_public_to_systemwide_data_curator(conn): - """Assign privilege to 'systemwide-data-curator' role.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "INSERT INTO role_privileges(role_id, privilege_id) " - "VALUES(?, 'system:resource:make-public')", - (fetch_systemwide_data_curator_role_id(cursor),)) - - -def revoke_make_public_from_systemwide_data_curator(conn): - """Revoke privilege from 'systemwide-data-curator' role.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "DELETE FROM role_privileges " - "WHERE role_id=? AND privilege_id='system:resource:make-public'", - (fetch_systemwide_data_curator_role_id(cursor),)) - - -steps = [ - step( - """ - INSERT INTO privileges(privilege_id, privilege_description) - VALUES( - 'system:resource:make-public', - 'Allow user to make a resource publicly accessible.') - """, - """ - DELETE FROM privileges WHERE privilege_id='system:resource:make-public' - """), - step(assign_make_public_to_systemwide_data_curator, - revoke_make_public_from_systemwide_data_curator), -] diff --git a/migrations/auth/20260331_01_FV1sL-add-privileges-to-role-systemwide-data-curator.py b/migrations/auth/20260331_01_FV1sL-add-privileges-to-role-systemwide-data-curator.py deleted file mode 100644 index 22863ae..0000000 --- a/migrations/auth/20260331_01_FV1sL-add-privileges-to-role-systemwide-data-curator.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -Add privileges to role systemwide-data-curator -""" -import contextlib - -from yoyo import step - -__depends__ = {'20260311_03_vxBCX-restrict-access-to-resources-make-public-feature'} - - -__new_privileges__ = ( - ("system:system-wide:inbredset:view-case-attribute", - "Enable view of any and all inbredset case attributes system-wide."), - ("system:system-wide:inbredset:edit-case-attribute", - "Enable edit of any and all inbredset case attributes system-wide."), - ("system:system-wide:inbredset:delete-case-attribute", - "Enable deletion of any and all inbredset case attributes system-wide."), - ("system:system-wide:inbredset:apply-case-attribute-edit", - "Enable applying changes to any and all inbredset case attributes system-wide."), - ("system:system-wide:inbredset:reject-case-attribute-edit", - "Enable rejecting changes to any and all inbredset case attributes system-wide.")) - - -def fetch_systemwide_data_curator_role_id(cursor): - "Fetch the role's ID." - cursor.execute("SELECT role_id FROM roles " - "WHERE role_name='systemwide-data-curator'") - return cursor.fetchone()[0] - - -def create_new_privileges(conn): - """Create new privileges for the system.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.executemany( - "INSERT INTO privileges(privilege_id, privilege_description) " - "VALUES (?, ?)", - __new_privileges__) - - -def delete_new_privileges(conn): - """Delete these new privileges from the system.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.executemany("DELETE FROM privileges WHERE privilege_id=?", - tuple((priv[0],) for priv in __new_privileges__)) - - -def assign_new_privileges(conn): - """Assign the new privileges to the `systemwide-data-curator` role.""" - with contextlib.closing(conn.cursor()) as cursor: - role_id = fetch_systemwide_data_curator_role_id(cursor) - cursor.executemany( - "INSERT INTO role_privileges(role_id, privilege_id) VALUES (?, ?)", - tuple((role_id, privilege[0]) for privilege in __new_privileges__)) - - -def revoke_new_privileges(conn): - """Revoke the new privileges from the `systemwide-data-curator` role.""" - with contextlib.closing(conn.cursor()) as cursor: - role_id = fetch_systemwide_data_curator_role_id(cursor) - cursor.executemany( - "DELETE FROM role_privileges WHERE role_id=? AND privilege_id=?", - tuple((role_id, privilege[0]) for privilege in __new_privileges__)) - - - -steps = [ - step(create_new_privileges, delete_new_privileges), - step(assign_new_privileges, revoke_new_privileges) -] diff --git a/migrations/auth/20260402_01_Bf8nm-add-user-and-time-tracking-to-resources-table.py b/migrations/auth/20260402_01_Bf8nm-add-user-and-time-tracking-to-resources-table.py deleted file mode 100644 index 702c418..0000000 --- a/migrations/auth/20260402_01_Bf8nm-add-user-and-time-tracking-to-resources-table.py +++ /dev/null @@ -1,185 +0,0 @@ -""" -Add user and time tracking to resources table -""" -import random -import contextlib -from datetime import datetime - -from yoyo import step - -__depends__ = {'20260331_01_FV1sL-add-privileges-to-role-systemwide-data-curator'} - -GN_AUTH_INIT_TIMESTAMP = 1691130509.0 -__admin_id__ = "" - - -def fetch_acentenos_id(conn): - """Fetch the default resource creator.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute("SELECT user_id FROM users WHERE email=?", - (("acent" "eno@" "uthsc" "." "edu"),)) - res = cursor.fetchone() - return res[0] if bool(res) else None - - -def fetch_a_sysadmin_id(conn, resources_table): - """Fetch one ID out of all system administrator users.""" - global __admin_id__ - - def __fetch__(): - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - f"SELECT ur.user_id FROM {resources_table} AS rsc " - "INNER JOIN user_roles AS ur ON rsc.resource_id=ur.resource_id " - "INNER JOIN roles AS r ON ur.role_id=r.role_id " - "WHERE resource_name='GeneNetwork System' " - "AND r.role_name='system-administrator'" - ) - return tuple(row[0] for row in cursor.fetchall()) - - if not bool(__admin_id__): - __admins__ = __fetch__() - if len(__admins__) > 0: - __admin_id__ = random.choice(__admins__) - - return __admin_id__ - - -def add_user_and_time_tracking_columns(conn): - """Add user and time tracking columns.""" - conn.execute( - """ - CREATE TABLE resources_new( - resource_id TEXT NOT NULL, - resource_name TEXT NOT NULL UNIQUE, - resource_category_id TEXT NOT NULL, - public INTEGER NOT NULL DEFAULT 0 CHECK (public=0 or public=1), - created_by TEXT NOT NULL, - created_at REAL NOT NULL DEFAULT '1691130509.0', - PRIMARY KEY(resource_id), - FOREIGN KEY(resource_category_id) - REFERENCES resource_categories(resource_category_id) - ON UPDATE CASCADE ON DELETE RESTRICT, - FOREIGN KEY(created_by) - REFERENCES users(user_id) ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - - -def drop_user_and_time_tracking_columns(conn): - """Drop user and time tracking columns.""" - conn.execute("PRAGMA foreign_keys = OFF") - conn.execute("DROP TABLE IF EXISTS resources") - conn.execute("ALTER TABLE resources_old RENAME TO resources") - conn.execute("PRAGMA foreign_key_check") - conn.execute("PRAGMA foreign_keys = ON") - - -def update_data_for_new_resources_table(conn): - """Add creator and time to original data.""" - __creator__ = ( - fetch_acentenos_id(conn) or fetch_a_sysadmin_id(conn, "resources")) - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute("SELECT * FROM resources") - cursor.executemany( - "INSERT INTO resources_new(" - " resource_id," - " resource_name," - " resource_category_id," - " public," - " created_by," - " created_at" - ") VALUES (?, ?, ?, ?, ?, ?)", - tuple( - tuple(row) + (__creator__, GN_AUTH_INIT_TIMESTAMP) - for row in cursor.fetchall())) - - -def restore_data_for_old_resources_table(conn): - """Remove creator and time from data.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute("SELECT * FROM resources") - cursor.executemany( - "INSERT INTO resources_old(" - " resource_id," - " resource_name," - " resource_category_id," - " public" - ") VALUES (?, ?, ?, ?)", - tuple(tuple(row)[0:4] for row in cursor.fetchall())) - - -def replace_old_table_with_new_table(conn): - """Restore old resources table with the new resources table.""" - conn.execute("PRAGMA foreign_keys = OFF") - conn.execute("DROP TABLE resources") - conn.execute("ALTER TABLE resources_new RENAME TO resources") - conn.execute("PRAGMA foreign_key_check") - conn.execute("PRAGMA foreign_keys = ON") - - -def restore_old_table(conn): - """Restore old 'resources' table schema.""" - conn.execute( - """ - CREATE TABLE resources_old( - resource_id TEXT NOT NULL, - resource_name TEXT NOT NULL UNIQUE, - resource_category_id TEXT NOT NULL, - public INTEGER NOT NULL DEFAULT 0 CHECK (public=0 or public=1), - PRIMARY KEY(resource_id), - FOREIGN KEY(resource_category_id) - REFERENCES resource_categories(resource_category_id) - ON UPDATE CASCADE ON DELETE RESTRICT - ) WITHOUT ROWID - """) - - -def parse_creator_and_time(cursor, row): - __return__ = None - - __name_parts__ = row[1].split("—") - if len(__name_parts__) == 4: - __email__, __inbredsetname__, __datetimestr__, count = __name_parts__ - cursor.execute("SELECT user_id FROM users WHERE email=?", - (__email__.strip(),)) - results = cursor.fetchone() - if bool(results): - __return__ = { - "resource_id": row[0], - "creator": results[0], - "created": datetime.fromisoformat(__datetimestr__).timestamp() - } - - return __return__ - - -def update_creators_and_time(conn): - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute("SELECT resource_id, resource_name FROM resources") - cursor.executemany( - "UPDATE resources SET created_by=:creator, created_at=:created " - "WHERE resource_id=:resource_id", - tuple(item for item in - (parse_creator_and_time(cursor, row) - for row in cursor.fetchall()) - if item is not None)) - - - -def restore_default_creators_and_time(conn): - with contextlib.closing(conn.cursor()) as cursor: - __creator__ = ( - fetch_acentenos_id(conn) or fetch_a_sysadmin_id(conn, "resources")) - cursor.execute("UPDATE resources SET created_by=?, created_at=?", - (__creator__, GN_AUTH_INIT_TIMESTAMP)) - - -steps = [ - step(add_user_and_time_tracking_columns, - drop_user_and_time_tracking_columns), - step(update_data_for_new_resources_table, - restore_data_for_old_resources_table), - step(replace_old_table_with_new_table, restore_old_table), - step(update_creators_and_time, restore_default_creators_and_time) -] diff --git a/migrations/auth/20260428_01_Tak6O-new-privilege-system-system-wide-data-view.py b/migrations/auth/20260428_01_Tak6O-new-privilege-system-system-wide-data-view.py deleted file mode 100644 index 2dddc56..0000000 --- a/migrations/auth/20260428_01_Tak6O-new-privilege-system-system-wide-data-view.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -New privilege: system:system-wide:data:view -""" - -from yoyo import step - -__depends__ = {'20260402_01_Bf8nm-add-user-and-time-tracking-to-resources-table'} - -steps = [ - step( - """ - INSERT INTO privileges(privilege_id, privilege_description) - VALUES('system:system-wide:data:view', - 'A user with this privilege can view any data on the entire system.') - """, - """ - DELETE FROM privileges WHERE privilege_id='system:system-wide:data:view' - """) -] diff --git a/migrations/auth/20260428_02_L6zIV-add-privileges-to-batch-editors-role.py b/migrations/auth/20260428_02_L6zIV-add-privileges-to-batch-editors-role.py deleted file mode 100644 index 537bf9b..0000000 --- a/migrations/auth/20260428_02_L6zIV-add-privileges-to-batch-editors-role.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -Add privileges to batch-editors role -""" -import contextlib - -from yoyo import step - -__depends__ = {'20260428_01_Tak6O-new-privilege-system-system-wide-data-view'} - - -def fetch_batch_editors_role_id(cursor): - """Fetch the ID of the batch-editors role.""" - cursor.execute("SELECT role_id FROM roles WHERE role_name='Batch Editors'") - res = cursor.fetchone() - if not bool(res): - cursor.execute( - "SELECT role_id FROM roles WHERE role_name='batch-editors'") - res = cursor.fetchone() - - return res[0] if bool(res) else None - - -def rename_role(conn): - """Rename role from 'Batch Editors' to 'batch-editors'.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "UPDATE roles SET role_name='batch-editors' WHERE role_id=?", - (fetch_batch_editors_role_id(cursor),)) - - -def restore_old_role_name(conn): - """Rename role from 'batch-editors' to 'Batch Editors'.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "UPDATE roles SET role_name='Batch Editors' WHERE role_id=?", - (fetch_batch_editors_role_id(cursor),)) - - -def add_new_privileges(conn): - """Add new privileges to 'batch-editors' role.""" - with contextlib.closing(conn.cursor()) as cursor: - role_id = fetch_batch_editors_role_id(cursor) - cursor.executemany( - "INSERT INTO role_privileges(role_id, privilege_id) VALUES(?, ?)", - tuple((role_id, priv) for priv in ( - "system:system-wide:data:view", - "system:system-wide:data:edit"))) - - -def remove_new_privileges(conn): - """Remove new privileges from 'batch-editors' role.""" - with contextlib.closing(conn.cursor()) as cursor: - cursor.execute( - "DELETE FROM role_privileges WHERE role_id=? AND privilege_id IN " - "('system:system-wide:data:view', 'system:system-wide:data:edit')", - (fetch_batch_editors_role_id(cursor),)) - - -steps = [ - step(rename_role, restore_old_role_name), - step(add_new_privileges, remove_new_privileges) -] diff --git a/migrations/auth/__init__.py b/migrations/auth/__init__.py deleted file mode 100644 index 1358c9a..0000000 --- a/migrations/auth/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"Auth(entic|oris)ation package." diff --git a/tests/unit/auth/test_migrations_init_data_in_resource_categories_table.py b/tests/unit/auth/test_migrations_init_data_in_resource_categories_table.py index c34a549..a32cacb 100644 --- a/tests/unit/auth/test_migrations_init_data_in_resource_categories_table.py +++ b/tests/unit/auth/test_migrations_init_data_in_resource_categories_table.py @@ -8,7 +8,7 @@ from gn_auth.migrations import get_migration, apply_migrations, rollback_migrati from tests.unit.auth.conftest import ( apply_single_migration, rollback_single_migration, migrations_up_to) -MIGRATION_PATH = "migrations/auth/20221108_04_CKcSL-init-data-in-resource-categories-table.py" +MIGRATION_PATH = "gn_auth/migrations/auth/20221108_04_CKcSL-init-data-in-resource-categories-table.py" @pytest.mark.unit_test def test_apply_init_data(auth_testdb_path, auth_migrations_dir, backend): diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 53ee062..c6abc65 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -30,7 +30,8 @@ def fxtr_app(): f'testdb_{datetime.now().strftime("%Y%m%dT%H%M%S")}') testuploadsdir = Path(testdir).joinpath("uploads") testuploadsdir.mkdir() - app = create_app({ + app = create_app() + app.config.update({ "TESTING": True, "AUTH_DB": testdb, "GN_AUTH_SECRETS": str(setup_secrets(testdir)), -- cgit 1.4.1