auth

Paddy 2015-05-17 Parent:581c60f8dd23 Child:8ecb60d29b0d

171:807d20a0b197 Go to Latest

auth/context.go

Add login verification to Config. Keep track of how we're going to verify logins using the Config struct.

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