auth

Paddy 2015-04-11 Parent:762953f6a7f2 Child:6f473576c6ae

161:849f3820b164 Go to Latest

auth/token_postgres.go

Stop soft-deleting Profiles and actually delete them. The information we're storing in Profiles isn't unique enough that we should go through the hassle we're going through to soft-delete it. Add a deleteProfile method to our profileStore, and implement it for our postgres and memstore implementations. Add a DeleteProfile wrapper for our Context. Remove the Deleted property from the Profile type and the ProfileChange type, and update references to it. Stop cleaning up after our Profile in the UpdateProfileHandler, because there's no longer any way to delete the Profile from the UpdateProfileHandler. Update our get/list* methods so they don't filter on the non-existent Deleted property anymore. Update our SQL schema definition to not include the deleted column. Update our profile tests to use the DeleteProfile method and stop comparing the no-longer-existing Deleted property.

History
paddy@155 1 package auth
paddy@155 2
paddy@155 3 import (
paddy@155 4 "code.secondbit.org/uuid.hg"
paddy@155 5
paddy@155 6 "github.com/lib/pq"
paddy@155 7 "github.com/secondbit/pan"
paddy@155 8 )
paddy@155 9
paddy@155 10 type tokenScope struct {
paddy@155 11 Token string
paddy@155 12 Scope string
paddy@155 13 }
paddy@155 14
paddy@155 15 func (t tokenScope) GetSQLTableName() string {
paddy@155 16 return "scopes_tokens"
paddy@155 17 }
paddy@155 18
paddy@155 19 func (t Token) GetSQLTableName() string {
paddy@155 20 return "tokens"
paddy@155 21 }
paddy@155 22
paddy@155 23 func (p *postgres) getTokenSQL(token string, refresh bool) *pan.Query {
paddy@155 24 var t Token
paddy@155 25 fields, _ := pan.GetFields(t)
paddy@155 26 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(t))
paddy@155 27 query.IncludeWhere()
paddy@155 28 if !refresh {
paddy@155 29 query.Include(pan.GetUnquotedColumn(t, "AccessToken")+" = ?", token)
paddy@155 30 } else {
paddy@155 31 query.Include(pan.GetUnquotedColumn(t, "RefreshToken")+" = ?", token)
paddy@155 32 }
paddy@155 33 return query.FlushExpressions(" ")
paddy@155 34 }
paddy@155 35
paddy@155 36 func (p *postgres) getToken(token string, refresh bool) (Token, error) {
paddy@155 37 query := p.getTokenSQL(token, refresh)
paddy@155 38 rows, err := p.db.Query(query.String(), query.Args...)
paddy@155 39 if err != nil {
paddy@155 40 return Token{}, err
paddy@155 41 }
paddy@155 42 var t Token
paddy@155 43 var found bool
paddy@155 44 for rows.Next() {
paddy@155 45 err := pan.Unmarshal(rows, &t)
paddy@155 46 if err != nil {
paddy@155 47 return t, err
paddy@155 48 }
paddy@155 49 found = true
paddy@155 50 }
paddy@155 51 if err = rows.Err(); err != nil {
paddy@155 52 return t, err
paddy@155 53 }
paddy@155 54 if !found {
paddy@155 55 return t, ErrTokenNotFound
paddy@155 56 }
paddy@155 57 query = p.getTokenScopesSQL([]string{t.AccessToken})
paddy@155 58 rows, err = p.db.Query(query.String(), query.Args...)
paddy@155 59 if err != nil {
paddy@155 60 return t, err
paddy@155 61 }
paddy@155 62 for rows.Next() {
paddy@155 63 var ts tokenScope
paddy@155 64 err = pan.Unmarshal(rows, &ts)
paddy@155 65 if err != nil {
paddy@155 66 return t, err
paddy@155 67 }
paddy@155 68 t.Scopes = append(t.Scopes, ts.Scope)
paddy@155 69 }
paddy@155 70 if err = rows.Err(); err != nil {
paddy@155 71 return t, err
paddy@155 72 }
paddy@155 73 return t, nil
paddy@155 74 }
paddy@155 75
paddy@155 76 func (p *postgres) saveTokenSQL(token Token) *pan.Query {
paddy@155 77 fields, values := pan.GetFields(token)
paddy@155 78 query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(token))
paddy@155 79 query.Include("(" + pan.QueryList(fields) + ")")
paddy@155 80 query.Include("VALUES")
paddy@155 81 query.Include("("+pan.VariableList(len(values))+")", values...)
paddy@155 82 return query.FlushExpressions(" ")
paddy@155 83 }
paddy@155 84
paddy@155 85 func (p *postgres) saveTokenScopesSQL(ts []tokenScope) *pan.Query {
paddy@155 86 fields, _ := pan.GetFields(ts[0])
paddy@155 87 query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(ts[0]))
paddy@155 88 query.Include("(" + pan.QueryList(fields) + ")")
paddy@155 89 query.Include("VALUES")
paddy@155 90 query.FlushExpressions(" ")
paddy@155 91 for _, t := range ts {
paddy@155 92 _, values := pan.GetFields(t)
paddy@155 93 query.Include("("+pan.VariableList(len(values))+")", values...)
paddy@155 94 }
paddy@155 95 return query.FlushExpressions(", ")
paddy@155 96 }
paddy@155 97
paddy@155 98 func (p *postgres) saveToken(token Token) error {
paddy@155 99 query := p.saveTokenSQL(token)
paddy@155 100 _, err := p.db.Exec(query.String(), query.Args...)
paddy@155 101 if e, ok := err.(*pq.Error); ok && e.Constraint == "tokens_pkey" {
paddy@155 102 err = ErrTokenAlreadyExists
paddy@155 103 }
paddy@155 104 if err != nil || len(token.Scopes) < 1 {
paddy@155 105 return err
paddy@155 106 }
paddy@155 107 var ts []tokenScope
paddy@155 108 for _, scope := range token.Scopes {
paddy@155 109 ts = append(ts, tokenScope{Token: token.AccessToken, Scope: scope})
paddy@155 110 }
paddy@155 111 query = p.saveTokenScopesSQL(ts)
paddy@155 112 _, err = p.db.Exec(query.String(), query.Args...)
paddy@155 113 return err
paddy@155 114 }
paddy@155 115
paddy@155 116 func (p *postgres) revokeTokenSQL(token string, refresh bool) *pan.Query {
paddy@155 117 var t Token
paddy@155 118 query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(t)+" SET ")
paddy@155 119 query.Include(pan.GetUnquotedColumn(t, "Revoked")+" = ?", true)
paddy@155 120 query.IncludeWhere()
paddy@155 121 if !refresh {
paddy@155 122 query.Include(pan.GetUnquotedColumn(t, "AccessToken")+" = ?", token)
paddy@155 123 } else {
paddy@155 124 query.Include(pan.GetUnquotedColumn(t, "RefreshToken")+" = ?", token)
paddy@155 125 }
paddy@155 126 return query.FlushExpressions(" ")
paddy@155 127 }
paddy@155 128
paddy@155 129 func (p *postgres) revokeToken(token string, refresh bool) error {
paddy@155 130 query := p.revokeTokenSQL(token, refresh)
paddy@155 131 res, err := p.db.Exec(query.String(), query.Args...)
paddy@155 132 if err != nil {
paddy@155 133 return err
paddy@155 134 }
paddy@155 135 rows, err := res.RowsAffected()
paddy@155 136 if err != nil {
paddy@155 137 return err
paddy@155 138 }
paddy@155 139 if rows == 0 {
paddy@155 140 return ErrTokenNotFound
paddy@155 141 }
paddy@155 142 return nil
paddy@155 143 }
paddy@155 144
paddy@155 145 func (p *postgres) getTokensByProfileIDSQL(profileID uuid.ID, num, offset int) *pan.Query {
paddy@155 146 var token Token
paddy@155 147 fields, _ := pan.GetFields(token)
paddy@155 148 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(token))
paddy@155 149 query.IncludeWhere()
paddy@155 150 query.Include(pan.GetUnquotedColumn(token, "ProfileID")+" = ?", profileID)
paddy@155 151 query.IncludeLimit(int64(num))
paddy@155 152 query.IncludeOffset(int64(offset))
paddy@155 153 return query.FlushExpressions(" ")
paddy@155 154 }
paddy@155 155
paddy@155 156 func (p *postgres) getTokenScopesSQL(tokens []string) *pan.Query {
paddy@155 157 var t tokenScope
paddy@155 158 fields, _ := pan.GetFields(t)
paddy@155 159 tokensI := make([]interface{}, len(tokens))
paddy@155 160 for pos, token := range tokens {
paddy@155 161 tokensI[pos] = token
paddy@155 162 }
paddy@155 163 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(t))
paddy@155 164 query.IncludeWhere()
paddy@155 165 query.Include(pan.GetUnquotedColumn(t, "Token")+" IN ("+pan.VariableList(len(tokensI))+")", tokensI...)
paddy@155 166 return query.FlushExpressions(" ")
paddy@155 167 }
paddy@155 168
paddy@155 169 func (p *postgres) getTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) {
paddy@155 170 query := p.getTokensByProfileIDSQL(profileID, num, offset)
paddy@155 171 rows, err := p.db.Query(query.String(), query.Args...)
paddy@155 172 if err != nil {
paddy@155 173 return []Token{}, err
paddy@155 174 }
paddy@155 175 var tokens []Token
paddy@155 176 var tokenIDs []string
paddy@155 177 for rows.Next() {
paddy@155 178 var token Token
paddy@155 179 err = pan.Unmarshal(rows, &token)
paddy@155 180 if err != nil {
paddy@155 181 return tokens, err
paddy@155 182 }
paddy@155 183 tokens = append(tokens, token)
paddy@155 184 tokenIDs = append(tokenIDs, token.AccessToken)
paddy@155 185 }
paddy@155 186 if err = rows.Err(); err != nil {
paddy@155 187 return tokens, err
paddy@155 188 }
paddy@155 189 if len(tokenIDs) < 1 {
paddy@155 190 return tokens, nil
paddy@155 191 }
paddy@155 192 scopes := map[string][]string{}
paddy@155 193 query = p.getTokenScopesSQL(tokenIDs)
paddy@155 194 rows, err = p.db.Query(query.String(), query.Args...)
paddy@155 195 if err != nil {
paddy@155 196 return tokens, err
paddy@155 197 }
paddy@155 198 for rows.Next() {
paddy@155 199 var t tokenScope
paddy@155 200 err = pan.Unmarshal(rows, &t)
paddy@155 201 if err != nil {
paddy@155 202 return tokens, err
paddy@155 203 }
paddy@155 204 scopes[t.Token] = append(scopes[t.Token], t.Scope)
paddy@155 205 }
paddy@155 206 if err = rows.Err(); err != nil {
paddy@155 207 return tokens, err
paddy@155 208 }
paddy@155 209 for pos, token := range tokens {
paddy@155 210 token.Scopes = scopes[token.AccessToken]
paddy@155 211 tokens[pos] = token
paddy@155 212 }
paddy@155 213 return tokens, nil
paddy@155 214 }