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