auth

Paddy 2015-05-17 Parent:849f3820b164 Child:8ecb60d29b0d

169:37a42585660e Go to Latest

auth/profile_postgres.go

Create interfaces for login verification flow. We needed an interface that we could use to say "send the email to verify the user's login" so that we could verify the emails we have are actually valid. This implements an NSQ version that sends an email_verification event. We'll get listener implementations that pull these messages off NSQ and actually send the emails. This also implements, for testing purposes, a version that just echoes the Login Value and the Verification code to stdout.

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@148 262 func (p *postgres) listLoginsSQL(profile uuid.ID, num, offset int) *pan.Query {
paddy@148 263 var login Login
paddy@149 264 fields, _ := pan.GetFields(login)
paddy@148 265 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(login))
paddy@148 266 query.IncludeWhere()
paddy@150 267 query.Include(pan.GetUnquotedColumn(login, "ProfileID")+" = ?", profile)
paddy@148 268 query.IncludeLimit(int64(num))
paddy@148 269 query.IncludeOffset(int64(offset))
paddy@148 270 return query.FlushExpressions(" ")
paddy@148 271 }
paddy@148 272
paddy@148 273 func (p *postgres) listLogins(profile uuid.ID, num, offset int) ([]Login, error) {
paddy@148 274 query := p.listLoginsSQL(profile, num, offset)
paddy@148 275 rows, err := p.db.Query(query.String(), query.Args...)
paddy@148 276 if err != nil {
paddy@148 277 return []Login{}, err
paddy@148 278 }
paddy@148 279 var logins []Login
paddy@148 280 for rows.Next() {
paddy@148 281 var login Login
paddy@148 282 err = pan.Unmarshal(rows, &login)
paddy@148 283 if err != nil {
paddy@148 284 return logins, err
paddy@148 285 }
paddy@148 286 logins = append(logins, login)
paddy@148 287 }
paddy@148 288 if err := rows.Err(); err != nil {
paddy@148 289 return logins, err
paddy@148 290 }
paddy@148 291 return logins, nil
paddy@148 292 }