Skip to content

Commit 7194e83

Browse files
devGregAclaude
andcommitted
docs(authorization): add migration rehearsal procedure
Runnable verification script for the four upgrade paths the legacy authorization rewrite has to handle: 1. Pro path — transparent, state-only flip; no row changes. 2. OS-standalone upgrade — schema migration adds authorized_users, data migration backfills from RBAC tables (which stay verbatim), state-release flips RBAC models to managed=False. 3. OS -> Pro reinstall — Pro adopts existing tables; reconcile management command syncs authorized_users edits made under OS-only operation back into Product_Member / Product_Type_Member. 4. Fresh OS install — schema-only migration; data migration is a no-op via the dojo_role introspection guard. Each scenario specifies setup, exact commands, and the verification queries that confirm the desired post-state. Scenarios 1 and 3 are verified in this environment against the bare_bones fixture (442 Pro tests pass; reconcile dry-run reports the expected 1 Product_Member + 1 Product_Type_Member to create from the backfilled authorized_users). Scenarios 2 and 4 are documented with the procedure to run on appropriate snapshots — they require an OS-only deployment topology and a fresh DB respectively, neither of which the dev docker-compose exposes by default. The file lives in dojo/authorization/ alongside the migrations so PR reviewers and customers maintaining downstream installs can find the verification procedure next to the code that requires it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0f90728 commit 7194e83

1 file changed

Lines changed: 247 additions & 0 deletions

File tree

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
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

Comments
 (0)