Skip to content

Commit 26f2381

Browse files
committed
Improve AuthN DB migration, address API key scopes
1 parent 5b82437 commit 26f2381

1 file changed

Lines changed: 55 additions & 33 deletions

File tree

tiled/authn_database/migrations/versions/d829476bc173_split_apikeys_scope_into_create_and_.py

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
77
"""
88
from alembic import op
9+
from sqlalchemy.orm import lazyload
910
from sqlalchemy.orm.session import Session
1011

11-
from tiled.authn_database.orm import Role
12+
from tiled.authn_database.orm import APIKey, Role
1213

1314
# revision identifiers, used by Alembic.
1415
revision = "d829476bc173"
@@ -18,57 +19,78 @@
1819

1920

2021
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+
}
2531

2632

2733
def upgrade():
2834
"""
2935
Add new scopes to Roles.
3036
Remove old scopes from Roles, if present.
37+
38+
Also adjust scopes for server API keys.
3139
"""
3240
connection = op.get_bind()
3341
with Session(bind=connection) as db:
3442
for role_name in ROLES:
3543
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()
4964

5065

5166
def downgrade():
5267
"""
5368
Remove new scopes from Roles, if present.
5469
Add old scopes to Roles, if not preesent.
70+
71+
Also adjust scopes for server API keys.
5572
"""
5673
connection = op.get_bind()
5774
with Session(bind=connection) as db:
5875
for role_name in ROLES:
5976
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

Comments
 (0)