auth
auth/context.go
Send events when logins are verified. Add an ActionLoginVerified constant to use as the action when a login has been verified. On second thought, this should probably just be "verified", huh? Then we can reuse it across models. Oops. We also added a call to send a login verified event to NSQ when the login is verified.
1 package auth
3 import (
4 "html/template"
5 "io"
6 "log"
7 "net/url"
8 "time"
10 "code.secondbit.org/events.hg"
11 "code.secondbit.org/uuid.hg"
12 )
14 // Context wraps the different storage interfaces and should
15 // be used as the main point of interaction for the data storage
16 // layer.
17 type Context struct {
18 template *template.Template
19 loginURI *url.URL
20 clients clientStore
21 authCodes authorizationCodeStore
22 profiles profileStore
23 tokens tokenStore
24 sessions sessionStore
25 scopes scopeStore
26 eventsPublisher events.Publisher
27 config Config
28 }
30 // NewContext takes a Config instance and uses it to bootstrap a Context
31 // using the information provided in the Config variable.
32 func NewContext(config Config) (Context, error) {
33 if config.iterations == 0 {
34 return Context{}, ErrConfigNotInitialized
35 }
36 context := Context{
37 clients: config.ClientStore,
38 authCodes: config.AuthCodeStore,
39 profiles: config.ProfileStore,
40 tokens: config.TokenStore,
41 sessions: config.SessionStore,
42 scopes: config.ScopeStore,
43 eventsPublisher: config.EventsPublisher,
44 template: config.Template,
45 config: config,
46 }
47 var err error
48 context.loginURI, err = url.Parse(config.LoginURI)
49 if err != nil {
50 log.Println(err)
51 return Context{}, ErrInvalidLoginURI
52 }
53 return context, nil
54 }
56 // Render uses the HTML templates associated with the Context to render the
57 // template specified by name to out using data to fill any template variables.
58 func (c Context) Render(out io.Writer, name string, data interface{}) {
59 if c.template == nil {
60 log.Println("No template set on Context, can't render anything!")
61 return
62 }
63 err := c.template.ExecuteTemplate(out, name, data)
64 if err != nil {
65 log.Println("Error executing template", name, ":", err)
66 }
67 }
69 // GetClient returns a single Client by its ID from the
70 // clientStore associated with the Context.
71 func (c Context) GetClient(id uuid.ID) (Client, error) {
72 if c.clients == nil {
73 return Client{}, ErrNoClientStore
74 }
75 return c.clients.getClient(id)
76 }
78 // SaveClient stores the passed Client in the clientStore
79 // associated with the Context.
80 func (c Context) SaveClient(client Client) error {
81 if c.clients == nil {
82 return ErrNoClientStore
83 }
84 return c.clients.saveClient(client)
85 }
87 // UpdateClient applies the specified ClientChange to the Client
88 // with the specified ID in the clientStore associated with the
89 // Context.
90 func (c Context) UpdateClient(id uuid.ID, change ClientChange) error {
91 if c.clients == nil {
92 return ErrNoClientStore
93 }
94 return c.clients.updateClient(id, change)
95 }
97 // ListClientsByOwner returns a slice of up to num Clients, starting at offset (inclusive)
98 // that have the specified OwnerID in the clientStore associated with the Context.
99 func (c Context) ListClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error) {
100 if c.clients == nil {
101 return []Client{}, ErrNoClientStore
102 }
103 return c.clients.listClientsByOwner(ownerID, num, offset)
104 }
106 func (c Context) DeleteClientsByOwner(ownerID uuid.ID) error {
107 if c.clients == nil {
108 return ErrNoClientStore
109 }
110 return c.clients.deleteClientsByOwner(ownerID)
111 }
113 // AddEndpoints stores the specified Endpoints in the clientStore associated with the Context.
114 func (c Context) AddEndpoints(endpoints []Endpoint) error {
115 if c.clients == nil {
116 return ErrNoClientStore
117 }
118 for pos, endpoint := range endpoints {
119 u, err := normalizeURIString(endpoint.URI)
120 if err != nil {
121 return err
122 }
123 endpoint.NormalizedURI = u
124 endpoints[pos] = endpoint
125 }
126 return c.clients.addEndpoints(endpoints)
127 }
129 // GetEndpoint retrieves the Endpoint with the specified ID from the clientStore associated
130 // with the Context, if and only if it belongs to the Client with the specified ID.
131 func (c Context) GetEndpoint(client, endpoint uuid.ID) (Endpoint, error) {
132 if c.clients == nil {
133 return Endpoint{}, ErrNoClientStore
134 }
135 return c.clients.getEndpoint(client, endpoint)
136 }
138 // RemoveEndpoint deletes the Endpoint with the specified ID from the clientStore associated
139 // with the Context, and disassociates the Endpoint from the specified Client.
140 func (c Context) RemoveEndpoint(client, endpoint uuid.ID) error {
141 if c.clients == nil {
142 return ErrNoClientStore
143 }
144 return c.clients.removeEndpoint(client, endpoint)
145 }
147 // CheckEndpoint finds Endpoints in the clientStore associated with the Context that belong
148 // to the Client specified by the passed ID and match the URI passed. URI matches must be
149 // performed according to RFC 3986 Section 6.
150 func (c Context) CheckEndpoint(client uuid.ID, URI string) (bool, error) {
151 if c.clients == nil {
152 return false, ErrNoClientStore
153 }
154 u, err := normalizeURIString(URI)
155 if err != nil {
156 return false, err
157 }
158 return c.clients.checkEndpoint(client, u)
159 }
161 // ListEndpoints finds Endpoints in the clientStore associated with the Context that belong
162 // to the Client specified by the passed ID. It returns up to num endpoints, starting at offset,
163 // exclusive.
164 func (c Context) ListEndpoints(client uuid.ID, num, offset int) ([]Endpoint, error) {
165 if c.clients == nil {
166 return []Endpoint{}, ErrNoClientStore
167 }
168 return c.clients.listEndpoints(client, num, offset)
169 }
171 func (c Context) RemoveEndpointsByClientID(client uuid.ID) error {
172 if c.clients == nil {
173 return ErrNoClientStore
174 }
175 return c.clients.removeEndpointsByClientID(client)
176 }
178 // CountEndpoints returns the number of Endpoints the are associated with the Client specified by the
179 // passed ID in the clientStore associated with the Context.
180 func (c Context) CountEndpoints(client uuid.ID) (int64, error) {
181 if c.clients == nil {
182 return 0, ErrNoClientStore
183 }
184 return c.clients.countEndpoints(client)
185 }
187 // GetAuthorizationCode returns the AuthorizationCode specified by the provided code from the authorizationCodeStore associated with the
188 // Context.
189 func (c Context) GetAuthorizationCode(code string) (AuthorizationCode, error) {
190 if c.authCodes == nil {
191 return AuthorizationCode{}, ErrNoAuthorizationCodeStore
192 }
193 return c.authCodes.getAuthorizationCode(code)
194 }
196 // SaveAuthorizationCode stores the passed AuthorizationCode in the authorizationCodeStore associated with the Context.
197 func (c Context) SaveAuthorizationCode(authCode AuthorizationCode) error {
198 if c.authCodes == nil {
199 return ErrNoAuthorizationCodeStore
200 }
201 return c.authCodes.saveAuthorizationCode(authCode)
202 }
204 // DeleteAuthorizationCode removes the AuthorizationCode specified by the provided code from the authorizationCodeStore associated with
205 // the Context.
206 func (c Context) DeleteAuthorizationCode(code string) error {
207 if c.authCodes == nil {
208 return ErrNoAuthorizationCodeStore
209 }
210 return c.authCodes.deleteAuthorizationCode(code)
211 }
213 // DeleteAuthorizationCodesByProfileID removes the AuthorizationCodes associated with the Profile specified by the provided ID from the
214 // authorizationCodeStore associated with the Context.
215 func (c Context) DeleteAuthorizationCodesByProfileID(profileID uuid.ID) error {
216 if c.authCodes == nil {
217 return ErrNoAuthorizationCodeStore
218 }
219 return c.authCodes.deleteAuthorizationCodesByProfileID(profileID)
220 }
222 func (c Context) DeleteAuthorizationCodesByClientID(clientID uuid.ID) error {
223 if c.authCodes == nil {
224 return ErrNoAuthorizationCodeStore
225 }
226 return c.authCodes.deleteAuthorizationCodesByClientID(clientID)
227 }
229 // UseAuthorizationCode marks the AuthorizationCode specified by the provided code as used in the authorizationCodeStore associated with
230 // the Context. Once an AuthorizationCode is marked as used, its Used property will be set to true when retrieved from the authorizationCodeStore.
231 func (c Context) UseAuthorizationCode(code string) error {
232 if c.authCodes == nil {
233 return ErrNoAuthorizationCodeStore
234 }
235 return c.authCodes.useAuthorizationCode(code)
236 }
238 // GetProfileByID returns the Profile specified by the provided ID from the profileStore associated with
239 // the Context.
240 func (c Context) GetProfileByID(id uuid.ID) (Profile, error) {
241 if c.profiles == nil {
242 return Profile{}, ErrNoProfileStore
243 }
244 return c.profiles.getProfileByID(id)
245 }
247 // GetProfileByLogin returns the Profile associated with the specified Login from the profileStore associated
248 // with the Context.
249 func (c Context) GetProfileByLogin(value string) (Profile, error) {
250 if c.profiles == nil {
251 return Profile{}, ErrNoProfileStore
252 }
253 return c.profiles.getProfileByLogin(value)
254 }
256 // SaveProfile inserts the passed Profile into the profileStore associated with the Context.
257 func (c Context) SaveProfile(profile Profile) error {
258 if c.profiles == nil {
259 return ErrNoProfileStore
260 }
261 return c.profiles.saveProfile(profile)
262 }
264 // UpdateProfile applies the supplied ProfileChange to the Profile that matches the specified ID
265 // in the profileStore associated with the Context.
266 func (c Context) UpdateProfile(id uuid.ID, change ProfileChange) error {
267 if c.profiles == nil {
268 return ErrNoProfileStore
269 }
270 return c.profiles.updateProfile(id, change)
271 }
273 // UpdateProfiles applies the supplied BulkProfileChange to every Profile that matches one of the
274 // specified IDs in the profileStore associated with the Context.
275 func (c Context) UpdateProfiles(ids []uuid.ID, change BulkProfileChange) error {
276 if c.profiles == nil {
277 return ErrNoProfileStore
278 }
279 return c.profiles.updateProfiles(ids, change)
280 }
282 // DeleteProfile removes the specified Profile from the profileStore associated with the Context.
283 func (c Context) DeleteProfile(id uuid.ID) error {
284 if c.profiles == nil {
285 return ErrNoProfileStore
286 }
287 return c.profiles.deleteProfile(id)
288 }
290 // AddLogin stores the passed Login in the profileStore associated with the Context. It also associates
291 // the newly-created Login with the Orofile in login.ProfileID.
292 func (c Context) AddLogin(login Login) error {
293 if c.profiles == nil {
294 return ErrNoProfileStore
295 }
296 return c.profiles.addLogin(login)
297 }
299 // GetLogin returns the Login specified from the profileStore associated with the Context.
300 func (c Context) GetLogin(value string) (Login, error) {
301 if c.profiles == nil {
302 return Login{}, ErrNoProfileStore
303 }
304 return c.profiles.getLogin(value)
305 }
307 // RemoveLogin removes the specified Login from the profileStore associated with the Context, provided
308 // the Login has a ProfileID property that matches the profile ID passed in. It also disassociates the
309 // deleted Login from the Profile in login.ProfileID.
310 func (c Context) RemoveLogin(value string, profile uuid.ID) error {
311 if c.profiles == nil {
312 return ErrNoProfileStore
313 }
314 return c.profiles.removeLogin(value, profile)
315 }
317 // RemoveLoginsByProfile removes all Logins connected to the specified Profile in the profileStore
318 // associated with the Context and disassociates them from the Profile.
319 func (c Context) RemoveLoginsByProfile(profile uuid.ID) error {
320 if c.profiles == nil {
321 return ErrNoProfileStore
322 }
323 return c.profiles.removeLoginsByProfile(profile)
324 }
326 // RecordLoginUse sets the LastUsed property of the Login specified in the profileStore associated with
327 // the Context to the value passed in as when.
328 func (c Context) RecordLoginUse(value string, when time.Time) error {
329 if c.profiles == nil {
330 return ErrNoProfileStore
331 }
332 return c.profiles.recordLoginUse(value, when)
333 }
335 // VerifyLogin sets the Verified property of the Login specied to true in the profileStore associated with
336 // the Context, assuming the Verification property of the Login in the profileStore matches the verification
337 // passed. If the verifications do not match, an ErrLoginVerificationInvalid error is returned.
338 func (c Context) VerifyLogin(value, verification string) error {
339 if c.profiles == nil {
340 return ErrNoProfileStore
341 }
342 return c.profiles.verifyLogin(value, verification)
343 }
345 // ListLogins returns a slice of up to num Logins associated with the specified Profile from the profileStore
346 // associated with the Context, skipping offset Profiles.
347 func (c Context) ListLogins(profile uuid.ID, num, offset int) ([]Login, error) {
348 if c.profiles == nil {
349 return []Login{}, ErrNoProfileStore
350 }
351 return c.profiles.listLogins(profile, num, offset)
352 }
354 // GetToken returns the Token specified from the tokenStore associated with the Context.
355 // If refresh is true, the token input should be compared against the refresh tokens, not the
356 // access tokens.
357 func (c Context) GetToken(token string, refresh bool) (Token, error) {
358 if c.tokens == nil {
359 return Token{}, ErrNoTokenStore
360 }
361 return c.tokens.getToken(token, refresh)
362 }
364 // SaveToken stores the passed Token in the tokenStore associated with the Context.
365 func (c Context) SaveToken(token Token) error {
366 if c.tokens == nil {
367 return ErrNoTokenStore
368 }
369 return c.tokens.saveToken(token)
370 }
372 // RevokeToken revokes the Token identfied by the passed token string from the tokenStore associated
373 // with the context.
374 func (c Context) RevokeToken(token string) error {
375 if c.tokens == nil {
376 return ErrNoTokenStore
377 }
378 return c.tokens.revokeToken(token)
379 }
381 // RevokeTokensByProfileID revokes the Tokens associated with the Profile identified by the passed ID in
382 // the tokenStore associated with the Context.
383 func (c Context) RevokeTokensByProfileID(profileID uuid.ID) error {
384 if c.tokens == nil {
385 return ErrNoTokenStore
386 }
387 return c.tokens.revokeTokensByProfileID(profileID)
388 }
390 func (c Context) RevokeTokensByClientID(clientID uuid.ID) error {
391 if c.tokens == nil {
392 return ErrNoTokenStore
393 }
394 return c.tokens.revokeTokensByClientID(clientID)
395 }
397 // GetTokensByProfileID returns a slice of up to num Tokens with a ProfileID that matches the specified
398 // profileID from the tokenStore associated with the Context, skipping offset Tokens.
399 func (c Context) GetTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) {
400 if c.tokens == nil {
401 return []Token{}, ErrNoTokenStore
402 }
403 return c.tokens.getTokensByProfileID(profileID, num, offset)
404 }
406 // CreateSession stores the passed Session in the sessionStore associated with the Context.
407 func (c Context) CreateSession(session Session) error {
408 if c.sessions == nil {
409 return ErrNoSessionStore
410 }
411 return c.sessions.createSession(session)
412 }
414 // GetSession returns the Session specified from the sessionStore associated with the Context.
415 func (c Context) GetSession(id string) (Session, error) {
416 if c.sessions == nil {
417 return Session{}, ErrNoSessionStore
418 }
419 return c.sessions.getSession(id)
420 }
422 // TerminateSession sets the Session identified by the passed ID as inactive in the sessionStore assocated
423 // with the Context.
424 func (c Context) TerminateSession(id string) error {
425 if c.sessions == nil {
426 return ErrNoSessionStore
427 }
428 return c.sessions.terminateSession(id)
429 }
431 // TerminateSessionsByProfile sets the Sessions associated with the passed Profile ID as inactive in the
432 // sessionStore associated with the Context.
433 func (c Context) TerminateSessionsByProfile(profile uuid.ID) error {
434 if c.sessions == nil {
435 return ErrNoSessionStore
436 }
437 return c.sessions.terminateSessionsByProfile(profile)
438 }
440 // RemoveSession removes the Session identified by the passed ID from the sessionStore associated with
441 // the Context.
442 func (c Context) RemoveSession(id string) error {
443 if c.sessions == nil {
444 return ErrNoSessionStore
445 }
446 return c.sessions.removeSession(id)
447 }
449 // ListSessions returns a slice of up to num Sessions from the sessionStore associated with the Context,
450 // ordered by the date they were created, descending. If before.IsZero() returns false, only Sessions
451 // that were created before that time will be returned. If profile is not nil, only Sessions belonging to
452 // that Profile will be returned.
453 func (c Context) ListSessions(profile uuid.ID, before time.Time, num int64) ([]Session, error) {
454 if c.sessions == nil {
455 return []Session{}, ErrNoSessionStore
456 }
457 return c.sessions.listSessions(profile, before, num)
458 }
460 func (c Context) CreateScopes(scopes []Scope) error {
461 if c.scopes == nil {
462 return ErrNoScopeStore
463 }
464 return c.scopes.createScopes(scopes)
465 }
467 func (c Context) GetScopes(ids []string) ([]Scope, error) {
468 if c.scopes == nil {
469 return []Scope{}, ErrNoScopeStore
470 }
471 return c.scopes.getScopes(ids)
472 }
474 func (c Context) UpdateScope(id string, change ScopeChange) error {
475 if c.scopes == nil {
476 return ErrNoScopeStore
477 }
478 return c.scopes.updateScope(id, change)
479 }
481 func (c Context) RemoveScopes(ids []string) error {
482 if c.scopes == nil {
483 return ErrNoScopeStore
484 }
485 return c.scopes.removeScopes(ids)
486 }
488 func (c Context) ListScopes() ([]Scope, error) {
489 if c.scopes == nil {
490 return []Scope{}, ErrNoScopeStore
491 }
492 return c.scopes.listScopes()
493 }
495 func (c Context) SendModelEvent(m events.Model, action string) error {
496 if c.eventsPublisher == nil {
497 log.Println("Event publisher not set!")
498 }
499 return events.PublishModelEvent(c.eventsPublisher, m, action)
500 }