feat: add SQLiteMagicLinkRepository with tests
This commit is contained in:
parent
bbe0dac8cb
commit
9f4914a922
2 changed files with 154 additions and 1 deletions
|
|
@ -2,7 +2,7 @@ from datetime import UTC, datetime
|
|||
|
||||
import aiosqlite
|
||||
|
||||
from fastapi_oidc_op.models import PasswordCredential, User, WebAuthnCredential
|
||||
from fastapi_oidc_op.models import MagicLink, PasswordCredential, User, WebAuthnCredential
|
||||
from fastapi_oidc_op.store.exceptions import DuplicateError
|
||||
|
||||
|
||||
|
|
@ -238,3 +238,54 @@ class SQLiteCredentialRepository:
|
|||
cursor = await self._db.execute("DELETE FROM password_credentials WHERE user_id = ?", (user_id,))
|
||||
await self._db.commit()
|
||||
return cursor.rowcount > 0
|
||||
|
||||
|
||||
class SQLiteMagicLinkRepository:
|
||||
def __init__(self, db: aiosqlite.Connection) -> None:
|
||||
self._db = db
|
||||
|
||||
def _row_to_magic_link(self, row: aiosqlite.Row) -> MagicLink:
|
||||
return MagicLink(
|
||||
token=row["token"],
|
||||
username=row["username"],
|
||||
expires_at=datetime.fromisoformat(row["expires_at"]),
|
||||
used=bool(row["used"]),
|
||||
created_by=row["created_by"],
|
||||
note=row["note"],
|
||||
)
|
||||
|
||||
async def create(self, link: MagicLink) -> MagicLink:
|
||||
try:
|
||||
await self._db.execute(
|
||||
"INSERT INTO magic_links (token, username, expires_at, used, created_by, note) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
(
|
||||
link.token,
|
||||
link.username,
|
||||
link.expires_at.isoformat(),
|
||||
int(link.used),
|
||||
link.created_by,
|
||||
link.note,
|
||||
),
|
||||
)
|
||||
await self._db.commit()
|
||||
except aiosqlite.IntegrityError as e:
|
||||
raise DuplicateError(str(e)) from e
|
||||
return link
|
||||
|
||||
async def get_by_token(self, token: str) -> MagicLink | None:
|
||||
async with self._db.execute("SELECT * FROM magic_links WHERE token = ?", (token,)) as cursor:
|
||||
row = await cursor.fetchone()
|
||||
if row is None:
|
||||
return None
|
||||
return self._row_to_magic_link(row)
|
||||
|
||||
async def mark_used(self, token: str) -> bool:
|
||||
cursor = await self._db.execute("UPDATE magic_links SET used = 1 WHERE token = ?", (token,))
|
||||
await self._db.commit()
|
||||
return cursor.rowcount > 0
|
||||
|
||||
async def delete_expired(self) -> int:
|
||||
now = datetime.now(UTC).isoformat()
|
||||
cursor = await self._db.execute("DELETE FROM magic_links WHERE expires_at < ? AND used = 0", (now,))
|
||||
await self._db.commit()
|
||||
return cursor.rowcount
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue