From c2ea01e6d9d11f3074616a295feb2b10d549b5c4 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Tue, 10 Feb 2026 21:45:09 +0000 Subject: [PATCH] Use strictly same now timestamp over a transaction, even for UUIDv7s generated. --- paskia/db/operations.py | 23 ++++++++++++++--------- paskia/db/structs.py | 17 +++++++++++------ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/paskia/db/operations.py b/paskia/db/operations.py index ab92134..c22239f 100644 --- a/paskia/db/operations.py +++ b/paskia/db/operations.py @@ -169,13 +169,14 @@ def create_org(org: Org, *, ctx: SessionContext | None = None) -> None: """ if org.uuid in _db.orgs: raise ValueError(f"Organization {org.uuid} already exists") + now = datetime.now(UTC) with _db.transaction("admin:create_org", ctx): - new_org = Org.create(display_name=org.display_name) + new_org = Org.create(display_name=org.display_name, created_at=now) new_org.uuid = org.uuid _db.orgs[org.uuid] = new_org # Create Administration role with org admin permission - admin_role_uuid = uuid7.create() + admin_role_uuid = uuid7.create(now) # Find the auth:org:admin permission UUID org_admin_perm_uuid = None for pid, p in _db.permissions.items(): @@ -690,6 +691,11 @@ def create_credential_session( if display_name: _db.users[user_uuid].display_name = display_name + # Align credential timestamps with transaction time + credential.created_at = now + credential.last_used = now + credential.last_verified = now + # Create credential _db.credentials[credential.uuid] = credential @@ -747,11 +753,12 @@ def bootstrap( ) # Generate UUIDs upfront - perm_admin_uuid = uuid7.create() - perm_org_admin_uuid = uuid7.create() - org_uuid = uuid7.create() - role_uuid = uuid7.create() - user_uuid = uuid7.create() + now = datetime.now(UTC) + perm_admin_uuid = uuid7.create(now) + perm_org_admin_uuid = uuid7.create(now) + org_uuid = uuid7.create(now) + role_uuid = uuid7.create(now) + user_uuid = uuid7.create(now) # Set reset token expiry (passphrase generated by ResetToken.create) if reset_expiry is None: @@ -759,8 +766,6 @@ def bootstrap( reset_expiry = reset_expires() - now = datetime.now(UTC) - with _db.transaction("bootstrap"): # Create auth:admin permission perm_admin = Permission( diff --git a/paskia/db/structs.py b/paskia/db/structs.py index 0bebf6c..8edceb6 100644 --- a/paskia/db/structs.py +++ b/paskia/db/structs.py @@ -54,14 +54,16 @@ class Permission(msgspec.Struct, dict=True, omit_defaults=True): scope: str, display_name: str, domain: str | None = None, + created_at: datetime | None = None, ) -> Permission: """Create a new Permission with auto-generated uuid7.""" + now = created_at or datetime.now(UTC) perm = cls( scope=scope, display_name=display_name, domain=domain, ) - perm.uuid = uuid7.create() + perm.uuid = uuid7.create(now) return perm @@ -85,10 +87,11 @@ class Org(msgspec.Struct, dict=True): return [p for p in db.data().permissions.values() if self.uuid in p.orgs] @classmethod - def create(cls, display_name: str) -> Org: + def create(cls, display_name: str, created_at: datetime | None = None) -> Org: """Create a new Org with auto-generated uuid7.""" + now = created_at or datetime.now(UTC) org = cls(display_name=display_name) - org.uuid = uuid7.create() + org.uuid = uuid7.create(now) return org @@ -138,15 +141,17 @@ class Role(msgspec.Struct, dict=True, omit_defaults=True): org: UUID | Org, display_name: str, permissions: set[UUID] | None = None, + created_at: datetime | None = None, ) -> Role: """Create a new Role with auto-generated uuid7.""" + now = created_at or datetime.now(UTC) org_uuid = org if isinstance(org, UUID) else org.uuid role = cls( org_uuid=org_uuid, display_name=display_name, permissions={p: True for p in (permissions or set())}, ) - role.uuid = uuid7.create() + role.uuid = uuid7.create(now) return role @@ -309,7 +314,7 @@ class Session(msgspec.Struct, dict=True): "expiry": self.expiry.isoformat(), } - def store(self, now: datetime) -> None: + def store(self, last_seen: datetime) -> None: """Store this session in the database and record a visit. Updates user.last_seen and user.visits. Must be called inside @@ -317,7 +322,7 @@ class Session(msgspec.Struct, dict=True): """ _data = db.data() _data.sessions[self.key] = self - _data.users[self.user_uuid].last_seen = now + _data.users[self.user_uuid].last_seen = last_seen _data.users[self.user_uuid].visits += 1 @classmethod