paskia 0.7.2__py3-none-any.whl → 0.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. paskia/_version.py +2 -2
  2. paskia/authsession.py +12 -49
  3. paskia/bootstrap.py +30 -25
  4. paskia/db/__init__.py +163 -401
  5. paskia/db/background.py +128 -0
  6. paskia/db/jsonl.py +132 -0
  7. paskia/db/operations.py +1241 -0
  8. paskia/db/structs.py +148 -0
  9. paskia/fastapi/admin.py +456 -215
  10. paskia/fastapi/api.py +16 -15
  11. paskia/fastapi/authz.py +7 -2
  12. paskia/fastapi/mainapp.py +2 -1
  13. paskia/fastapi/remote.py +20 -20
  14. paskia/fastapi/reset.py +9 -10
  15. paskia/fastapi/user.py +10 -18
  16. paskia/fastapi/ws.py +22 -19
  17. paskia/frontend-build/auth/admin/index.html +3 -3
  18. paskia/frontend-build/auth/assets/AccessDenied-aTdCvz9k.js +8 -0
  19. paskia/frontend-build/auth/assets/admin-BeNu48FR.css +1 -0
  20. paskia/frontend-build/auth/assets/admin-tVs8oyLv.js +1 -0
  21. paskia/frontend-build/auth/assets/{auth-BU_O38k2.css → auth-BKX7shEe.css} +1 -1
  22. paskia/frontend-build/auth/assets/auth-Dk3q4pNS.js +1 -0
  23. paskia/frontend-build/auth/index.html +3 -3
  24. paskia/globals.py +7 -10
  25. paskia/migrate/__init__.py +274 -0
  26. paskia/migrate/sql.py +381 -0
  27. paskia/util/permutil.py +16 -5
  28. paskia/util/sessionutil.py +3 -2
  29. paskia/util/userinfo.py +12 -26
  30. {paskia-0.7.2.dist-info → paskia-0.8.0.dist-info}/METADATA +21 -25
  31. {paskia-0.7.2.dist-info → paskia-0.8.0.dist-info}/RECORD +33 -29
  32. {paskia-0.7.2.dist-info → paskia-0.8.0.dist-info}/entry_points.txt +1 -0
  33. paskia/db/sql.py +0 -1424
  34. paskia/frontend-build/auth/assets/AccessDenied-C-lL9vbN.js +0 -8
  35. paskia/frontend-build/auth/assets/admin-Cs6Mg773.css +0 -1
  36. paskia/frontend-build/auth/assets/admin-Df5_Damp.js +0 -1
  37. paskia/frontend-build/auth/assets/auth-Df3pjeSS.js +0 -1
  38. paskia/util/tokens.py +0 -44
  39. {paskia-0.7.2.dist-info → paskia-0.8.0.dist-info}/WHEEL +0 -0
paskia/db/structs.py ADDED
@@ -0,0 +1,148 @@
1
+ from datetime import datetime
2
+ from uuid import UUID
3
+
4
+ import msgspec
5
+
6
+
7
+ class Permission(msgspec.Struct, omit_defaults=True):
8
+ uuid: UUID # UUID primary key
9
+ scope: str # Permission scope identifier (e.g. "auth:admin", "myapp:write")
10
+ display_name: str
11
+ domain: str | None = None # If set, scopes permission to this domain
12
+
13
+
14
+ class Role(msgspec.Struct):
15
+ uuid: UUID
16
+ org_uuid: UUID
17
+ display_name: str
18
+ permissions: list[str] = [] # permission UUIDs this role grants
19
+
20
+
21
+ class Org(msgspec.Struct):
22
+ uuid: UUID
23
+ display_name: str
24
+ permissions: list[str] = [] # permission UUIDs this org can grant
25
+ roles: list[Role] = [] # roles belonging to this org
26
+
27
+
28
+ class User(msgspec.Struct):
29
+ uuid: UUID
30
+ display_name: str
31
+ role_uuid: UUID
32
+ created_at: datetime | None = None
33
+ last_seen: datetime | None = None
34
+ visits: int = 0
35
+
36
+
37
+ class Credential(msgspec.Struct):
38
+ uuid: UUID
39
+ credential_id: bytes # Long binary ID from the authenticator
40
+ user_uuid: UUID
41
+ aaguid: UUID
42
+ public_key: bytes
43
+ sign_count: int
44
+ created_at: datetime
45
+ last_used: datetime | None = None
46
+ last_verified: datetime | None = None
47
+
48
+
49
+ class Session(msgspec.Struct):
50
+ key: str
51
+ user_uuid: UUID
52
+ credential_uuid: UUID
53
+ host: str | None
54
+ ip: str | None
55
+ user_agent: str | None
56
+ expiry: datetime
57
+
58
+ def metadata(self) -> dict:
59
+ """Return session metadata for backwards compatibility."""
60
+ return {
61
+ "ip": self.ip,
62
+ "user_agent": self.user_agent,
63
+ "expiry": self.expiry.isoformat(),
64
+ }
65
+
66
+
67
+ class ResetToken(msgspec.Struct):
68
+ key: bytes
69
+ user_uuid: UUID
70
+ expiry: datetime
71
+ token_type: str
72
+
73
+
74
+ class SessionContext(msgspec.Struct):
75
+ session: Session
76
+ user: User
77
+ org: Org
78
+ role: Role
79
+ credential: Credential | None = None
80
+ permissions: list[Permission] | None = None
81
+
82
+
83
+ # -------------------------------------------------------------------------
84
+ # Internal storage types (different structure for efficient storage)
85
+ # -------------------------------------------------------------------------
86
+
87
+
88
+ class _PermissionData(msgspec.Struct, omit_defaults=True):
89
+ scope: str # Permission scope identifier
90
+ display_name: str
91
+ domain: str | None = None
92
+ orgs: dict[UUID, bool] = {} # org_uuid -> True (which orgs can grant this)
93
+
94
+
95
+ class _OrgData(msgspec.Struct):
96
+ display_name: str
97
+ created_at: datetime | None = None
98
+
99
+
100
+ class _RoleData(msgspec.Struct):
101
+ org: UUID
102
+ display_name: str
103
+ permissions: dict[UUID, bool] = {} # permission_uuid -> True
104
+
105
+
106
+ class _UserData(msgspec.Struct):
107
+ display_name: str
108
+ role: UUID
109
+ created_at: datetime
110
+ last_seen: datetime | None
111
+ visits: int
112
+
113
+
114
+ class _CredentialData(msgspec.Struct):
115
+ credential_id: bytes
116
+ user: UUID
117
+ aaguid: UUID
118
+ public_key: bytes
119
+ sign_count: int
120
+ created_at: datetime
121
+ last_used: datetime | None
122
+ last_verified: datetime | None
123
+
124
+
125
+ class _SessionData(msgspec.Struct):
126
+ user: UUID
127
+ credential: UUID
128
+ host: str | None
129
+ ip: str | None
130
+ user_agent: str | None
131
+ expiry: datetime
132
+
133
+
134
+ class _ResetTokenData(msgspec.Struct):
135
+ user: UUID
136
+ expiry: datetime
137
+ token_type: str
138
+
139
+
140
+ class _DatabaseData(msgspec.Struct, omit_defaults=True):
141
+ permissions: dict[UUID, _PermissionData]
142
+ orgs: dict[UUID, _OrgData]
143
+ roles: dict[UUID, _RoleData]
144
+ users: dict[UUID, _UserData]
145
+ credentials: dict[UUID, _CredentialData]
146
+ sessions: dict[str, _SessionData]
147
+ reset_tokens: dict[bytes, _ResetTokenData]
148
+ v: int = 0