this repo has no description
1package db 2 3import ( 4 "context" 5 "database/sql" 6 "fmt" 7 "log" 8 9 "github.com/google/uuid" 10 _ "github.com/mattn/go-sqlite3" 11 "golang.org/x/crypto/bcrypt" 12) 13 14type DB struct { 15 db *sql.DB 16} 17 18func Make(dbPath string) (*DB, error) { 19 db, err := sql.Open("sqlite3", dbPath) 20 if err != nil { 21 return nil, err 22 } 23 _, err = db.Exec(` 24 create table if not exists registrations ( 25 id integer primary key autoincrement, 26 domain text not null unique, 27 did text not null, 28 secret text not null, 29 created integer default (strftime('%s', 'now')), 30 registered integer 31 ); 32 `) 33 if err != nil { 34 return nil, err 35 } 36 return &DB{db: db}, nil 37} 38 39type RegStatus uint32 40 41const ( 42 Registered RegStatus = iota 43 Unregistered 44 Pending 45) 46 47// returns registered status, did of owner, error 48func (d *DB) RegistrationStatus(domain string) (RegStatus, string, error) { 49 var registeredBy string 50 var registratedAt *uint64 51 err := d.db.QueryRow(` 52 select did, registered from registrations 53 where domain = ? 54 `, domain).Scan(&registeredBy, &registratedAt) 55 if err != nil { 56 if err == sql.ErrNoRows { 57 return Unregistered, "", nil 58 } else { 59 return Unregistered, "", err 60 } 61 } 62 63 if registratedAt != nil { 64 return Registered, registeredBy, nil 65 } else { 66 return Pending, registeredBy, nil 67 } 68} 69 70func (d *DB) GenerateRegistrationKey(domain, did string) (string, error) { 71 // sanity check: does this domain already have a registration? 72 status, owner, err := d.RegistrationStatus(domain) 73 if err != nil { 74 return "", err 75 } 76 switch status { 77 case Registered: 78 // already registered by `owner` 79 return "", fmt.Errorf("%s already registered by %s", domain, owner) 80 case Pending: 81 log.Printf("%s registered by %s, status pending", domain, owner) 82 // TODO: provide a warning here, and allow the current user to overwrite 83 // the registration, this prevents users from registering domains that they 84 // do not own 85 default: 86 // ok, we can register this domain 87 } 88 89 secret := uuid.New().String() 90 hashedSecret, err := bcrypt.GenerateFromPassword([]byte(secret), 3) 91 92 if err != nil { 93 return "", err 94 } 95 96 _, err = d.db.Exec(` 97 insert into registrations (domain, did, secret) 98 values (?, ?, ?) 99 on conflict(domain) do update set did = excluded.did, secret = excluded.secret 100 `, domain, did, fmt.Sprintf("%x", hashedSecret)) 101 102 if err != nil { 103 return "", err 104 } 105 106 return secret, nil 107} 108 109func (d *DB) Register(domain, secret string) error { 110 ctx := context.TODO() 111 112 tx, err := d.db.BeginTx(ctx, nil) 113 if err != nil { 114 return err 115 } 116 117 res := tx.QueryRow(`select secret from registrations where domain = ?`, domain) 118 119 var storedSecret string 120 err = res.Scan(&storedSecret) 121 if err != nil { 122 return err 123 } 124 125 err = bcrypt.CompareHashAndPassword([]byte(storedSecret), []byte(secret)) 126 if err != nil { 127 return err 128 } 129 130 _, err = tx.Exec(` 131 update registrations 132 set registered = strftime('%s', 'now') 133 where domain = ?; 134 `, domain) 135 if err != nil { 136 return err 137 } 138 139 err = tx.Commit() 140 if err != nil { 141 return err 142 } 143 144 return nil 145}