|
6 | 6 |
|
7 | 7 | """ |
8 | 8 | from alembic import op |
| 9 | +from sqlalchemy.orm import lazyload |
9 | 10 | from sqlalchemy.orm.session import Session |
10 | 11 |
|
11 | | -from tiled.authn_database.orm import Role |
| 12 | +from tiled.authn_database.orm import APIKey, Role |
12 | 13 |
|
13 | 14 | # revision identifiers, used by Alembic. |
14 | 15 | revision = "d829476bc173" |
|
18 | 19 |
|
19 | 20 |
|
20 | 21 | ROLES = ["admin", "user"] |
21 | | -NEW_SCOPES_USER = ["create:apikeys", "revoke:apikeys", "create:node"] |
22 | | -OLD_SCOPES_USER = ["apikeys", "create"] |
23 | | -NEW_SCOPES_ADMIN = ["create:node"] |
24 | | -OLD_SCOPES_ADMIN = ["create"] |
| 22 | +OLD_TO_NEW_SCOPES = { |
| 23 | + "create": ["create:node"], |
| 24 | + "apikeys": ["revoke:apikeys", "create:apikeys"], |
| 25 | +} |
| 26 | +NEW_TO_OLD_SCOPES = { |
| 27 | + "create:node": ["create"], |
| 28 | + "revoke:apikeys": ["apikeys"], |
| 29 | + "create:apikeys": ["apikeys"], |
| 30 | +} |
25 | 31 |
|
26 | 32 |
|
27 | 33 | def upgrade(): |
28 | 34 | """ |
29 | 35 | Add new scopes to Roles. |
30 | 36 | Remove old scopes from Roles, if present. |
| 37 | +
|
| 38 | + Also adjust scopes for server API keys. |
31 | 39 | """ |
32 | 40 | connection = op.get_bind() |
33 | 41 | with Session(bind=connection) as db: |
34 | 42 | for role_name in ROLES: |
35 | 43 | role = db.query(Role).filter(Role.name == role_name).first() |
36 | | - scopes = role.scopes.copy() |
37 | | - if role_name == "admin": |
38 | | - NEW_SCOPES = NEW_SCOPES_ADMIN |
39 | | - OLD_SCOPES = OLD_SCOPES_ADMIN |
40 | | - else: |
41 | | - NEW_SCOPES = NEW_SCOPES_USER |
42 | | - OLD_SCOPES = OLD_SCOPES_USER |
43 | | - for scope in OLD_SCOPES: |
44 | | - if scope in scopes: |
45 | | - scopes.remove(scope) |
46 | | - scopes.extend(NEW_SCOPES) |
47 | | - role.scopes = scopes |
48 | | - db.commit() |
| 44 | + if not role: |
| 45 | + raise RuntimeError(f"Expected role '{role_name}' not found in db!") |
| 46 | + scopes = set(role.scopes or []) |
| 47 | + for old_scope, new_scopes in OLD_TO_NEW_SCOPES.items(): |
| 48 | + if old_scope in scopes: |
| 49 | + scopes.discard(old_scope) |
| 50 | + scopes |= set(new_scopes) |
| 51 | + role.scopes = list(scopes) |
| 52 | + |
| 53 | + for apikey in ( |
| 54 | + db.query(APIKey).options(lazyload(APIKey.principal)).yield_per(500) |
| 55 | + ): |
| 56 | + scopes = set(apikey.scopes) |
| 57 | + for old_scope, new_scopes in OLD_TO_NEW_SCOPES.items(): |
| 58 | + if old_scope in scopes: |
| 59 | + scopes.discard(old_scope) |
| 60 | + scopes |= set(new_scopes) |
| 61 | + apikey.scopes = list(scopes) |
| 62 | + |
| 63 | + db.commit() |
49 | 64 |
|
50 | 65 |
|
51 | 66 | def downgrade(): |
52 | 67 | """ |
53 | 68 | Remove new scopes from Roles, if present. |
54 | 69 | Add old scopes to Roles, if not preesent. |
| 70 | +
|
| 71 | + Also adjust scopes for server API keys. |
55 | 72 | """ |
56 | 73 | connection = op.get_bind() |
57 | 74 | with Session(bind=connection) as db: |
58 | 75 | for role_name in ROLES: |
59 | 76 | role = db.query(Role).filter(Role.name == role_name).first() |
60 | | - scopes = role.scopes.copy() |
61 | | - if role_name == "admin": |
62 | | - NEW_SCOPES = NEW_SCOPES_ADMIN |
63 | | - OLD_SCOPES = OLD_SCOPES_ADMIN |
64 | | - else: |
65 | | - NEW_SCOPES = NEW_SCOPES_USER |
66 | | - OLD_SCOPES = OLD_SCOPES_USER |
67 | | - for scope in NEW_SCOPES: |
68 | | - if scope in scopes: |
69 | | - scopes.remove(scope) |
70 | | - for scope in OLD_SCOPES: |
71 | | - if scope not in scopes: |
72 | | - scopes.append(scope) |
73 | | - role.scopes = scopes |
74 | | - db.commit() |
| 77 | + if not role: |
| 78 | + raise RuntimeError(f"Expected role '{role_name}' not found in db!") |
| 79 | + scopes = set(role.scopes or []) |
| 80 | + for new_scope, old_scopes in NEW_TO_OLD_SCOPES.items(): |
| 81 | + if new_scope in scopes: |
| 82 | + scopes.discard(new_scope) |
| 83 | + scopes |= set(old_scopes) |
| 84 | + role.scopes = list(scopes) |
| 85 | + |
| 86 | + for apikey in ( |
| 87 | + db.query(APIKey).options(lazyload(APIKey.principal)).yield_per(500) |
| 88 | + ): |
| 89 | + scopes = set(apikey.scopes) |
| 90 | + for new_scope, old_scopes in NEW_TO_OLD_SCOPES.items(): |
| 91 | + if new_scope in scopes: |
| 92 | + scopes.discard(new_scope) |
| 93 | + scopes |= set(old_scopes) |
| 94 | + apikey.scopes = list(scopes) |
| 95 | + |
| 96 | + db.commit() |
0 commit comments