auth

Paddy 2015-04-19 Parent:6f473576c6ae Child:cf1aef6eb81f

163:73e12d5a1124 Go to Latest

auth/token_postgres.go

Use postgres arrays for scope associations. Use the new pqarrays library I wrote to store Scope associations for Tokens and AuthorizationCodes, instead of using our hacky and abstraction-breaking many-to-many code. We also created the authStore.deleteAuthorizationCodesByProfileID method, to clear out the AuthorizationCodes that belong to a Profile (used when the Profile is deleted). So we added the implementation for memstore and for our postgres store. Call Context.DeleteAuthorizationCodesByProfileID when deleting a Profile to clean up after it. Rename sortedScopes to Scopes, which we use pqarrays.StringArray's methods on to fulfill the sql.Scanner and driver.Valuer interfaces. This lets us store Scopes in postgres arrays. Create a stringsToScopes helper function that creates Scope objects, with their IDs filled by the strings specified. Update our GrantType.Validate function signature to return Scopes instead of []string. Create a Scopes.Strings() helper method that returns a []string of the IDs of the Scopes. Update our SQL init file to use the new postgres array definition, instead of the many-to-many definition.

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 func (t Token) GetSQLTableName() string {
paddy@155 11 return "tokens"
paddy@155 12 }
paddy@155 13
paddy@155 14 func (p *postgres) getTokenSQL(token string, refresh bool) *pan.Query {
paddy@155 15 var t Token
paddy@155 16 fields, _ := pan.GetFields(t)
paddy@155 17 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(t))
paddy@155 18 query.IncludeWhere()
paddy@155 19 if !refresh {
paddy@155 20 query.Include(pan.GetUnquotedColumn(t, "AccessToken")+" = ?", token)
paddy@155 21 } else {
paddy@155 22 query.Include(pan.GetUnquotedColumn(t, "RefreshToken")+" = ?", token)
paddy@155 23 }
paddy@155 24 return query.FlushExpressions(" ")
paddy@155 25 }
paddy@155 26
paddy@155 27 func (p *postgres) getToken(token string, refresh bool) (Token, error) {
paddy@155 28 query := p.getTokenSQL(token, refresh)
paddy@155 29 rows, err := p.db.Query(query.String(), query.Args...)
paddy@155 30 if err != nil {
paddy@155 31 return Token{}, err
paddy@155 32 }
paddy@155 33 var t Token
paddy@155 34 var found bool
paddy@155 35 for rows.Next() {
paddy@155 36 err := pan.Unmarshal(rows, &t)
paddy@155 37 if err != nil {
paddy@155 38 return t, err
paddy@155 39 }
paddy@155 40 found = true
paddy@155 41 }
paddy@155 42 if err = rows.Err(); err != nil {
paddy@155 43 return t, err
paddy@155 44 }
paddy@155 45 if !found {
paddy@155 46 return t, ErrTokenNotFound
paddy@155 47 }
paddy@155 48 return t, nil
paddy@155 49 }
paddy@155 50
paddy@155 51 func (p *postgres) saveTokenSQL(token Token) *pan.Query {
paddy@155 52 fields, values := pan.GetFields(token)
paddy@155 53 query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(token))
paddy@155 54 query.Include("(" + pan.QueryList(fields) + ")")
paddy@155 55 query.Include("VALUES")
paddy@155 56 query.Include("("+pan.VariableList(len(values))+")", values...)
paddy@155 57 return query.FlushExpressions(" ")
paddy@155 58 }
paddy@155 59
paddy@155 60 func (p *postgres) saveToken(token Token) error {
paddy@155 61 query := p.saveTokenSQL(token)
paddy@155 62 _, err := p.db.Exec(query.String(), query.Args...)
paddy@155 63 if e, ok := err.(*pq.Error); ok && e.Constraint == "tokens_pkey" {
paddy@155 64 err = ErrTokenAlreadyExists
paddy@155 65 }
paddy@155 66 if err != nil || len(token.Scopes) < 1 {
paddy@155 67 return err
paddy@155 68 }
paddy@155 69 return err
paddy@155 70 }
paddy@155 71
paddy@155 72 func (p *postgres) revokeTokenSQL(token string, refresh bool) *pan.Query {
paddy@155 73 var t Token
paddy@155 74 query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(t)+" SET ")
paddy@155 75 query.Include(pan.GetUnquotedColumn(t, "Revoked")+" = ?", true)
paddy@155 76 query.IncludeWhere()
paddy@155 77 if !refresh {
paddy@155 78 query.Include(pan.GetUnquotedColumn(t, "AccessToken")+" = ?", token)
paddy@155 79 } else {
paddy@155 80 query.Include(pan.GetUnquotedColumn(t, "RefreshToken")+" = ?", token)
paddy@155 81 }
paddy@155 82 return query.FlushExpressions(" ")
paddy@155 83 }
paddy@155 84
paddy@155 85 func (p *postgres) revokeToken(token string, refresh bool) error {
paddy@155 86 query := p.revokeTokenSQL(token, refresh)
paddy@155 87 res, err := p.db.Exec(query.String(), query.Args...)
paddy@155 88 if err != nil {
paddy@155 89 return err
paddy@155 90 }
paddy@155 91 rows, err := res.RowsAffected()
paddy@155 92 if err != nil {
paddy@155 93 return err
paddy@155 94 }
paddy@155 95 if rows == 0 {
paddy@155 96 return ErrTokenNotFound
paddy@155 97 }
paddy@155 98 return nil
paddy@155 99 }
paddy@155 100
paddy@162 101 func (p *postgres) revokeTokensByProfileIDSQL(profileID uuid.ID) *pan.Query {
paddy@162 102 var t Token
paddy@162 103 query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(t)+" SET ")
paddy@162 104 query.Include(pan.GetUnquotedColumn(t, "Revoked")+" = ?", true)
paddy@162 105 query.IncludeWhere()
paddy@162 106 query.Include(pan.GetUnquotedColumn(t, "ProfileID")+" = ?", profileID)
paddy@162 107 return query.FlushExpressions(" ")
paddy@162 108 }
paddy@162 109
paddy@162 110 func (p *postgres) revokeTokensByProfileID(profileID uuid.ID) error {
paddy@162 111 query := p.revokeTokensByProfileIDSQL(profileID)
paddy@162 112 res, err := p.db.Exec(query.String(), query.Args...)
paddy@162 113 if err != nil {
paddy@162 114 return err
paddy@162 115 }
paddy@162 116 rows, err := res.RowsAffected()
paddy@162 117 if err != nil {
paddy@162 118 return err
paddy@162 119 }
paddy@162 120 if rows == 0 {
paddy@162 121 return ErrProfileNotFound
paddy@162 122 }
paddy@162 123 return nil
paddy@162 124 }
paddy@162 125
paddy@155 126 func (p *postgres) getTokensByProfileIDSQL(profileID uuid.ID, num, offset int) *pan.Query {
paddy@155 127 var token Token
paddy@155 128 fields, _ := pan.GetFields(token)
paddy@155 129 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(token))
paddy@155 130 query.IncludeWhere()
paddy@155 131 query.Include(pan.GetUnquotedColumn(token, "ProfileID")+" = ?", profileID)
paddy@155 132 query.IncludeLimit(int64(num))
paddy@155 133 query.IncludeOffset(int64(offset))
paddy@155 134 return query.FlushExpressions(" ")
paddy@155 135 }
paddy@155 136
paddy@155 137 func (p *postgres) getTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) {
paddy@155 138 query := p.getTokensByProfileIDSQL(profileID, num, offset)
paddy@155 139 rows, err := p.db.Query(query.String(), query.Args...)
paddy@155 140 if err != nil {
paddy@155 141 return []Token{}, err
paddy@155 142 }
paddy@155 143 var tokens []Token
paddy@155 144 var tokenIDs []string
paddy@155 145 for rows.Next() {
paddy@155 146 var token Token
paddy@155 147 err = pan.Unmarshal(rows, &token)
paddy@155 148 if err != nil {
paddy@155 149 return tokens, err
paddy@155 150 }
paddy@155 151 tokens = append(tokens, token)
paddy@155 152 tokenIDs = append(tokenIDs, token.AccessToken)
paddy@155 153 }
paddy@155 154 if err = rows.Err(); err != nil {
paddy@155 155 return tokens, err
paddy@155 156 }
paddy@155 157 return tokens, nil
paddy@155 158 }