auth

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

163:73e12d5a1124 Go to Latest

auth/context.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
1 package auth
3 import (
4 "html/template"
5 "io"
6 "log"
7 "net/url"
8 "time"
10 "code.secondbit.org/uuid.hg"
11 )
13 // Context wraps the different storage interfaces and should
14 // be used as the main point of interaction for the data storage
15 // layer.
16 type Context struct {
17 template *template.Template
18 loginURI *url.URL
19 clients clientStore
20 authCodes authorizationCodeStore
21 profiles profileStore
22 tokens tokenStore
23 sessions sessionStore
24 scopes scopeStore
25 config Config
26 }
28 // NewContext takes a Config instance and uses it to bootstrap a Context
29 // using the information provided in the Config variable.
30 func NewContext(config Config) (Context, error) {
31 if config.iterations == 0 {
32 return Context{}, ErrConfigNotInitialized
33 }
34 context := Context{
35 clients: config.ClientStore,
36 authCodes: config.AuthCodeStore,
37 profiles: config.ProfileStore,
38 tokens: config.TokenStore,
39 sessions: config.SessionStore,
40 scopes: config.ScopeStore,
41 template: config.Template,
42 config: config,
43 }
44 var err error
45 context.loginURI, err = url.Parse(config.LoginURI)
46 if err != nil {
47 log.Println(err)
48 return Context{}, ErrInvalidLoginURI
49 }
50 return context, nil
51 }
53 // Render uses the HTML templates associated with the Context to render the
54 // template specified by name to out using data to fill any template variables.
55 func (c Context) Render(out io.Writer, name string, data interface{}) {
56 if c.template == nil {
57 log.Println("No template set on Context, can't render anything!")
58 return
59 }
60 err := c.template.ExecuteTemplate(out, name, data)
61 if err != nil {
62 log.Println("Error executing template", name, ":", err)
63 }
64 }
66 // GetClient returns a single Client by its ID from the
67 // clientStore associated with the Context.
68 func (c Context) GetClient(id uuid.ID) (Client, error) {
69 if c.clients == nil {
70 return Client{}, ErrNoClientStore
71 }
72 return c.clients.getClient(id)
73 }
75 // SaveClient stores the passed Client in the clientStore
76 // associated with the Context.
77 func (c Context) SaveClient(client Client) error {
78 if c.clients == nil {
79 return ErrNoClientStore
80 }
81 return c.clients.saveClient(client)
82 }
84 // UpdateClient applies the specified ClientChange to the Client
85 // with the specified ID in the clientStore associated with the
86 // Context.
87 func (c Context) UpdateClient(id uuid.ID, change ClientChange) error {
88 if c.clients == nil {
89 return ErrNoClientStore
90 }
91 return c.clients.updateClient(id, change)
92 }
94 // ListClientsByOwner returns a slice of up to num Clients, starting at offset (inclusive)
95 // that have the specified OwnerID in the clientStore associated with the Context.
96 func (c Context) ListClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error) {
97 if c.clients == nil {
98 return []Client{}, ErrNoClientStore
99 }
100 return c.clients.listClientsByOwner(ownerID, num, offset)
101 }
103 // AddEndpoints stores the specified Endpoints in the clientStore associated with the Context.
104 func (c Context) AddEndpoints(endpoints []Endpoint) error {
105 if c.clients == nil {
106 return ErrNoClientStore
107 }
108 for pos, endpoint := range endpoints {
109 u, err := normalizeURIString(endpoint.URI)
110 if err != nil {
111 return err
112 }
113 endpoint.NormalizedURI = u
114 endpoints[pos] = endpoint
115 }
116 return c.clients.addEndpoints(endpoints)
117 }
119 // GetEndpoint retrieves the Endpoint with the specified ID from the clientStore associated
120 // with the Context, if and only if it belongs to the Client with the specified ID.
121 func (c Context) GetEndpoint(client, endpoint uuid.ID) (Endpoint, error) {
122 if c.clients == nil {
123 return Endpoint{}, ErrNoClientStore
124 }
125 return c.clients.getEndpoint(client, endpoint)
126 }
128 // RemoveEndpoint deletes the Endpoint with the specified ID from the clientStore associated
129 // with the Context, and disassociates the Endpoint from the specified Client.
130 func (c Context) RemoveEndpoint(client, endpoint uuid.ID) error {
131 if c.clients == nil {
132 return ErrNoClientStore
133 }
134 return c.clients.removeEndpoint(client, endpoint)
135 }
137 // CheckEndpoint finds Endpoints in the clientStore associated with the Context that belong
138 // to the Client specified by the passed ID and match the URI passed. URI matches must be
139 // performed according to RFC 3986 Section 6.
140 func (c Context) CheckEndpoint(client uuid.ID, URI string) (bool, error) {
141 if c.clients == nil {
142 return false, ErrNoClientStore
143 }
144 u, err := normalizeURIString(URI)
145 if err != nil {
146 return false, err
147 }
148 return c.clients.checkEndpoint(client, u)
149 }
151 // ListEndpoints finds Endpoints in the clientStore associated with the Context that belong
152 // to the Client specified by the passed ID. It returns up to num endpoints, starting at offset,
153 // exclusive.
154 func (c Context) ListEndpoints(client uuid.ID, num, offset int) ([]Endpoint, error) {
155 if c.clients == nil {
156 return []Endpoint{}, ErrNoClientStore
157 }
158 return c.clients.listEndpoints(client, num, offset)
159 }
161 // CountEndpoints returns the number of Endpoints the are associated with the Client specified by the
162 // passed ID in the clientStore associated with the Context.
163 func (c Context) CountEndpoints(client uuid.ID) (int64, error) {
164 if c.clients == nil {
165 return 0, ErrNoClientStore
166 }
167 return c.clients.countEndpoints(client)
168 }
170 // GetAuthorizationCode returns the AuthorizationCode specified by the provided code from the authorizationCodeStore associated with the
171 // Context.
172 func (c Context) GetAuthorizationCode(code string) (AuthorizationCode, error) {
173 if c.authCodes == nil {
174 return AuthorizationCode{}, ErrNoAuthorizationCodeStore
175 }
176 return c.authCodes.getAuthorizationCode(code)
177 }
179 // SaveAuthorizationCode stores the passed AuthorizationCode in the authorizationCodeStore associated with the Context.
180 func (c Context) SaveAuthorizationCode(authCode AuthorizationCode) error {
181 if c.authCodes == nil {
182 return ErrNoAuthorizationCodeStore
183 }
184 return c.authCodes.saveAuthorizationCode(authCode)
185 }
187 // DeleteAuthorizationCode removes the AuthorizationCode specified by the provided code from the authorizationCodeStore associated with
188 // the Context.
189 func (c Context) DeleteAuthorizationCode(code string) error {
190 if c.authCodes == nil {
191 return ErrNoAuthorizationCodeStore
192 }
193 return c.authCodes.deleteAuthorizationCode(code)
194 }
196 // DeleteAuthorizationCodesByProfileID removes the AuthorizationCodes associated with the Profile specified by the provided ID from the
197 // authorizationCodeStore associated with the Context.
198 func (c Context) DeleteAuthorizationCodesByProfileID(profileID uuid.ID) error {
199 if c.authCodes == nil {
200 return ErrNoAuthorizationCodeStore
201 }
202 return c.authCodes.deleteAuthorizationCodesByProfileID(profileID)
203 }
205 // UseAuthorizationCode marks the AuthorizationCode specified by the provided code as used in the authorizationCodeStore associated with
206 // the Context. Once an AuthorizationCode is marked as used, its Used property will be set to true when retrieved from the authorizationCodeStore.
207 func (c Context) UseAuthorizationCode(code string) error {
208 if c.authCodes == nil {
209 return ErrNoAuthorizationCodeStore
210 }
211 return c.authCodes.useAuthorizationCode(code)
212 }
214 // GetProfileByID returns the Profile specified by the provided ID from the profileStore associated with
215 // the Context.
216 func (c Context) GetProfileByID(id uuid.ID) (Profile, error) {
217 if c.profiles == nil {
218 return Profile{}, ErrNoProfileStore
219 }
220 return c.profiles.getProfileByID(id)
221 }
223 // GetProfileByLogin returns the Profile associated with the specified Login from the profileStore associated
224 // with the Context.
225 func (c Context) GetProfileByLogin(value string) (Profile, error) {
226 if c.profiles == nil {
227 return Profile{}, ErrNoProfileStore
228 }
229 return c.profiles.getProfileByLogin(value)
230 }
232 // SaveProfile inserts the passed Profile into the profileStore associated with the Context.
233 func (c Context) SaveProfile(profile Profile) error {
234 if c.profiles == nil {
235 return ErrNoProfileStore
236 }
237 return c.profiles.saveProfile(profile)
238 }
240 // UpdateProfile applies the supplied ProfileChange to the Profile that matches the specified ID
241 // in the profileStore associated with the Context.
242 func (c Context) UpdateProfile(id uuid.ID, change ProfileChange) error {
243 if c.profiles == nil {
244 return ErrNoProfileStore
245 }
246 return c.profiles.updateProfile(id, change)
247 }
249 // UpdateProfiles applies the supplied BulkProfileChange to every Profile that matches one of the
250 // specified IDs in the profileStore associated with the Context.
251 func (c Context) UpdateProfiles(ids []uuid.ID, change BulkProfileChange) error {
252 if c.profiles == nil {
253 return ErrNoProfileStore
254 }
255 return c.profiles.updateProfiles(ids, change)
256 }
258 // DeleteProfile removes the specified Profile from the profileStore associated with the Context.
259 func (c Context) DeleteProfile(id uuid.ID) error {
260 if c.profiles == nil {
261 return ErrNoProfileStore
262 }
263 return c.profiles.deleteProfile(id)
264 }
266 // AddLogin stores the passed Login in the profileStore associated with the Context. It also associates
267 // the newly-created Login with the Orofile in login.ProfileID.
268 func (c Context) AddLogin(login Login) error {
269 if c.profiles == nil {
270 return ErrNoProfileStore
271 }
272 return c.profiles.addLogin(login)
273 }
275 // RemoveLogin removes the specified Login from the profileStore associated with the Context, provided
276 // the Login has a ProfileID property that matches the profile ID passed in. It also disassociates the
277 // deleted Login from the Profile in login.ProfileID.
278 func (c Context) RemoveLogin(value string, profile uuid.ID) error {
279 if c.profiles == nil {
280 return ErrNoProfileStore
281 }
282 return c.profiles.removeLogin(value, profile)
283 }
285 // RemoveLoginsByProfile removes all Logins connected to the specified Profile in the profileStore
286 // associated with the Context and disassociates them from the Profile.
287 func (c Context) RemoveLoginsByProfile(profile uuid.ID) error {
288 if c.profiles == nil {
289 return ErrNoProfileStore
290 }
291 return c.profiles.removeLoginsByProfile(profile)
292 }
294 // RecordLoginUse sets the LastUsed property of the Login specified in the profileStore associated with
295 // the Context to the value passed in as when.
296 func (c Context) RecordLoginUse(value string, when time.Time) error {
297 if c.profiles == nil {
298 return ErrNoProfileStore
299 }
300 return c.profiles.recordLoginUse(value, when)
301 }
303 // ListLogins returns a slice of up to num Logins associated with the specified Profile from the profileStore
304 // associated with the Context, skipping offset Profiles.
305 func (c Context) ListLogins(profile uuid.ID, num, offset int) ([]Login, error) {
306 if c.profiles == nil {
307 return []Login{}, ErrNoProfileStore
308 }
309 return c.profiles.listLogins(profile, num, offset)
310 }
312 // GetToken returns the Token specified from the tokenStore associated with the Context.
313 // If refresh is true, the token input should be compared against the refresh tokens, not the
314 // access tokens.
315 func (c Context) GetToken(token string, refresh bool) (Token, error) {
316 if c.tokens == nil {
317 return Token{}, ErrNoTokenStore
318 }
319 return c.tokens.getToken(token, refresh)
320 }
322 // SaveToken stores the passed Token in the tokenStore associated with the Context.
323 func (c Context) SaveToken(token Token) error {
324 if c.tokens == nil {
325 return ErrNoTokenStore
326 }
327 return c.tokens.saveToken(token)
328 }
330 // RevokeToken revokes the Token identfied by the passed token string from the tokenStore associated
331 // with the context. If refresh is true, the token input should be compared against the refresh tokens,
332 // not the access tokens.
333 func (c Context) RevokeToken(token string, refresh bool) error {
334 if c.tokens == nil {
335 return ErrNoTokenStore
336 }
337 return c.tokens.revokeToken(token, refresh)
338 }
340 // RevokeTokensByProfileID revokes the Tokens associated with the Profile identified by the passed ID in
341 // the tokenStore associated with the Context.
342 func (c Context) RevokeTokensByProfileID(profileID uuid.ID) error {
343 if c.tokens == nil {
344 return ErrNoTokenStore
345 }
346 return c.tokens.revokeTokensByProfileID(profileID)
347 }
349 // GetTokensByProfileID returns a slice of up to num Tokens with a ProfileID that matches the specified
350 // profileID from the tokenStore associated with the Context, skipping offset Tokens.
351 func (c Context) GetTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) {
352 if c.tokens == nil {
353 return []Token{}, ErrNoTokenStore
354 }
355 return c.tokens.getTokensByProfileID(profileID, num, offset)
356 }
358 // CreateSession stores the passed Session in the sessionStore associated with the Context.
359 func (c Context) CreateSession(session Session) error {
360 if c.sessions == nil {
361 return ErrNoSessionStore
362 }
363 return c.sessions.createSession(session)
364 }
366 // GetSession returns the Session specified from the sessionStore associated with the Context.
367 func (c Context) GetSession(id string) (Session, error) {
368 if c.sessions == nil {
369 return Session{}, ErrNoSessionStore
370 }
371 return c.sessions.getSession(id)
372 }
374 // TerminateSession sets the Session identified by the passed ID as inactive in the sessionStore assocated
375 // with the Context.
376 func (c Context) TerminateSession(id string) error {
377 if c.sessions == nil {
378 return ErrNoSessionStore
379 }
380 return c.sessions.terminateSession(id)
381 }
383 // TerminateSessionsByProfile sets the Sessions associated with the passed Profile ID as inactive in the
384 // sessionStore associated with the Context.
385 func (c Context) TerminateSessionsByProfile(profile uuid.ID) error {
386 if c.sessions == nil {
387 return ErrNoSessionStore
388 }
389 return c.sessions.terminateSessionsByProfile(profile)
390 }
392 // RemoveSession removes the Session identified by the passed ID from the sessionStore associated with
393 // the Context.
394 func (c Context) RemoveSession(id string) error {
395 if c.sessions == nil {
396 return ErrNoSessionStore
397 }
398 return c.sessions.removeSession(id)
399 }
401 // ListSessions returns a slice of up to num Sessions from the sessionStore associated with the Context,
402 // ordered by the date they were created, descending. If before.IsZero() returns false, only Sessions
403 // that were created before that time will be returned. If profile is not nil, only Sessions belonging to
404 // that Profile will be returned.
405 func (c Context) ListSessions(profile uuid.ID, before time.Time, num int64) ([]Session, error) {
406 if c.sessions == nil {
407 return []Session{}, ErrNoSessionStore
408 }
409 return c.sessions.listSessions(profile, before, num)
410 }
412 func (c Context) CreateScopes(scopes []Scope) error {
413 if c.scopes == nil {
414 return ErrNoScopeStore
415 }
416 return c.scopes.createScopes(scopes)
417 }
419 func (c Context) GetScopes(ids []string) ([]Scope, error) {
420 if c.scopes == nil {
421 return []Scope{}, ErrNoScopeStore
422 }
423 return c.scopes.getScopes(ids)
424 }
426 func (c Context) UpdateScope(id string, change ScopeChange) error {
427 if c.scopes == nil {
428 return ErrNoScopeStore
429 }
430 return c.scopes.updateScope(id, change)
431 }
433 func (c Context) RemoveScopes(ids []string) error {
434 if c.scopes == nil {
435 return ErrNoScopeStore
436 }
437 return c.scopes.removeScopes(ids)
438 }
440 func (c Context) ListScopes() ([]Scope, error) {
441 if c.scopes == nil {
442 return []Scope{}, ErrNoScopeStore
443 }
444 return c.scopes.listScopes()
445 }