package db import ( "crypto/rand" "database/sql" "encoding/hex" "errors" "time" ) // --- Sessions --- (mirrors crud_admin.go's session functions, parallel schema) func newMailboxSessionToken() string { b := make([]byte, 32) rand.Read(b) return hex.EncodeToString(b) } func (d *DB) CreateMailboxSession(mailboxID int64, mfaVerified bool, ttl time.Duration) (string, error) { token := newMailboxSessionToken() _, err := d.Exec(`INSERT INTO esrv_mailbox_sessions (token, mailbox_id, mfa_verified, expires_at) VALUES (?, ?, ?, ?)`, token, mailboxID, mfaVerified, time.Now().Add(ttl)) if err != nil { return "", err } return token, nil } func (d *DB) GetMailboxSession(token string) (*MailboxSession, error) { row := d.QueryRow(`SELECT token, mailbox_id, mfa_verified, created_at, expires_at FROM esrv_mailbox_sessions WHERE token = ?`, token) var s MailboxSession var createdAt, expiresAt string if err := row.Scan(&s.Token, &s.MailboxID, &s.MFAVerified, &createdAt, &expiresAt); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil } return nil, err } s.CreatedAt, _ = parseTime(createdAt) s.ExpiresAt, _ = parseTime(expiresAt) return &s, nil } func (d *DB) MarkMailboxSessionMFAVerified(token string) error { _, err := d.Exec(`UPDATE esrv_mailbox_sessions SET mfa_verified = 1 WHERE token = ?`, token) return err } func (d *DB) DeleteMailboxSession(token string) error { _, err := d.Exec(`DELETE FROM esrv_mailbox_sessions WHERE token = ?`, token) return err } // --- WebAuthn credentials --- (mirrors crud_admin.go, parallel schema) func (d *DB) ListMailboxWebAuthnCredentials(mailboxID int64) ([]MailboxWebAuthnCredential, error) { rows, err := d.Query(`SELECT id, mailbox_id, name, credential_id, credential_data, created_at FROM esrv_mailbox_webauthn_credentials WHERE mailbox_id = ? ORDER BY created_at`, mailboxID) if err != nil { return nil, err } defer rows.Close() var out []MailboxWebAuthnCredential for rows.Next() { var c MailboxWebAuthnCredential var createdAt string if err := rows.Scan(&c.ID, &c.MailboxID, &c.Name, &c.CredentialID, &c.CredentialData, &createdAt); err != nil { return nil, err } c.CreatedAt, _ = parseTime(createdAt) out = append(out, c) } return out, rows.Err() } func (d *DB) CreateMailboxWebAuthnCredential(mailboxID int64, name, credentialID, credentialData string) error { _, err := d.Exec(`INSERT INTO esrv_mailbox_webauthn_credentials (mailbox_id, name, credential_id, credential_data) VALUES (?, ?, ?, ?)`, mailboxID, name, credentialID, credentialData) return err } func (d *DB) DeleteMailboxWebAuthnCredential(id, mailboxID int64) error { _, err := d.Exec(`DELETE FROM esrv_mailbox_webauthn_credentials WHERE id = ? AND mailbox_id = ?`, id, mailboxID) return err } func (d *DB) CountMailboxWebAuthnCredentials(mailboxID int64) (int, error) { var n int err := d.QueryRow(`SELECT COUNT(*) FROM esrv_mailbox_webauthn_credentials WHERE mailbox_id = ?`, mailboxID).Scan(&n) return n, err }