auth

Paddy 2015-04-11 Parent:762953f6a7f2 Child:73e12d5a1124

162:6f473576c6ae Go to Latest

auth/token_postgres.go

Clean up sessions and tokens after Profile is deleted. Add a terminateSessionsByProfile method to our sessionStore to mark Sessions associated with a Profile as inactive. Implement memstore and postgres implementations of the terminateSessionsByProfile method. Add a TerminateSessionsByProfile wrapper method to Context. Add a revokeTokensByProfileID method to our tokenStore to mark Tokens associated with a Profile as revoked. Implement memstore and postgres implementation of the revokeTokensByProfileID method. Add a RevokeTokensByProfileID wrapper method to Context. Call our RevokeTokensByProfileID and TerminateSessionsByProfile methods after a Profile is deleted, to clean up the Tokens and Sessions associated with it.

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