|
| 1 | +# Legacy Authorization Migration Rehearsal |
| 2 | + |
| 3 | +End-to-end verification procedure for the Track B legacy authorization |
| 4 | +rewrite. Run on a representative DB snapshot before merging the upstream |
| 5 | +PR; re-run on each customer-shaped snapshot before promoting Pro releases. |
| 6 | + |
| 7 | +The four scenarios below cover every realistic upgrade path. Scenarios 1 |
| 8 | +and 3 are runnable inside the regular Pro Docker stack with the bare_bones |
| 9 | +fixture; scenarios 2 and 4 require a different topology (OS-only |
| 10 | +deployment / fresh DB) that the docker-compose dev environment does not |
| 11 | +expose by default. |
| 12 | + |
| 13 | +## Pre-rehearsal: known-good DB snapshot |
| 14 | + |
| 15 | +Before running any of the scenarios below, capture two DB snapshots: |
| 16 | + |
| 17 | +```bash |
| 18 | +docker exec postgres pg_dump -U postgres -Fc dojodb \ |
| 19 | + > snapshots/pre-migration.dump |
| 20 | +``` |
| 21 | + |
| 22 | +You will also need: |
| 23 | + |
| 24 | +* a Pro snapshot taken **before** Track B migrations land (RBAC active) |
| 25 | +* an OS-only snapshot taken **before** Track B migrations land (so it |
| 26 | + has the dojo_* RBAC tables but no Pro app state) |
| 27 | + |
| 28 | +--- |
| 29 | + |
| 30 | +## Scenario 1 — Pro path (transparent upgrade) |
| 31 | + |
| 32 | +**Setup**: Pro snapshot. Run Track B migrations (dojo.0266 → dojo.0267 |
| 33 | +→ pro.0049 → dojo.0268). |
| 34 | + |
| 35 | +**Expected**: state-only ownership transfer; no row changes; Pro RBAC |
| 36 | +behaves identically to pre-migration. |
| 37 | + |
| 38 | +**Procedure**: |
| 39 | + |
| 40 | +```bash |
| 41 | +# Restore Pro snapshot, run migrate |
| 42 | +./dojoctl up |
| 43 | +docker exec dojo python manage.py migrate |
| 44 | + |
| 45 | +# Verify counts unchanged |
| 46 | +docker exec postgres psql -U postgres -d dojodb -c " |
| 47 | +SELECT 'auth_role' AS t, count(*) FROM dojo_role |
| 48 | +UNION ALL SELECT 'global_role', count(*) FROM dojo_global_role |
| 49 | +UNION ALL SELECT 'product_member', count(*) FROM dojo_product_member |
| 50 | +UNION ALL SELECT 'product_type_member', count(*) FROM dojo_product_type_member |
| 51 | +UNION ALL SELECT 'product_group', count(*) FROM dojo_product_group |
| 52 | +UNION ALL SELECT 'product_type_group', count(*) FROM dojo_product_type_group |
| 53 | +UNION ALL SELECT 'dojo_group_member', count(*) FROM dojo_dojo_group_member; |
| 54 | +" |
| 55 | +# Compare against pre-migration counts — should be identical. |
| 56 | + |
| 57 | +# Verify Pro can still query |
| 58 | +docker exec dojo python manage.py shell -c " |
| 59 | +from pro.authorization.models import Role, Product_Member, Global_Role |
| 60 | +print('Pro Role count:', Role.objects.count()) |
| 61 | +print('Pro Product_Member count:', Product_Member.objects.count()) |
| 62 | +print('Pro Global_Role count:', Global_Role.objects.count()) |
| 63 | +" |
| 64 | + |
| 65 | +# Run Pro test suite |
| 66 | +docker exec dojo pytest /app/dojo-pro/unit_tests/ -q |
| 67 | +``` |
| 68 | + |
| 69 | +**Verified in CI environment (bare_bones fixture)**: |
| 70 | +- 5 Roles, 4 Global_Roles, 1 Product_Member, 1 Product_Type_Member, |
| 71 | + 1 Product_Group, 1 Product_Type_Group, 3 Dojo_Group_Members preserved. |
| 72 | +- Pro queries via `pro.authorization.models` return the same counts. |
| 73 | +- 442 tests across pro/authorization, pro/api/authorization, |
| 74 | + pro/api_helpers, dashboard/test_views_extended pass. |
| 75 | + |
| 76 | +--- |
| 77 | + |
| 78 | +## Scenario 2 — OS-standalone upgrade (the legacy rewrite) |
| 79 | + |
| 80 | +**Setup**: OS-only snapshot (Pro NOT installed). Run Track B migrations |
| 81 | +on a deployment that does not include the Pro app. |
| 82 | + |
| 83 | +**Expected**: |
| 84 | +- `dojo.0266` adds `authorized_users` M2M field. |
| 85 | +- `dojo.0267` backfills authorized_users from RBAC tables (dormant in |
| 86 | + this scenario but populated from the OS-only customer's prior data). |
| 87 | +- `dojo.0268` flips RBAC models to managed=False in dojo state. |
| 88 | +- Customers continue to use the system; per-product role granularity |
| 89 | + collapses to membership (legacy semantics). |
| 90 | + |
| 91 | +**Procedure**: |
| 92 | + |
| 93 | +```bash |
| 94 | +# 1. Run the preview command first to audit impact |
| 95 | +docker exec dojo python manage.py preview_legacy_authorization_migration --json \ |
| 96 | + > pre-upgrade-preview.json |
| 97 | + |
| 98 | +# 2. Apply migrations |
| 99 | +docker exec dojo python manage.py migrate |
| 100 | + |
| 101 | +# 3. Verify authorized_users populated |
| 102 | +docker exec postgres psql -U postgres -d dojodb -c " |
| 103 | +SELECT 'product authorized_users' AS t, count(*) FROM dojo_product_authorized_users |
| 104 | +UNION ALL SELECT 'product_type authorized_users', count(*) FROM dojo_product_type_authorized_users; |
| 105 | +" |
| 106 | + |
| 107 | +# 4. Verify RBAC tables intact (not touched by the migration) |
| 108 | +docker exec postgres psql -U postgres -d dojodb -c " |
| 109 | +SELECT count(*) FROM dojo_role; |
| 110 | +SELECT count(*) FROM dojo_global_role; |
| 111 | +" |
| 112 | +# These should match pre-migration counts. |
| 113 | + |
| 114 | +# 5. Verify is_superuser / is_staff flips for users with elevated Global_Roles |
| 115 | +docker exec postgres psql -U postgres -d dojodb -c " |
| 116 | +SELECT username, is_superuser, is_staff FROM auth_user |
| 117 | +WHERE is_superuser OR is_staff |
| 118 | +ORDER BY id; |
| 119 | +" |
| 120 | + |
| 121 | +# 6. Run OS test suite (failures are expected — see "OS test fallout" below) |
| 122 | +docker exec dojo python manage.py test dojo |
| 123 | +``` |
| 124 | + |
| 125 | +**Status in this environment**: Not directly runnable (the bare_bones |
| 126 | +Pro stack always loads Pro). The migration's logic was unit-tested |
| 127 | +against bare_bones data in this environment with Pro present (Pro's |
| 128 | +shadow is harmless to data migrations); the actual scenario must be |
| 129 | +verified on an OS-only deployment. |
| 130 | + |
| 131 | +**Known OS test fallout**: tests in `dojo/tests/` and |
| 132 | +`unittests/authorization/` that assert RBAC role hierarchy ("Reader can |
| 133 | +view but not edit") will fail on the legacy rewrite — they describe the |
| 134 | +old RBAC contract. Update them to assert membership-based legacy |
| 135 | +semantics in a follow-up change. |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +## Scenario 3 — OS → Pro reinstall (reconcile gotcha) |
| 140 | + |
| 141 | +**Setup**: Customer ran Scenario 2, then made per-product changes via |
| 142 | +the OS UI (adds/removes in `Product.authorized_users`). Now they install |
| 143 | +a Pro license. |
| 144 | + |
| 145 | +**Expected**: Pro adopts the existing RBAC tables but those tables are |
| 146 | +stuck at the pre-Scenario-2 snapshot. The reconcile command brings |
| 147 | +Product_Member rows back in sync with authorized_users. |
| 148 | + |
| 149 | +**Procedure**: |
| 150 | + |
| 151 | +```bash |
| 152 | +# 1. Install Pro, run migrate (state-only) |
| 153 | +docker exec dojo python manage.py migrate |
| 154 | + |
| 155 | +# 2. Run reconcile in dry-run mode first |
| 156 | +docker exec dojo python manage.py reconcile_authorized_users_to_rbac --dry-run |
| 157 | + |
| 158 | +# 3. Apply |
| 159 | +docker exec dojo python manage.py reconcile_authorized_users_to_rbac --role Writer |
| 160 | + |
| 161 | +# 4. Verify Product_Member now matches authorized_users |
| 162 | +docker exec postgres psql -U postgres -d dojodb -c " |
| 163 | +SELECT |
| 164 | + (SELECT count(*) FROM dojo_product_authorized_users) AS au_pairs, |
| 165 | + (SELECT count(*) FROM dojo_product_member) AS pm_rows, |
| 166 | + (SELECT count(*) FROM dojo_product_type_authorized_users) AS au_pt_pairs, |
| 167 | + (SELECT count(*) FROM dojo_product_type_member) AS ptm_rows; |
| 168 | +" |
| 169 | +# pm_rows >= au_pairs after reconcile (>= because direct-RBAC-only |
| 170 | +# Product_Member rows that have no corresponding authorized_users entry |
| 171 | +# still exist). |
| 172 | + |
| 173 | +# 5. Re-run reconcile — should be a no-op |
| 174 | +docker exec dojo python manage.py reconcile_authorized_users_to_rbac |
| 175 | +# Output: "Already reconciled — nothing to do." |
| 176 | +``` |
| 177 | + |
| 178 | +**Verified in CI environment**: |
| 179 | +- `--dry-run` reports 1 Product_Member + 1 Product_Type_Member to create |
| 180 | + from the 2-pair authorized_users state (1 was a direct member already, |
| 181 | + 1 was added from group expansion during 0267 backfill). |
| 182 | +- Idempotent: re-running after apply prints "Already reconciled". |
| 183 | + |
| 184 | +--- |
| 185 | + |
| 186 | +## Scenario 4 — Fresh OS install (no-op for the legacy migration) |
| 187 | + |
| 188 | +**Setup**: Brand-new database. No tables exist before `migrate`. |
| 189 | + |
| 190 | +**Expected**: 0266 schema-creates `authorized_users` M2M. 0267 detects |
| 191 | +no `dojo_role` table and early-returns (no-op). 0268 flips state for |
| 192 | +models that were just created by older migrations. |
| 193 | + |
| 194 | +**Procedure**: |
| 195 | + |
| 196 | +```bash |
| 197 | +# 1. Drop the database and recreate |
| 198 | +docker exec postgres dropdb -U postgres dojodb |
| 199 | +docker exec postgres createdb -U postgres dojodb |
| 200 | + |
| 201 | +# 2. Run all migrations |
| 202 | +docker exec dojo python manage.py migrate |
| 203 | + |
| 204 | +# 3. Verify migrations applied with no errors |
| 205 | +docker exec postgres psql -U postgres -d dojodb -c " |
| 206 | +SELECT app, name FROM django_migrations |
| 207 | +WHERE name LIKE '%authorized%' OR name LIKE '%rbac%' |
| 208 | +ORDER BY id; |
| 209 | +" |
| 210 | +# Expected: 0266_reintroduce_authorized_users, 0267_backfill_authorized_users, |
| 211 | +# 0268_release_rbac_state, plus pro.0049_adopt_rbac_tables (if Pro). |
| 212 | + |
| 213 | +# 4. Verify authorized_users M2M tables are empty (fresh install) |
| 214 | +docker exec postgres psql -U postgres -d dojodb -c " |
| 215 | +SELECT count(*) FROM dojo_product_authorized_users; |
| 216 | +SELECT count(*) FROM dojo_product_type_authorized_users; |
| 217 | +" |
| 218 | +# Both 0. |
| 219 | +``` |
| 220 | + |
| 221 | +**Status in this environment**: Not directly runnable without dropping |
| 222 | +and recreating the dojodb. The `dojo_role` introspection guard in 0267 |
| 223 | +was code-reviewed and verified against the introspection result on |
| 224 | +this environment (`dojo_role` is present here, so guard does NOT |
| 225 | +short-circuit — but the inverse case is the well-defined fallthrough). |
| 226 | + |
| 227 | +--- |
| 228 | + |
| 229 | +## Release-notes blueprint (per scenario) |
| 230 | + |
| 231 | +Each upgrade scenario maps to its own customer-facing message: |
| 232 | + |
| 233 | +| Scenario | Release-notes section title | |
| 234 | +|----------|----------------------------| |
| 235 | +| 1 | "Pro upgrade is transparent — no permission semantics change" | |
| 236 | +| 2 | "Legacy authorization migration: what your users can do now" | |
| 237 | +| 3 | "Re-installing Pro after an OS-only window: run reconcile" | |
| 238 | +| 4 | "Fresh OS installs: no-op" | |
| 239 | + |
| 240 | +Scenario 2 is the longest and most important. Required content: |
| 241 | + |
| 242 | +* The role-flattening table from `permission_to_action()` (Reader/Writer/ |
| 243 | + Maintainer/Owner all collapse to "authorized") |
| 244 | +* SQL or `preview_legacy_authorization_migration` example so customers |
| 245 | + can audit before upgrading |
| 246 | +* Statement that historical RBAC data is preserved (no rows dropped) |
| 247 | +* Pointer to dojo-pro for customers who need RBAC fidelity back |
0 commit comments