Blob


1 package mailmux
3 import (
4 "database/sql"
5 "errors"
6 "fmt"
8 _ "github.com/mattn/go-sqlite3"
9 )
11 // UserDB is an implementation of UserStore backed by a SQLite3 database.
12 // Users are fully authenticated after confirming ownership of their
13 // email address by supplying a matching ticket to a generated ticket file.
14 type UserDB struct {
15 *sql.DB
16 TicketDir string
17 }
19 // OpenUserDB opens the named user database file and ticket directory.
20 func OpenUserDB(name, dir string) (*UserDB, error) {
21 db, err := sql.Open("sqlite3", name)
22 if err != nil {
23 return nil, err
24 }
25 return &UserDB{db, dir}, db.Ping()
26 }
28 func (db *UserDB) Lookup(name string) (User, error) {
29 var u User
30 row := db.QueryRow("SELECT username, password FROM users WHERE username = ?", name)
31 if err := row.Scan(&u.name, &u.password); err != nil {
32 if errors.Is(err, sql.ErrNoRows) {
33 return User{}, fmt.Errorf("lookup %s: %w", name, ErrUnknownUser)
34 }
35 return User{}, fmt.Errorf("lookup %s: %w", name, err)
36 }
37 return u, nil
38 }
40 func (db *UserDB) add(username string, pw Password) error {
41 _, err := db.Exec("INSERT INTO users (username, password) VALUES (?, ?)", username, pw)
42 if err != nil {
43 return fmt.Errorf("add %s: %w", username, err)
44 }
45 if err := createTicket(db.TicketDir, username, pw); err != nil {
46 return fmt.Errorf("add %s: create ticket: %w", username, err)
47 }
48 return nil
49 }
51 func (db *UserDB) Change(name string, new Password) error {
52 _, err := db.Lookup(name)
53 if errors.Is(err, ErrUnknownUser) {
54 return db.add(name, new)
55 }
56 if err != nil {
57 return fmt.Errorf("change %s: %w", name, err)
58 }
60 _, err = db.Exec("UPDATE users SET password = ? WHERE username = ?", new, name)
61 if err != nil {
62 return fmt.Errorf("change %s: %w", name, err)
63 }
64 return nil
65 }
67 func (db *UserDB) Delete(name string) error {
68 _, err := db.Lookup(name)
69 if err != nil {
70 return fmt.Errorf("delete %s: %w", name, err)
71 }
72 _, err = db.Exec("DELETE FROM users WHERE username = ?", name)
73 if err != nil {
74 return fmt.Errorf("delete %s: %w", name, err)
75 }
76 return nil
77 }