1package db
2
3import (
4 "crypto/rand"
5 "database/sql"
6 "encoding/hex"
7 "fmt"
8 "log"
9 "time"
10)
11
12type Registration struct {
13 Domain string
14 ByDid string
15 Created *time.Time
16 Registered *time.Time
17}
18
19func (r *Registration) Status() Status {
20 if r.Registered != nil {
21 return Registered
22 } else {
23 return Pending
24 }
25}
26
27type Status uint32
28
29const (
30 Registered Status = iota
31 Pending
32)
33
34// returns registered status, did of owner, error
35func RegistrationsByDid(e Execer, did string) ([]Registration, error) {
36 var registrations []Registration
37
38 rows, err := e.Query(`
39 select domain, did, created, registered from registrations
40 where did = ?
41 `, did)
42 if err != nil {
43 return nil, err
44 }
45
46 for rows.Next() {
47 var createdAt *string
48 var registeredAt *string
49 var registration Registration
50 err = rows.Scan(®istration.Domain, ®istration.ByDid, &createdAt, ®isteredAt)
51
52 if err != nil {
53 log.Println(err)
54 } else {
55 createdAtTime, _ := time.Parse(time.RFC3339, *createdAt)
56 var registeredAtTime *time.Time
57 if registeredAt != nil {
58 x, _ := time.Parse(time.RFC3339, *registeredAt)
59 registeredAtTime = &x
60 }
61
62 registration.Created = &createdAtTime
63 registration.Registered = registeredAtTime
64 registrations = append(registrations, registration)
65 }
66 }
67
68 return registrations, nil
69}
70
71// returns registered status, did of owner, error
72func RegistrationByDomain(e Execer, domain string) (*Registration, error) {
73 var createdAt *string
74 var registeredAt *string
75 var registration Registration
76
77 err := e.QueryRow(`
78 select domain, did, created, registered from registrations
79 where domain = ?
80 `, domain).Scan(®istration.Domain, ®istration.ByDid, &createdAt, ®isteredAt)
81
82 if err != nil {
83 if err == sql.ErrNoRows {
84 return nil, nil
85 } else {
86 return nil, err
87 }
88 }
89
90 createdAtTime, _ := time.Parse(time.RFC3339, *createdAt)
91 var registeredAtTime *time.Time
92 if registeredAt != nil {
93 x, _ := time.Parse(time.RFC3339, *registeredAt)
94 registeredAtTime = &x
95 }
96
97 registration.Created = &createdAtTime
98 registration.Registered = registeredAtTime
99
100 return ®istration, nil
101}
102
103func genSecret() string {
104 key := make([]byte, 32)
105 rand.Read(key)
106 return hex.EncodeToString(key)
107}
108
109func GenerateRegistrationKey(e Execer, domain, did string) (string, error) {
110 // sanity check: does this domain already have a registration?
111 reg, err := RegistrationByDomain(e, domain)
112 if err != nil {
113 return "", err
114 }
115
116 // registration is open
117 if reg != nil {
118 switch reg.Status() {
119 case Registered:
120 // already registered by `owner`
121 return "", fmt.Errorf("%s already registered by %s", domain, reg.ByDid)
122 case Pending:
123 // TODO: be loud about this
124 log.Printf("%s registered by %s, status pending", domain, reg.ByDid)
125 }
126 }
127
128 secret := genSecret()
129
130 _, err = e.Exec(`
131 insert into registrations (domain, did, secret)
132 values (?, ?, ?)
133 on conflict(domain) do update set did = excluded.did, secret = excluded.secret, created = excluded.created
134 `, domain, did, secret)
135
136 if err != nil {
137 return "", err
138 }
139
140 return secret, nil
141}
142
143func GetRegistrationKey(e Execer, domain string) (string, error) {
144 res := e.QueryRow(`select secret from registrations where domain = ?`, domain)
145
146 var secret string
147 err := res.Scan(&secret)
148 if err != nil || secret == "" {
149 return "", err
150 }
151
152 return secret, nil
153}
154
155func Register(e Execer, domain string) error {
156 _, err := e.Exec(`
157 update registrations
158 set registered = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
159 where domain = ?;
160 `, domain)
161
162 return err
163}