auth

Paddy 2015-07-18 Parent:8ecb60d29b0d

180:4b68bac597b7 Go to Latest

auth/profile_postgres.go

Update client to detect errors. The client doesn't treat non-200 responses as errors automatically, so we need to detect when the response.Errors property is set, and use that to return an error. To avoid the boilerplate and an extensive error system, I just wrapped them in an httpErrors type that implements the error interface. That way the errors can be returned, and callers can type-cast and interrogate them. I also updated the GetLogin function to return an auth.ErrLoginNotFound error when the httpErrors response indicates that's the reason the request failed.

History
paddy@148 1 package auth
paddy@148 2
paddy@148 3 import (
paddy@148 4 "time"
paddy@148 5
paddy@148 6 "code.secondbit.org/uuid.hg"
paddy@149 7 "github.com/lib/pq"
paddy@148 8 "github.com/secondbit/pan"
paddy@148 9 )
paddy@148 10
paddy@148 11 func (p Profile) GetSQLTableName() string {
paddy@148 12 return "profiles"
paddy@148 13 }
paddy@148 14
paddy@148 15 func (l Login) GetSQLTableName() string {
paddy@148 16 return "logins"
paddy@148 17 }
paddy@148 18
paddy@148 19 func (p *postgres) getProfileByIDSQL(id uuid.ID) *pan.Query {
paddy@148 20 var profile Profile
paddy@149 21 fields, _ := pan.GetFields(profile)
paddy@148 22 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(profile))
paddy@148 23 query.IncludeWhere()
paddy@161 24 query.Include(pan.GetUnquotedColumn(profile, "ID")+" = ?", id)
paddy@148 25 return query.FlushExpressions(" ")
paddy@148 26 }
paddy@148 27
paddy@148 28 func (p *postgres) getProfileByID(id uuid.ID) (Profile, error) {
paddy@148 29 query := p.getProfileByIDSQL(id)
paddy@148 30 rows, err := p.db.Query(query.String(), query.Args...)
paddy@148 31 if err != nil {
paddy@148 32 return Profile{}, err
paddy@148 33 }
paddy@148 34 var profile Profile
paddy@149 35 var found bool
paddy@148 36 for rows.Next() {
paddy@148 37 err := pan.Unmarshal(rows, &profile)
paddy@148 38 if err != nil {
paddy@148 39 return Profile{}, err
paddy@148 40 }
paddy@149 41 found = true
paddy@148 42 }
paddy@148 43 if err := rows.Err(); err != nil {
paddy@148 44 return Profile{}, err
paddy@148 45 }
paddy@149 46 if !found {
paddy@149 47 return profile, ErrProfileNotFound
paddy@149 48 }
paddy@148 49 return profile, nil
paddy@148 50 }
paddy@148 51
paddy@148 52 func (p *postgres) getProfileByLoginSQL(value string) *pan.Query {
paddy@148 53 var profile Profile
paddy@148 54 var login Login
paddy@149 55 fields, _ := pan.GetUnquotedAbsoluteFields(profile)
paddy@148 56 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(profile))
paddy@148 57 query.Include("INNER JOIN " + pan.GetTableName(login))
paddy@149 58 query.Include("ON " + pan.GetUnquotedAbsoluteColumn(profile, "ID") + " = " + pan.GetUnquotedAbsoluteColumn(login, "ProfileID"))
paddy@148 59 query.IncludeWhere()
paddy@161 60 query.Include(pan.GetUnquotedAbsoluteColumn(login, "Value")+" = ?", value)
paddy@148 61 return query.FlushExpressions(" ")
paddy@148 62 }
paddy@148 63
paddy@148 64 func (p *postgres) getProfileByLogin(value string) (Profile, error) {
paddy@148 65 query := p.getProfileByLoginSQL(value)
paddy@148 66 rows, err := p.db.Query(query.String(), query.Args...)
paddy@148 67 if err != nil {
paddy@148 68 return Profile{}, err
paddy@148 69 }
paddy@148 70 var profile Profile
paddy@149 71 var found bool
paddy@148 72 for rows.Next() {
paddy@148 73 err := pan.Unmarshal(rows, &profile)
paddy@148 74 if err != nil {
paddy@148 75 return Profile{}, err
paddy@148 76 }
paddy@149 77 found = true
paddy@148 78 }
paddy@148 79 if err := rows.Err(); err != nil {
paddy@148 80 return Profile{}, err
paddy@148 81 }
paddy@149 82 if !found {
paddy@149 83 return profile, ErrProfileNotFound
paddy@149 84 }
paddy@148 85 return profile, nil
paddy@148 86 }
paddy@148 87
paddy@148 88 func (p *postgres) saveProfileSQL(profile Profile) *pan.Query {
paddy@149 89 fields, values := pan.GetFields(profile)
paddy@148 90 query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(profile))
paddy@148 91 query.Include("(" + pan.QueryList(fields) + ")")
paddy@148 92 query.Include("VALUES")
paddy@148 93 query.Include("("+pan.VariableList(len(values))+")", values...)
paddy@148 94 return query.FlushExpressions(" ")
paddy@148 95 }
paddy@148 96
paddy@148 97 func (p *postgres) saveProfile(profile Profile) error {
paddy@148 98 query := p.saveProfileSQL(profile)
paddy@148 99 _, err := p.db.Exec(query.String(), query.Args...)
paddy@149 100 if e, ok := err.(*pq.Error); ok && e.Constraint == "profiles_pkey" {
paddy@149 101 err = ErrProfileAlreadyExists
paddy@149 102 }
paddy@148 103 return err
paddy@148 104 }
paddy@148 105
paddy@148 106 func (p *postgres) updateProfileSQL(id uuid.ID, change ProfileChange) *pan.Query {
paddy@148 107 var profile Profile
paddy@148 108 query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(profile)+" SET ")
paddy@149 109 query.IncludeIfNotNil(pan.GetUnquotedColumn(profile, "Name")+" = ?", change.Name)
paddy@149 110 query.IncludeIfNotNil(pan.GetUnquotedColumn(profile, "Passphrase")+" = ?", change.Passphrase)
paddy@149 111 query.IncludeIfNotNil(pan.GetUnquotedColumn(profile, "Iterations")+" = ?", change.Iterations)
paddy@149 112 query.IncludeIfNotNil(pan.GetUnquotedColumn(profile, "Salt")+" = ?", change.Salt)
paddy@149 113 query.IncludeIfNotNil(pan.GetUnquotedColumn(profile, "PassphraseScheme")+" = ?", change.PassphraseScheme)
paddy@149 114 query.IncludeIfNotNil(pan.GetUnquotedColumn(profile, "Compromised")+" = ?", change.Compromised)
paddy@149 115 query.IncludeIfNotNil(pan.GetUnquotedColumn(profile, "LockedUntil")+" = ?", change.LockedUntil)
paddy@149 116 query.IncludeIfNotNil(pan.GetUnquotedColumn(profile, "PassphraseReset")+" = ?", change.PassphraseReset)
paddy@149 117 query.IncludeIfNotNil(pan.GetUnquotedColumn(profile, "PassphraseResetCreated")+" = ?", change.PassphraseResetCreated)
paddy@149 118 query.IncludeIfNotNil(pan.GetUnquotedColumn(profile, "LastSeen")+" = ?", change.LastSeen)
paddy@148 119 query.FlushExpressions(", ")
paddy@148 120 query.IncludeWhere()
paddy@150 121 query.Include(pan.GetUnquotedColumn(profile, "ID")+" = ?", id)
paddy@148 122 return query.FlushExpressions(" ")
paddy@148 123 }
paddy@148 124
paddy@148 125 func (p *postgres) updateProfile(id uuid.ID, change ProfileChange) error {
paddy@149 126 if change.Empty() {
paddy@149 127 return nil
paddy@149 128 }
paddy@148 129 query := p.updateProfileSQL(id, change)
paddy@148 130 _, err := p.db.Exec(query.String(), query.Args...)
paddy@148 131 return err
paddy@148 132 }
paddy@148 133
paddy@148 134 func (p *postgres) updateProfilesSQL(ids []uuid.ID, change BulkProfileChange) *pan.Query {
paddy@149 135 if change.Empty() {
paddy@149 136 return nil
paddy@149 137 }
paddy@148 138 var profile Profile
paddy@148 139 query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(profile)+" SET ")
paddy@149 140 query.IncludeIfNotNil(pan.GetUnquotedColumn(profile, "Compromised")+" = ?", change.Compromised)
paddy@148 141 query.FlushExpressions(", ")
paddy@148 142 query.IncludeWhere()
paddy@148 143 intids := make([]interface{}, len(ids))
paddy@148 144 for pos, id := range ids {
paddy@148 145 intids[pos] = id
paddy@148 146 }
paddy@149 147 query.Include(pan.GetUnquotedColumn(profile, "ID")+" IN ("+pan.VariableList(len(ids))+")", intids...)
paddy@148 148 return query.FlushExpressions(" ")
paddy@148 149 }
paddy@148 150
paddy@148 151 func (p *postgres) updateProfiles(ids []uuid.ID, change BulkProfileChange) error {
paddy@148 152 query := p.updateProfilesSQL(ids, change)
paddy@148 153 _, err := p.db.Exec(query.String(), query.Args...)
paddy@148 154 return err
paddy@148 155 }
paddy@148 156
paddy@161 157 func (p *postgres) deleteProfileSQL(id uuid.ID) *pan.Query {
paddy@161 158 var profile Profile
paddy@161 159 query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(profile))
paddy@161 160 query.IncludeWhere()
paddy@161 161 query.Include(pan.GetUnquotedColumn(profile, "ID")+" = ?", id)
paddy@161 162 return query.FlushExpressions(" ")
paddy@161 163 }
paddy@161 164
paddy@161 165 func (p *postgres) deleteProfile(id uuid.ID) error {
paddy@161 166 query := p.deleteProfileSQL(id)
paddy@161 167 res, err := p.db.Exec(query.String(), query.Args...)
paddy@161 168 if err != nil {
paddy@161 169 return err
paddy@161 170 }
paddy@161 171 rows, err := res.RowsAffected()
paddy@161 172 if err != nil {
paddy@161 173 return err
paddy@161 174 }
paddy@161 175 if rows == 0 {
paddy@161 176 return ErrProfileNotFound
paddy@161 177 }
paddy@161 178 return nil
paddy@161 179 }
paddy@161 180
paddy@148 181 func (p *postgres) addLoginSQL(login Login) *pan.Query {
paddy@149 182 fields, values := pan.GetFields(login)
paddy@148 183 query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(login))
paddy@148 184 query.Include("(" + pan.QueryList(fields) + ")")
paddy@148 185 query.Include("VALUES")
paddy@148 186 query.Include("("+pan.VariableList(len(values))+")", values...)
paddy@148 187 return query.FlushExpressions(" ")
paddy@148 188 }
paddy@148 189
paddy@148 190 func (p *postgres) addLogin(login Login) error {
paddy@148 191 query := p.addLoginSQL(login)
paddy@148 192 _, err := p.db.Exec(query.String(), query.Args...)
paddy@149 193 if e, ok := err.(*pq.Error); ok && e.Constraint == "logins_pkey" {
paddy@149 194 return ErrLoginAlreadyExists
paddy@149 195 }
paddy@148 196 return err
paddy@148 197 }
paddy@148 198
paddy@160 199 func (p *postgres) removeLoginsByProfileSQL(profile uuid.ID) *pan.Query {
paddy@160 200 var login Login
paddy@160 201 query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(login))
paddy@160 202 query.IncludeWhere()
paddy@160 203 query.Include(pan.GetUnquotedColumn(login, "ProfileID")+" = ?", profile)
paddy@160 204 return query.FlushExpressions(" ")
paddy@160 205 }
paddy@160 206
paddy@160 207 func (p *postgres) removeLoginsByProfile(profile uuid.ID) error {
paddy@160 208 query := p.removeLoginsByProfileSQL(profile)
paddy@160 209 res, err := p.db.Exec(query.String(), query.Args...)
paddy@160 210 if err != nil {
paddy@160 211 return err
paddy@160 212 }
paddy@160 213 rows, err := res.RowsAffected()
paddy@160 214 if err != nil {
paddy@160 215 return err
paddy@160 216 }
paddy@160 217 if rows == 0 {
paddy@160 218 return ErrProfileNotFound
paddy@160 219 }
paddy@160 220 return nil
paddy@160 221 }
paddy@160 222
paddy@148 223 func (p *postgres) removeLoginSQL(value string, profile uuid.ID) *pan.Query {
paddy@148 224 var login Login
paddy@148 225 query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(login))
paddy@148 226 query.IncludeWhere()
paddy@150 227 query.Include(pan.GetUnquotedColumn(login, "Value")+" = ? AND "+pan.GetUnquotedColumn(login, "ProfileID")+" = ?", value, profile)
paddy@148 228 return query.FlushExpressions(" ")
paddy@148 229 }
paddy@148 230
paddy@148 231 func (p *postgres) removeLogin(value string, profile uuid.ID) error {
paddy@148 232 query := p.removeLoginSQL(value, profile)
paddy@149 233 res, err := p.db.Exec(query.String(), query.Args...)
paddy@149 234 if err != nil {
paddy@149 235 return err
paddy@149 236 }
paddy@149 237 rows, err := res.RowsAffected()
paddy@149 238 if err != nil {
paddy@149 239 return err
paddy@149 240 }
paddy@149 241 if rows == 0 {
paddy@149 242 return ErrLoginNotFound
paddy@149 243 }
paddy@149 244 return nil
paddy@148 245 }
paddy@148 246
paddy@148 247 func (p *postgres) recordLoginUseSQL(value string, when time.Time) *pan.Query {
paddy@148 248 var login Login
paddy@148 249 query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(login)+" SET ")
paddy@150 250 query.Include(pan.GetUnquotedColumn(login, "LastUsed")+" = ?", when)
paddy@148 251 query.IncludeWhere()
paddy@150 252 query.Include(pan.GetUnquotedColumn(login, "Value")+" = ?", value)
paddy@148 253 return query.FlushExpressions(" ")
paddy@148 254 }
paddy@148 255
paddy@148 256 func (p *postgres) recordLoginUse(value string, when time.Time) error {
paddy@148 257 query := p.recordLoginUseSQL(value, when)
paddy@148 258 _, err := p.db.Exec(query.String(), query.Args...)
paddy@148 259 return err
paddy@148 260 }
paddy@148 261
paddy@172 262 func (p *postgres) verifyLoginSQL(value, verification string) *pan.Query {
paddy@172 263 var login Login
paddy@172 264 query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(login)+" SET ")
paddy@172 265 query.Include(pan.GetUnquotedColumn(login, "Verified")+" = ?", true)
paddy@172 266 query.IncludeWhere()
paddy@172 267 query.FlushExpressions(" ")
paddy@172 268 query.Include(pan.GetUnquotedColumn(login, "Value")+" = ?", value)
paddy@172 269 query.Include(pan.GetUnquotedColumn(login, "Verification")+" = ?", verification)
paddy@172 270 return query.FlushExpressions(" AND ")
paddy@172 271 }
paddy@172 272
paddy@172 273 func (p *postgres) verifyLogin(value, verification string) error {
paddy@172 274 query := p.verifyLoginSQL(value, verification)
paddy@172 275 res, err := p.db.Exec(query.String(), query.Args...)
paddy@172 276 if err != nil {
paddy@172 277 return err
paddy@172 278 }
paddy@172 279 rows, err := res.RowsAffected()
paddy@172 280 if err != nil {
paddy@172 281 return err
paddy@172 282 }
paddy@172 283 if rows == 0 {
paddy@172 284 return ErrLoginVerificationInvalid
paddy@172 285 }
paddy@172 286 return nil
paddy@172 287 }
paddy@172 288
paddy@172 289 func (p *postgres) getLoginSQL(value string) *pan.Query {
paddy@172 290 var login Login
paddy@172 291 fields, _ := pan.GetFields(login)
paddy@172 292 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(login))
paddy@172 293 query.IncludeWhere()
paddy@172 294 query.Include(pan.GetUnquotedColumn(login, "Value")+" = ?", value)
paddy@172 295 return query.FlushExpressions(" ")
paddy@172 296 }
paddy@172 297
paddy@172 298 func (p *postgres) getLogin(value string) (Login, error) {
paddy@172 299 query := p.getLoginSQL(value)
paddy@172 300 rows, err := p.db.Query(query.String(), query.Args...)
paddy@172 301 if err != nil {
paddy@172 302 return Login{}, err
paddy@172 303 }
paddy@172 304 var login Login
paddy@172 305 var found bool
paddy@172 306 for rows.Next() {
paddy@172 307 err := pan.Unmarshal(rows, &login)
paddy@172 308 if err != nil {
paddy@172 309 return Login{}, err
paddy@172 310 }
paddy@172 311 found = true
paddy@172 312 }
paddy@172 313 if err := rows.Err(); err != nil {
paddy@172 314 return Login{}, err
paddy@172 315 }
paddy@172 316 if !found {
paddy@172 317 return login, ErrLoginNotFound
paddy@172 318 }
paddy@172 319 return login, nil
paddy@172 320 }
paddy@172 321
paddy@148 322 func (p *postgres) listLoginsSQL(profile uuid.ID, num, offset int) *pan.Query {
paddy@148 323 var login Login
paddy@149 324 fields, _ := pan.GetFields(login)
paddy@148 325 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(login))
paddy@148 326 query.IncludeWhere()
paddy@150 327 query.Include(pan.GetUnquotedColumn(login, "ProfileID")+" = ?", profile)
paddy@148 328 query.IncludeLimit(int64(num))
paddy@148 329 query.IncludeOffset(int64(offset))
paddy@148 330 return query.FlushExpressions(" ")
paddy@148 331 }
paddy@148 332
paddy@148 333 func (p *postgres) listLogins(profile uuid.ID, num, offset int) ([]Login, error) {
paddy@148 334 query := p.listLoginsSQL(profile, num, offset)
paddy@148 335 rows, err := p.db.Query(query.String(), query.Args...)
paddy@148 336 if err != nil {
paddy@148 337 return []Login{}, err
paddy@148 338 }
paddy@148 339 var logins []Login
paddy@148 340 for rows.Next() {
paddy@148 341 var login Login
paddy@148 342 err = pan.Unmarshal(rows, &login)
paddy@148 343 if err != nil {
paddy@148 344 return logins, err
paddy@148 345 }
paddy@148 346 logins = append(logins, login)
paddy@148 347 }
paddy@148 348 if err := rows.Err(); err != nil {
paddy@148 349 return logins, err
paddy@148 350 }
paddy@148 351 return logins, nil
paddy@148 352 }