auth

Paddy 2015-07-15 Parent:cf1aef6eb81f

178:0a2c3d677161 Go to Latest

auth/client_postgres.go

Update to use a generic event emitter. Rather can creating a purpose-built event emitter for each and every event we need to emit (I'm looking at you, login verification event) which is _downright silly_, we're now using a generic event publisher that's based on saying "HEY A MODEL UPDATED". This means we need to change all our setup code in authd to use events.NewNSQPublisher or events.NewStdoutPublisher instead of our homegrown solutions. Which also means updating our config to take an events.Publisher instead of our LoginVerificationNotifier (blergh). Our Context also now uses an events.Publisher instead of a LoginVerificationNotifier. Party all around! We also replaced our SendLoginVerification helper method on Context with a SendModelEvent helper method on Context, which is just a light wrapper around events.PublishModelEvent. Of course, all this means we need to update our email_verification listener to listen to the correct channel (based on the model we want updates about) and filter down to a Created action or our new custom action for "the customer wants their verification resent", which I'm OK making a special case and not generic, because c'mon. But we had a subtle change to all our constants, some of which are unofficial constants now. I'm unsure how I feel about this. We also updated our email_verification listener so that we're unmarshalling to a custom loginEvent, which is just an events.Event that overwrites the Data property to be an auth.Login instance. This is to make sure we don't need to wrangle a map[string]interface{}, which is no fun. I'm also OK with special-casing like this, because it's 1) a tiny amount of code, 2) properly utilising composition, and 3) the only way I can think of to cleanly accomplish what I want. I also added a note about GetLogin's deficient handling of logins, namely that it doesn't recognise admins and return Verification codes to them, which would be a useful property for internal tools to take advantage of. Ah well. I updated the Profile and Login implementations so they're now event.Model instances, mainly by just exporting some strings from them through getters that will let us automatically build an Event from them. This lets us use the PublishModelEvent helper. I updated our CreateProfileHandler to properly mangle the login Verification property, and to fire off the ActionCreated events for the new Login and the new Profile. I updated our GetLoginHandler and UpdateLoginHandler to properly mangle the loginVerification property. God that's annoying. :-/ You'll note I didn't start publishing the events.ActionUpdated or events.ActionDeleted events for Profiles or Logins yet, and didn't bother publishing any events for literally any other type. That's because I'm a lazy piece of crap and will end up publishing them when I absolutely have to. Part of that is because if a channel isn't created/being read for a topic, the messages will just stack up in NSQ, and I don't want that. But mostly I'm lazy. Finally, I got to delete the entire profile_verification.go file, because we're no longer special-casing that. Hooray!

History
1 package auth
3 import (
4 "code.secondbit.org/uuid.hg"
5 "github.com/lib/pq"
6 "github.com/secondbit/pan"
7 )
9 func (c Client) GetSQLTableName() string {
10 return "clients"
11 }
13 func (e Endpoint) GetSQLTableName() string {
14 return "endpoints"
15 }
17 func (p *postgres) getClientSQL(id uuid.ID) *pan.Query {
18 var client Client
19 fields, _ := pan.GetFields(client)
20 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(client))
21 query.IncludeWhere()
22 query.Include(pan.GetUnquotedColumn(client, "ID")+" = ? AND "+pan.GetUnquotedColumn(client, "Deleted")+" = ?", id, false)
23 return query.FlushExpressions(" ")
24 }
26 func (p *postgres) getClient(id uuid.ID) (Client, error) {
27 query := p.getClientSQL(id)
28 rows, err := p.db.Query(query.String(), query.Args...)
29 if err != nil {
30 return Client{}, err
31 }
32 var client Client
33 var found bool
34 for rows.Next() {
35 err := pan.Unmarshal(rows, &client)
36 if err != nil {
37 return client, err
38 }
39 found = true
40 }
41 if err = rows.Err(); err != nil {
42 return client, err
43 }
44 if !found {
45 return client, ErrClientNotFound
46 }
47 return client, nil
48 }
50 func (p *postgres) saveClientSQL(client Client) *pan.Query {
51 fields, values := pan.GetFields(client)
52 query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(client))
53 query.Include("(" + pan.QueryList(fields) + ")")
54 query.Include("VALUES")
55 query.Include("("+pan.VariableList(len(values))+")", values...)
56 return query.FlushExpressions(" ")
57 }
59 func (p *postgres) saveClient(client Client) error {
60 query := p.saveClientSQL(client)
61 _, err := p.db.Exec(query.String(), query.Args...)
62 if e, ok := err.(*pq.Error); ok && e.Constraint == "clients_pkey" {
63 err = ErrClientAlreadyExists
64 }
65 return err
66 }
68 func (p *postgres) updateClientSQL(id uuid.ID, change ClientChange) *pan.Query {
69 var client Client
70 query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(client)+" SET ")
71 query.IncludeIfNotNil(pan.GetUnquotedColumn(client, "Secret")+" = ?", change.Secret)
72 query.IncludeIfNotNil(pan.GetUnquotedColumn(client, "OwnerID")+" = ?", change.OwnerID)
73 query.IncludeIfNotNil(pan.GetUnquotedColumn(client, "Name")+" = ?", change.Name)
74 query.IncludeIfNotNil(pan.GetUnquotedColumn(client, "Logo")+" = ?", change.Logo)
75 query.IncludeIfNotNil(pan.GetUnquotedColumn(client, "Website")+" = ?", change.Website)
76 query.IncludeIfNotNil(pan.GetUnquotedColumn(client, "Deleted")+" = ?", change.Deleted)
77 query.FlushExpressions(", ")
78 query.IncludeWhere()
79 query.Include(pan.GetUnquotedColumn(client, "ID")+" = ?", id)
80 return query.FlushExpressions(" ")
81 }
83 func (p *postgres) updateClient(id uuid.ID, change ClientChange) error {
84 if change.Empty() {
85 return nil
86 }
87 query := p.updateClientSQL(id, change)
88 _, err := p.db.Exec(query.String(), query.Args...)
89 return err
90 }
92 func (p *postgres) listClientsByOwnerSQL(ownerID uuid.ID, num, offset int) *pan.Query {
93 var client Client
94 fields, _ := pan.GetFields(client)
95 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(client))
96 query.IncludeWhere()
97 query.Include(pan.GetUnquotedColumn(client, "OwnerID")+" = ? AND "+pan.GetUnquotedColumn(client, "Deleted")+" = ?", ownerID, false)
98 if num > 0 {
99 query.IncludeLimit(int64(num))
100 }
101 query.IncludeOffset(int64(offset))
102 return query.FlushExpressions(" ")
103 }
105 func (p *postgres) listClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error) {
106 query := p.listClientsByOwnerSQL(ownerID, num, offset)
107 rows, err := p.db.Query(query.String(), query.Args...)
108 if err != nil {
109 return []Client{}, err
110 }
111 var clients []Client
112 for rows.Next() {
113 var client Client
114 err = pan.Unmarshal(rows, &client)
115 if err != nil {
116 return clients, err
117 }
118 clients = append(clients, client)
119 }
120 if err = rows.Err(); err != nil {
121 return clients, err
122 }
123 return clients, nil
124 }
126 func (p *postgres) deleteClientsByOwnerSQL(ownerID uuid.ID) *pan.Query {
127 var client Client
128 query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(client)+" SET")
129 query.Include(pan.GetUnquotedColumn(client, "Deleted")+"= ?", true)
130 query.IncludeWhere()
131 query.Include(pan.GetUnquotedColumn(client, "OwnerID")+" = ?", ownerID)
132 return query.FlushExpressions(" ")
133 }
135 func (p *postgres) deleteClientsByOwner(ownerID uuid.ID) error {
136 query := p.deleteClientsByOwnerSQL(ownerID)
137 _, err := p.db.Exec(query.String(), query.Args...)
138 return err
139 }
141 func (p *postgres) addEndpointsSQL(endpoints []Endpoint) *pan.Query {
142 fields, _ := pan.GetFields(endpoints[0])
143 query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(endpoints[0]))
144 query.Include("(" + pan.QueryList(fields) + ")")
145 query.Include("VALUES")
146 query.FlushExpressions(" ")
147 for _, endpoint := range endpoints {
148 _, values := pan.GetFields(endpoint)
149 query.Include("("+pan.VariableList(len(values))+")", values...)
150 }
151 return query.FlushExpressions(", ")
152 }
154 func (p *postgres) addEndpoints(endpoints []Endpoint) error {
155 if len(endpoints) < 1 {
156 return nil
157 }
158 query := p.addEndpointsSQL(endpoints)
159 _, err := p.db.Exec(query.String(), query.Args...)
160 if e, ok := err.(*pq.Error); ok && e.Constraint == "endpoints_pkey" {
161 return ErrEndpointAlreadyExists
162 }
163 return err
164 }
166 func (p *postgres) removeEndpointSQL(client, endpoint uuid.ID) *pan.Query {
167 var e Endpoint
168 query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(e))
169 query.IncludeWhere()
170 query.Include(pan.GetUnquotedColumn(e, "ID")+" = ? AND "+pan.GetUnquotedColumn(e, "ClientID")+" = ?", endpoint, client)
171 return query.FlushExpressions(" ")
172 }
174 func (p *postgres) removeEndpoint(client, endpoint uuid.ID) error {
175 query := p.removeEndpointSQL(client, endpoint)
176 res, err := p.db.Exec(query.String(), query.Args...)
177 if err != nil {
178 return err
179 }
180 rows, err := res.RowsAffected()
181 if err != nil {
182 return err
183 }
184 if rows == 0 {
185 return ErrEndpointNotFound
186 }
187 return nil
188 }
190 func (p *postgres) removeEndpointsByClientIDSQL(client uuid.ID) *pan.Query {
191 var e Endpoint
192 query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(e))
193 query.IncludeWhere()
194 query.Include(pan.GetUnquotedColumn(e, "ClientID")+" = ?", client)
195 return query.FlushExpressions(" ")
196 }
198 func (p *postgres) removeEndpointsByClientID(client uuid.ID) error {
199 query := p.removeEndpointsByClientIDSQL(client)
200 res, err := p.db.Exec(query.String(), query.Args...)
201 if err != nil {
202 return err
203 }
204 rows, err := res.RowsAffected()
205 if err != nil {
206 return err
207 }
208 if rows == 0 {
209 return ErrClientNotFound
210 }
211 return nil
212 }
214 func (p *postgres) getEndpointSQL(client, endpoint uuid.ID) *pan.Query {
215 var e Endpoint
216 fields, _ := pan.GetFields(e)
217 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(e))
218 query.IncludeWhere()
219 query.FlushExpressions(" ")
220 query.Include(pan.GetUnquotedColumn(e, "ID")+" = ?", endpoint)
221 query.Include(pan.GetUnquotedColumn(e, "ClientID")+" = ?", client)
222 return query.FlushExpressions(" AND ")
223 }
225 func (p *postgres) getEndpoint(client, endpoint uuid.ID) (Endpoint, error) {
226 query := p.getEndpointSQL(client, endpoint)
227 rows, err := p.db.Query(query.String(), query.Args...)
228 if err != nil {
229 return Endpoint{}, err
230 }
231 var e Endpoint
232 var found bool
233 for rows.Next() {
234 err := pan.Unmarshal(rows, &e)
235 if err != nil {
236 return e, err
237 }
238 found = true
239 }
240 if err = rows.Err(); err != nil {
241 return e, err
242 }
243 if !found {
244 return e, ErrEndpointNotFound
245 }
246 return e, nil
247 }
249 func (p *postgres) checkEndpointSQL(client uuid.ID, endpoint string) *pan.Query {
250 var e Endpoint
251 fields, _ := pan.GetFields(e)
252 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(e))
253 query.IncludeWhere()
254 query.FlushExpressions(" ")
255 query.Include(pan.GetUnquotedColumn(e, "ClientID")+" = ?", client)
256 query.Include(pan.GetUnquotedColumn(e, "NormalizedURI")+" = ?", endpoint)
257 return query.FlushExpressions(" AND ")
258 }
260 func (p *postgres) checkEndpoint(client uuid.ID, endpoint string) (bool, error) {
261 query := p.checkEndpointSQL(client, endpoint)
262 rows, err := p.db.Query(query.String(), query.Args...)
263 if err != nil {
264 return false, err
265 }
266 var found bool
267 for rows.Next() {
268 found = true
269 }
270 if err = rows.Err(); err != nil {
271 return found, err
272 }
273 return found, nil
274 }
276 func (p *postgres) listEndpointsSQL(client uuid.ID, num, offset int) *pan.Query {
277 var endpoint Endpoint
278 fields, _ := pan.GetFields(endpoint)
279 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(endpoint))
280 query.IncludeWhere()
281 query.Include(pan.GetUnquotedColumn(endpoint, "ClientID")+" = ?", client)
282 query.IncludeLimit(int64(num))
283 query.IncludeOffset(int64(offset))
284 return query.FlushExpressions(" ")
285 }
287 func (p *postgres) listEndpoints(client uuid.ID, num, offset int) ([]Endpoint, error) {
288 query := p.listEndpointsSQL(client, num, offset)
289 rows, err := p.db.Query(query.String(), query.Args...)
290 if err != nil {
291 return []Endpoint{}, err
292 }
293 var endpoints []Endpoint
294 for rows.Next() {
295 var endpoint Endpoint
296 err = pan.Unmarshal(rows, &endpoint)
297 if err != nil {
298 return endpoints, err
299 }
300 endpoints = append(endpoints, endpoint)
301 }
302 if err = rows.Err(); err != nil {
303 return endpoints, err
304 }
305 return endpoints, nil
306 }
308 func (p *postgres) countEndpointsSQL(client uuid.ID) *pan.Query {
309 var endpoint Endpoint
310 query := pan.New(pan.POSTGRES, "SELECT COUNT(*) FROM "+pan.GetTableName(endpoint))
311 query.IncludeWhere()
312 query.Include(pan.GetUnquotedColumn(endpoint, "ClientID")+" = ?", client)
313 return query.FlushExpressions(" ")
314 }
316 func (p *postgres) countEndpoints(client uuid.ID) (int64, error) {
317 query := p.countEndpointsSQL(client)
318 rows, err := p.db.Query(query.String(), query.Args...)
319 if err != nil {
320 return 0, err
321 }
322 var results int64
323 for rows.Next() {
324 err = pan.Unmarshal(rows, &results)
325 if err != nil {
326 return results, err
327 }
328 }
329 if err = rows.Err(); err != nil {
330 return results, err
331 }
332 return results, nil
333 }