General cleanup and minor improvements. Registration and auth currently working.
This commit is contained in:
+47
-16
@@ -8,6 +8,7 @@ for managing users and credentials in a WebAuthn authentication system.
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
|
||||
import aiosqlite
|
||||
|
||||
@@ -18,7 +19,8 @@ SQL_CREATE_USERS = """
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
user_id BINARY(16) PRIMARY KEY NOT NULL,
|
||||
user_name TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_seen TIMESTAMP NULL
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -28,7 +30,7 @@ SQL_CREATE_CREDENTIALS = """
|
||||
user_id BINARY(16) NOT NULL,
|
||||
aaguid BINARY(16) NOT NULL,
|
||||
public_key BLOB NOT NULL,
|
||||
sign_count INTEGER DEFAULT 0,
|
||||
sign_count INTEGER NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_used TIMESTAMP NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE CASCADE
|
||||
@@ -40,7 +42,7 @@ SQL_GET_USER_BY_USER_ID = """
|
||||
"""
|
||||
|
||||
SQL_CREATE_USER = """
|
||||
INSERT INTO users (user_id, user_name) VALUES (?, ?)
|
||||
INSERT INTO users (user_id, user_name, created_at, last_seen) VALUES (?, ?, ?, ?)
|
||||
"""
|
||||
|
||||
SQL_STORE_CREDENTIAL = """
|
||||
@@ -75,19 +77,20 @@ class User:
|
||||
user_id: bytes = b""
|
||||
user_name: str = ""
|
||||
created_at: Optional[datetime] = None
|
||||
last_seen: Optional[datetime] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Credential:
|
||||
"""Credential data model."""
|
||||
|
||||
credential_id: bytes = b""
|
||||
user_id: bytes = b""
|
||||
aaguid: bytes = b""
|
||||
public_key: bytes = b""
|
||||
sign_count: int = 0
|
||||
created_at: Optional[datetime] = None
|
||||
last_used: Optional[datetime] = None
|
||||
credential_id: bytes
|
||||
user_id: bytes
|
||||
aaguid: UUID
|
||||
public_key: bytes
|
||||
sign_count: int
|
||||
created_at: datetime
|
||||
last_used: datetime | None = None
|
||||
|
||||
|
||||
class Database:
|
||||
@@ -109,15 +112,23 @@ class Database:
|
||||
async with conn.execute(SQL_GET_USER_BY_USER_ID, (user_id,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if row:
|
||||
return User(user_id=row[0], user_name=row[1], created_at=row[2])
|
||||
return User(
|
||||
user_id=row[0],
|
||||
user_name=row[1],
|
||||
created_at=row[2],
|
||||
last_seen=row[3],
|
||||
)
|
||||
raise ValueError("User not found")
|
||||
|
||||
async def create_user(self, user_id: bytes, user_name: str) -> User:
|
||||
async def create_user(self, user: User) -> User:
|
||||
"""Create a new user and return the User dataclass."""
|
||||
async with aiosqlite.connect(self.db_path) as conn:
|
||||
await conn.execute(SQL_CREATE_USER, (user_id, user_name))
|
||||
await conn.execute(
|
||||
SQL_CREATE_USER,
|
||||
(user.user_id, user.user_name, user.created_at, user.last_seen),
|
||||
)
|
||||
await conn.commit()
|
||||
return User(user_id=user_id, user_name=user_name)
|
||||
return user
|
||||
|
||||
async def store_credential(self, credential: Credential) -> None:
|
||||
"""Store a credential for a user."""
|
||||
@@ -127,7 +138,7 @@ class Database:
|
||||
(
|
||||
credential.credential_id,
|
||||
credential.user_id,
|
||||
credential.aaguid,
|
||||
credential.aaguid.bytes,
|
||||
credential.public_key,
|
||||
credential.sign_count,
|
||||
),
|
||||
@@ -145,7 +156,7 @@ class Database:
|
||||
return Credential(
|
||||
credential_id=row[0],
|
||||
user_id=row[1],
|
||||
aaguid=row[2],
|
||||
aaguid=UUID(bytes=row[2]), # Convert bytes to UUID
|
||||
public_key=row[3],
|
||||
sign_count=row[4],
|
||||
created_at=row[5],
|
||||
@@ -153,6 +164,13 @@ class Database:
|
||||
)
|
||||
raise ValueError("Credential not found")
|
||||
|
||||
async def get_credentials_by_user_id(self, user_id: bytes) -> list[bytes]:
|
||||
"""Get all credential IDs for a user."""
|
||||
async with aiosqlite.connect(self.db_path) as conn:
|
||||
async with conn.execute(SQL_GET_USER_CREDENTIALS, (user_id,)) as cursor:
|
||||
rows = await cursor.fetchall()
|
||||
return [row[0] for row in rows]
|
||||
|
||||
async def update_credential(self, credential: Credential) -> None:
|
||||
"""Update the sign count for a credential."""
|
||||
async with aiosqlite.connect(self.db_path) as conn:
|
||||
@@ -162,6 +180,19 @@ class Database:
|
||||
)
|
||||
await conn.commit()
|
||||
|
||||
async def update_user_last_seen(
|
||||
self, user_id: bytes, last_seen: datetime | None = None
|
||||
) -> None:
|
||||
"""Update the last_seen timestamp for a user."""
|
||||
if last_seen is None:
|
||||
last_seen = datetime.now()
|
||||
async with aiosqlite.connect(self.db_path) as conn:
|
||||
await conn.execute(
|
||||
"UPDATE users SET last_seen = ? WHERE user_id = ?",
|
||||
(last_seen, user_id),
|
||||
)
|
||||
await conn.commit()
|
||||
|
||||
|
||||
# Global database instance
|
||||
db = Database()
|
||||
|
||||
Reference in New Issue
Block a user