auth
181:b7e685839a1b Browse Files
Break out scopes and events. This repo has gotten unwieldy, and there are portions of it that need to be imported by a large number of other packages. For example, scopes will be used in almost every API we write. Rather than importing the entirety of this codebase into every API we write, I've opted to move the scope logic out into a scopes package, with a subpackage for the defined types, which is all most projects actually want to import. We also define some event type constants, and importing those shouldn't require a project to import all our dependencies, either. So I made an events subpackage that just holds those constants. This package has become a little bit of a red-headed stepchild and is do for a refactor, but I'm trying to put that off as long as I can. The refactoring of our scopes stuff has left a bug wherein a token can be granted for scopes that don't exist. I'm going to need to revisit that, and also how to limit scopes to only be granted to the users that should be able to request them. But that's a battle for another day.
authcode.go authcode_test.go authd/server.go client.go client/login.go config.go context.go events/profile.go memstore.go oauth2.go oauth2_test.go profile.go scope.go scope_postgres.go scope_test.go session.go token.go token_test.go
1.1 --- a/authcode.go Sat Jul 18 03:38:27 2015 -0400 1.2 +++ b/authcode.go Mon Dec 14 04:17:21 2015 -0800 1.3 @@ -6,6 +6,7 @@ 1.4 "net/http" 1.5 "time" 1.6 1.7 + "code.secondbit.org/scopes.hg/types" 1.8 "code.secondbit.org/uuid.hg" 1.9 ) 1.10 1.11 @@ -37,7 +38,7 @@ 1.12 Created time.Time 1.13 ExpiresIn int32 1.14 ClientID uuid.ID 1.15 - Scopes Scopes 1.16 + Scopes scopeTypes.Scopes 1.17 RedirectURI string 1.18 State string 1.19 ProfileID uuid.ID 1.20 @@ -133,7 +134,7 @@ 1.21 return nil 1.22 } 1.23 1.24 -func authCodeGrantValidate(w http.ResponseWriter, r *http.Request, context Context) (scopes Scopes, profileID uuid.ID, valid bool) { 1.25 +func authCodeGrantValidate(w http.ResponseWriter, r *http.Request, context Context) (scopes scopeTypes.Scopes, profileID uuid.ID, valid bool) { 1.26 enc := json.NewEncoder(w) 1.27 code := r.PostFormValue("code") 1.28 if code == "" {
2.1 --- a/authcode_test.go Sat Jul 18 03:38:27 2015 -0400 2.2 +++ b/authcode_test.go Mon Dec 14 04:17:21 2015 -0800 2.3 @@ -11,6 +11,7 @@ 2.4 "testing" 2.5 "time" 2.6 2.7 + "code.secondbit.org/scopes.hg/types" 2.8 "code.secondbit.org/uuid.hg" 2.9 ) 2.10 2.11 @@ -69,7 +70,7 @@ 2.12 Created: time.Now().Round(time.Millisecond), 2.13 ExpiresIn: 180, 2.14 ClientID: uuid.NewID(), 2.15 - Scopes: stringsToScopes([]string{"scope"}), 2.16 + Scopes: scopeTypes.StringsToScopes([]string{"scope"}), 2.17 RedirectURI: "redirectURI", 2.18 State: "state", 2.19 } 2.20 @@ -161,7 +162,7 @@ 2.21 Created: time.Now().Round(time.Millisecond), 2.22 ExpiresIn: 180, 2.23 ClientID: uuid.NewID(), 2.24 - Scopes: stringsToScopes([]string{"scope"}), 2.25 + Scopes: scopeTypes.StringsToScopes([]string{"scope"}), 2.26 RedirectURI: "redirectURI", 2.27 State: "state", 2.28 } 2.29 @@ -343,7 +344,7 @@ 2.30 Created: time.Now().Round(time.Millisecond), 2.31 ExpiresIn: 180, 2.32 ClientID: uuid.NewID(), 2.33 - Scopes: stringsToScopes([]string{"scope"}), 2.34 + Scopes: scopeTypes.StringsToScopes([]string{"scope"}), 2.35 RedirectURI: "redirectURI", 2.36 State: "state", 2.37 }
3.1 --- a/authd/server.go Sat Jul 18 03:38:27 2015 -0400 3.2 +++ b/authd/server.go Mon Dec 14 04:17:21 2015 -0800 3.3 @@ -41,7 +41,6 @@ 3.4 config.ProfileStore = &p 3.5 config.TokenStore = &p 3.6 config.SessionStore = &p 3.7 - config.ScopeStore = &p 3.8 } else { 3.9 store := auth.NewMemstore() 3.10 config.ClientStore = store 3.11 @@ -49,7 +48,6 @@ 3.12 config.ProfileStore = store 3.13 config.TokenStore = store 3.14 config.SessionStore = store 3.15 - config.ScopeStore = store 3.16 } 3.17 config.Template = template.Must(template.New("base").ParseGlob("./templates/*.gotmpl")) 3.18 config.LoginURI = "/login" 3.19 @@ -70,13 +68,6 @@ 3.20 if err != nil { 3.21 panic(err) 3.22 } 3.23 - err = context.CreateScopes([]auth.Scope{ 3.24 - auth.ScopeLoginAdmin, 3.25 - {ID: "subscriptions", Name: "Manage subscriptions", Description: "Create, view, edit, and cancel your subscriptions."}, 3.26 - }) 3.27 - if err != nil && err != auth.ErrScopeAlreadyExists { 3.28 - log.Fatal(err) 3.29 - } 3.30 3.31 router := mux.NewRouter() 3.32 auth.RegisterOAuth2(router, context)
4.1 --- a/client.go Sat Jul 18 03:38:27 2015 -0400 4.2 +++ b/client.go Mon Dec 14 04:17:21 2015 -0800 4.3 @@ -15,6 +15,7 @@ 4.4 "github.com/PuerkitoBio/purell" 4.5 "github.com/gorilla/mux" 4.6 4.7 + "code.secondbit.org/scopes.hg/types" 4.8 "code.secondbit.org/uuid.hg" 4.9 ) 4.10 4.11 @@ -1074,8 +1075,8 @@ 4.12 encode(w, r, http.StatusCreated, resp) 4.13 } 4.14 4.15 -func clientCredentialsValidate(w http.ResponseWriter, r *http.Request, context Context) (scopes Scopes, profileID uuid.ID, valid bool) { 4.16 - scopes = stringsToScopes(strings.Split(r.PostFormValue("scope"), " ")) 4.17 +func clientCredentialsValidate(w http.ResponseWriter, r *http.Request, context Context) (scopes scopeTypes.Scopes, profileID uuid.ID, valid bool) { 4.18 + scopes = scopeTypes.StringsToScopes(strings.Split(r.PostFormValue("scope"), " ")) 4.19 valid = true 4.20 return 4.21 }
5.1 --- a/client/login.go Sat Jul 18 03:38:27 2015 -0400 5.2 +++ b/client/login.go Mon Dec 14 04:17:21 2015 -0800 5.3 @@ -2,10 +2,11 @@ 5.4 5.5 import ( 5.6 "code.secondbit.org/auth.hg" 5.7 + "code.secondbit.org/scopes.hg/types" 5.8 ) 5.9 5.10 func (c *Client) GetLogin(value string) (auth.Login, error) { 5.11 - resp, err := c.Get("/logins/"+value, auth.Scopes{auth.ScopeLoginAdmin}.Strings(), nil) 5.12 + resp, err := c.Get("/logins/"+value, scopeTypes.Scopes{auth.ScopeLoginAdmin}.Strings(), nil) 5.13 if err != nil { 5.14 hErr, ok := err.(httpErrors) 5.15 if ok && hErr[0].Slug == auth.RequestErrNotFound {
6.1 --- a/config.go Sat Jul 18 03:38:27 2015 -0400 6.2 +++ b/config.go Mon Dec 14 04:17:21 2015 -0800 6.3 @@ -26,7 +26,6 @@ 6.4 ProfileStore profileStore 6.5 TokenStore tokenStore 6.6 SessionStore sessionStore 6.7 - ScopeStore scopeStore 6.8 EventsPublisher events.Publisher 6.9 Template *template.Template 6.10 LoginURI string
7.1 --- a/context.go Sat Jul 18 03:38:27 2015 -0400 7.2 +++ b/context.go Mon Dec 14 04:17:21 2015 -0800 7.3 @@ -22,7 +22,6 @@ 7.4 profiles profileStore 7.5 tokens tokenStore 7.6 sessions sessionStore 7.7 - scopes scopeStore 7.8 eventsPublisher events.Publisher 7.9 config Config 7.10 } 7.11 @@ -39,7 +38,6 @@ 7.12 profiles: config.ProfileStore, 7.13 tokens: config.TokenStore, 7.14 sessions: config.SessionStore, 7.15 - scopes: config.ScopeStore, 7.16 eventsPublisher: config.EventsPublisher, 7.17 template: config.Template, 7.18 config: config, 7.19 @@ -457,41 +455,6 @@ 7.20 return c.sessions.listSessions(profile, before, num) 7.21 } 7.22 7.23 -func (c Context) CreateScopes(scopes []Scope) error { 7.24 - if c.scopes == nil { 7.25 - return ErrNoScopeStore 7.26 - } 7.27 - return c.scopes.createScopes(scopes) 7.28 -} 7.29 - 7.30 -func (c Context) GetScopes(ids []string) ([]Scope, error) { 7.31 - if c.scopes == nil { 7.32 - return []Scope{}, ErrNoScopeStore 7.33 - } 7.34 - return c.scopes.getScopes(ids) 7.35 -} 7.36 - 7.37 -func (c Context) UpdateScope(id string, change ScopeChange) error { 7.38 - if c.scopes == nil { 7.39 - return ErrNoScopeStore 7.40 - } 7.41 - return c.scopes.updateScope(id, change) 7.42 -} 7.43 - 7.44 -func (c Context) RemoveScopes(ids []string) error { 7.45 - if c.scopes == nil { 7.46 - return ErrNoScopeStore 7.47 - } 7.48 - return c.scopes.removeScopes(ids) 7.49 -} 7.50 - 7.51 -func (c Context) ListScopes() ([]Scope, error) { 7.52 - if c.scopes == nil { 7.53 - return []Scope{}, ErrNoScopeStore 7.54 - } 7.55 - return c.scopes.listScopes() 7.56 -} 7.57 - 7.58 func (c Context) SendModelEvent(m events.Model, action string) error { 7.59 if c.eventsPublisher == nil { 7.60 log.Println("Event publisher not set!")
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/events/profile.go Mon Dec 14 04:17:21 2015 -0800 8.3 @@ -0,0 +1,6 @@ 8.4 +package authEvents 8.5 + 8.6 +const ( 8.7 + ActionResendVerification = "resend_verification" 8.8 + ActionLoginVerified = "login_verified" 8.9 +)
9.1 --- a/memstore.go Sat Jul 18 03:38:27 2015 -0400 9.2 +++ b/memstore.go Mon Dec 14 04:17:21 2015 -0800 9.3 @@ -31,9 +31,6 @@ 9.4 9.5 sessions map[string]Session 9.6 sessionLock sync.RWMutex 9.7 - 9.8 - scopes map[string]Scope 9.9 - scopeLock sync.RWMutex 9.10 } 9.11 9.12 // NewMemstore returns an in-memory version of our datastores, 9.13 @@ -53,7 +50,6 @@ 9.14 logins: map[string]Login{}, 9.15 profileLoginLookup: map[string][]string{}, 9.16 sessions: map[string]Session{}, 9.17 - scopes: map[string]Scope{}, 9.18 } 9.19 } 9.20
10.1 --- a/oauth2.go Sat Jul 18 03:38:27 2015 -0400 10.2 +++ b/oauth2.go Mon Dec 14 04:17:21 2015 -0800 10.3 @@ -15,6 +15,7 @@ 10.4 "sync" 10.5 "time" 10.6 10.7 + "code.secondbit.org/scopes.hg/types" 10.8 "code.secondbit.org/uuid.hg" 10.9 "github.com/gorilla/mux" 10.10 ) 10.11 @@ -68,7 +69,7 @@ 10.12 // The ReturnToken will be called when a token is created and needs to be returned to the client. If it returns true, the token 10.13 // was successfully returned and the Invalidate function will be called asynchronously. 10.14 type GrantType struct { 10.15 - Validate func(w http.ResponseWriter, r *http.Request, context Context) (scopes Scopes, profileID uuid.ID, valid bool) 10.16 + Validate func(w http.ResponseWriter, r *http.Request, context Context) (scopes scopeTypes.Scopes, profileID uuid.ID, valid bool) 10.17 Invalidate func(r *http.Request, context Context) error 10.18 ReturnToken func(w http.ResponseWriter, r *http.Request, token Token, context Context) bool 10.19 AuditString func(r *http.Request) string 10.20 @@ -278,21 +279,20 @@ 10.21 http.Redirect(w, r, redirectURL.String(), http.StatusFound) 10.22 return 10.23 } 10.24 - scopeParams := strings.Split(r.URL.Query().Get("scope"), " ") 10.25 - scopes, err := context.GetScopes(scopeParams) 10.26 - if err != nil { 10.27 - if err == ErrScopeNotFound { 10.28 - q.Add("error", "invalid_scope") 10.29 - redirectURL.RawQuery = q.Encode() 10.30 - http.Redirect(w, r, redirectURL.String(), http.StatusFound) 10.31 - return 10.32 - } 10.33 - log.Println("Error retrieving scopes:", err) 10.34 - q.Add("error", "server_error") 10.35 + scopes := scopeTypes.StringsToScopes(strings.Split(r.URL.Query().Get("scope"), " ")) 10.36 + // BUG(paddy): need to check if Scopes actually exist 10.37 + /*if err == ErrScopeNotFound { 10.38 + q.Add("error", "invalid_scope") 10.39 redirectURL.RawQuery = q.Encode() 10.40 http.Redirect(w, r, redirectURL.String(), http.StatusFound) 10.41 return 10.42 } 10.43 + log.Println("Error retrieving scopes:", err) 10.44 + q.Add("error", "server_error") 10.45 + redirectURL.RawQuery = q.Encode() 10.46 + http.Redirect(w, r, redirectURL.String(), http.StatusFound) 10.47 + return 10.48 + */ 10.49 if r.Method == "POST" { 10.50 if checkCSRF(r, session) != nil { 10.51 log.Println("CSRF attempt detected.")
11.1 --- a/oauth2_test.go Sat Jul 18 03:38:27 2015 -0400 11.2 +++ b/oauth2_test.go Mon Dec 14 04:17:21 2015 -0800 11.3 @@ -11,6 +11,7 @@ 11.4 "testing" 11.5 "time" 11.6 11.7 + "code.secondbit.org/scopes.hg/types" 11.8 "code.secondbit.org/uuid.hg" 11.9 ) 11.10 11.11 @@ -35,7 +36,6 @@ 11.12 profiles: store, 11.13 tokens: store, 11.14 sessions: store, 11.15 - scopes: store, 11.16 } 11.17 client := Client{ 11.18 ID: uuid.NewID(), 11.19 @@ -78,15 +78,17 @@ 11.20 if err != nil { 11.21 t.Fatal("Can't store session:", err) 11.22 } 11.23 - scope := Scope{ 11.24 - ID: "testscope", 11.25 - Name: "Test Scope", 11.26 - Description: "Hug dispensation.", 11.27 - } 11.28 - err = testContext.CreateScopes([]Scope{scope}) 11.29 - if err != nil { 11.30 - t.Fatal("Can't store scope:", err) 11.31 - } 11.32 + /* 11.33 + scope := scopeTypes.Scope{ 11.34 + ID: "testscope", 11.35 + Name: "Test Scope", 11.36 + Description: "Hug dispensation.", 11.37 + } 11.38 + err = testContext.CreateScopes([]Scope{scope}) 11.39 + if err != nil { 11.40 + t.Fatal("Can't store scope:", err) 11.41 + }*/ 11.42 + // BUG(paddy): create the scopes in the scopeStore 11.43 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil) 11.44 if err != nil { 11.45 t.Fatal("Can't build request:", err) 11.46 @@ -457,7 +459,6 @@ 11.47 profiles: store, 11.48 tokens: store, 11.49 sessions: store, 11.50 - scopes: store, 11.51 } 11.52 client := Client{ 11.53 ID: uuid.NewID(), 11.54 @@ -492,15 +493,18 @@ 11.55 if err != nil { 11.56 t.Fatal("Can't store session:", err) 11.57 } 11.58 - scope := Scope{ 11.59 - ID: "testscope", 11.60 - Name: "Test Scope", 11.61 - Description: "High five fabrication.", 11.62 - } 11.63 - err = testContext.CreateScopes([]Scope{scope}) 11.64 - if err != nil { 11.65 - t.Fatal("Can't create scope:", err) 11.66 - } 11.67 + /* 11.68 + scope := scopeTypes.Scope{ 11.69 + ID: "testscope", 11.70 + Name: "Test Scope", 11.71 + Description: "High five fabrication.", 11.72 + } 11.73 + // BUG(paddy): Create the Scopes in the scopeStore 11.74 + err = testContext.CreateScopes([]Scope{scope}) 11.75 + if err != nil { 11.76 + t.Fatal("Can't create scope:", err) 11.77 + } 11.78 + */ 11.79 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil) 11.80 if err != nil { 11.81 t.Fatal("Can't build request:", err) 11.82 @@ -794,7 +798,7 @@ 11.83 Created: time.Now().Round(time.Millisecond), 11.84 ExpiresIn: 600, 11.85 ClientID: client.ID, 11.86 - Scopes: stringsToScopes([]string{"testscope"}), 11.87 + Scopes: scopeTypes.StringsToScopes([]string{"testscope"}), 11.88 RedirectURI: "https://client.secondbit.org/", 11.89 State: "teststate", 11.90 ProfileID: uuid.NewID(),
12.1 --- a/profile.go Sat Jul 18 03:38:27 2015 -0400 12.2 +++ b/profile.go Mon Dec 14 04:17:21 2015 -0800 12.3 @@ -9,7 +9,9 @@ 12.4 "strings" 12.5 "time" 12.6 12.7 + "code.secondbit.org/auth.hg/events" 12.8 "code.secondbit.org/events.hg" 12.9 + "code.secondbit.org/scopes.hg/types" 12.10 "code.secondbit.org/uuid.hg" 12.11 12.12 "github.com/gorilla/mux" 12.13 @@ -26,10 +28,6 @@ 12.14 MaxNameLength = 64 12.15 // MaxEmailLength is the maximum length, in bytes, of an email address, exclusive. 12.16 MaxEmailLength = 64 12.17 - 12.18 - // ActionResendVerification is the action property of the event sent when the user wants to resend their login verification code. 12.19 - ActionResendVerification = "resend_verification" 12.20 - ActionLoginVerified = "login_verified" 12.21 ) 12.22 12.23 var ( 12.24 @@ -71,7 +69,7 @@ 12.25 // duration, to prevent brute force attacks. 12.26 ErrProfileLocked = errors.New("profile locked") 12.27 12.28 - ScopeLoginAdmin = Scope{ID: "login_admin", Name: "Administer Logins", Description: "Read and write logins, bypassing ACL."} 12.29 + ScopeLoginAdmin = scopeTypes.Scope{ID: "login_admin", Name: "Administer Logins", Description: "Read and write logins, bypassing ACL."} 12.30 ) 12.31 12.32 // Profile represents a single user of the service, 12.33 @@ -900,7 +898,7 @@ 12.34 encode(w, r, http.StatusInternalServerError, actOfGodResponse) 12.35 return 12.36 } 12.37 - go context.SendModelEvent(login, ActionLoginVerified) 12.38 + go context.SendModelEvent(login, authEvents.ActionLoginVerified) 12.39 login.Verified = true 12.40 } else if req.ResendVerification != nil { 12.41 if !*req.ResendVerification { 12.42 @@ -908,7 +906,7 @@ 12.43 encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 12.44 return 12.45 } 12.46 - go context.SendModelEvent(login, ActionResendVerification) 12.47 + go context.SendModelEvent(login, authEvents.ActionResendVerification) 12.48 } else { 12.49 errors = append(errors, RequestError{Slug: RequestErrMissing, Field: "/"}) 12.50 encode(w, r, http.StatusBadRequest, Response{Errors: errors})
13.1 --- a/scope.go Sat Jul 18 03:38:27 2015 -0400 13.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 13.3 @@ -1,151 +0,0 @@ 13.4 -package auth 13.5 - 13.6 -import ( 13.7 - "errors" 13.8 - "sort" 13.9 -) 13.10 - 13.11 -var ( 13.12 - ErrNoScopeStore = errors.New("scopeStore not set in Context") 13.13 - ErrScopeNotFound = errors.New("scope not found") 13.14 - ErrScopeAlreadyExists = errors.New("scope already exists") 13.15 -) 13.16 - 13.17 -// Scope represents a limit on the access that a grant provides. 13.18 -type Scope struct { 13.19 - ID string 13.20 - Name string 13.21 - Description string 13.22 -} 13.23 - 13.24 -func (s *Scope) ApplyChange(change ScopeChange) { 13.25 - if change.Name != nil { 13.26 - s.Name = *change.Name 13.27 - } 13.28 - if change.Description != nil { 13.29 - s.Description = *change.Description 13.30 - } 13.31 -} 13.32 - 13.33 -type Scopes []Scope 13.34 - 13.35 -func (s Scopes) Len() int { 13.36 - return len(s) 13.37 -} 13.38 - 13.39 -func (s Scopes) Swap(i, j int) { 13.40 - s[i], s[j] = s[j], s[i] 13.41 -} 13.42 - 13.43 -func (s Scopes) Less(i, j int) bool { 13.44 - return s[i].ID < s[j].ID 13.45 -} 13.46 - 13.47 -func (s Scopes) Strings() []string { 13.48 - res := make([]string, len(s)) 13.49 - for pos, scope := range s { 13.50 - res[pos] = scope.ID 13.51 - } 13.52 - return res 13.53 -} 13.54 - 13.55 -func stringsToScopes(s []string) Scopes { 13.56 - res := make(Scopes, len(s)) 13.57 - for pos, scope := range s { 13.58 - res[pos] = Scope{ID: scope} 13.59 - } 13.60 - return res 13.61 -} 13.62 - 13.63 -// ScopeChange represents a change to a Scope. 13.64 -type ScopeChange struct { 13.65 - Name *string 13.66 - Description *string 13.67 -} 13.68 - 13.69 -func (s ScopeChange) Empty() bool { 13.70 - return s.Name == nil && s.Description == nil 13.71 -} 13.72 - 13.73 -type scopeStore interface { 13.74 - createScopes(scopes []Scope) error 13.75 - getScopes(ids []string) ([]Scope, error) 13.76 - updateScope(id string, change ScopeChange) error 13.77 - removeScopes(ids []string) error 13.78 - listScopes() ([]Scope, error) 13.79 -} 13.80 - 13.81 -func (m *memstore) createScopes(scopes []Scope) error { 13.82 - m.scopeLock.Lock() 13.83 - defer m.scopeLock.Unlock() 13.84 - 13.85 - for _, scope := range scopes { 13.86 - if _, ok := m.scopes[scope.ID]; ok { 13.87 - return ErrScopeAlreadyExists 13.88 - } 13.89 - } 13.90 - for _, scope := range scopes { 13.91 - m.scopes[scope.ID] = scope 13.92 - } 13.93 - return nil 13.94 -} 13.95 - 13.96 -func (m *memstore) getScopes(ids []string) ([]Scope, error) { 13.97 - m.scopeLock.RLock() 13.98 - defer m.scopeLock.RUnlock() 13.99 - 13.100 - scopes := []Scope{} 13.101 - for _, id := range ids { 13.102 - scope, ok := m.scopes[id] 13.103 - if !ok { 13.104 - continue 13.105 - } 13.106 - scopes = append(scopes, scope) 13.107 - } 13.108 - sorted := Scopes(scopes) 13.109 - sort.Sort(sorted) 13.110 - scopes = sorted 13.111 - return scopes, nil 13.112 -} 13.113 - 13.114 -func (m *memstore) updateScope(id string, change ScopeChange) error { 13.115 - m.scopeLock.Lock() 13.116 - defer m.scopeLock.Unlock() 13.117 - 13.118 - scope, ok := m.scopes[id] 13.119 - if !ok { 13.120 - return ErrScopeNotFound 13.121 - } 13.122 - scope.ApplyChange(change) 13.123 - m.scopes[id] = scope 13.124 - return nil 13.125 -} 13.126 - 13.127 -func (m *memstore) removeScopes(ids []string) error { 13.128 - m.scopeLock.Lock() 13.129 - defer m.scopeLock.Unlock() 13.130 - 13.131 - for _, id := range ids { 13.132 - if _, ok := m.scopes[id]; !ok { 13.133 - return ErrScopeNotFound 13.134 - } 13.135 - } 13.136 - for _, id := range ids { 13.137 - delete(m.scopes, id) 13.138 - } 13.139 - return nil 13.140 -} 13.141 - 13.142 -func (m *memstore) listScopes() ([]Scope, error) { 13.143 - m.scopeLock.RLock() 13.144 - defer m.scopeLock.RUnlock() 13.145 - 13.146 - scopes := []Scope{} 13.147 - for _, scope := range m.scopes { 13.148 - scopes = append(scopes, scope) 13.149 - } 13.150 - sorted := Scopes(scopes) 13.151 - sort.Sort(sorted) 13.152 - scopes = sorted 13.153 - return scopes, nil 13.154 -}
14.1 --- a/scope_postgres.go Sat Jul 18 03:38:27 2015 -0400 14.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 14.3 @@ -1,180 +0,0 @@ 14.4 -package auth 14.5 - 14.6 -import ( 14.7 - "code.secondbit.org/pqarrays.hg" 14.8 - "database/sql/driver" 14.9 - "github.com/lib/pq" 14.10 - "github.com/secondbit/pan" 14.11 -) 14.12 - 14.13 -func (s Scope) GetSQLTableName() string { 14.14 - return "scopes" 14.15 -} 14.16 - 14.17 -func (s Scopes) Value() (driver.Value, error) { 14.18 - ids := make(pqarrays.StringArray, 0, len(s)) 14.19 - for _, scope := range s { 14.20 - ids = append(ids, scope.ID) 14.21 - } 14.22 - return ids.Value() 14.23 -} 14.24 - 14.25 -func (s *Scopes) Scan(value interface{}) error { 14.26 - *s = (*s)[:0] 14.27 - var ids pqarrays.StringArray 14.28 - err := ids.Scan(value) 14.29 - if err != nil { 14.30 - return err 14.31 - } 14.32 - for _, id := range ids { 14.33 - *s = append(*s, Scope{ID: id}) 14.34 - } 14.35 - return nil 14.36 -} 14.37 - 14.38 -func (p *postgres) createScopesSQL(scopes []Scope) *pan.Query { 14.39 - fields, _ := pan.GetFields(scopes[0]) 14.40 - query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(scopes[0])) 14.41 - query.Include("(" + pan.QueryList(fields) + ")") 14.42 - query.Include("VALUES") 14.43 - query.FlushExpressions(" ") 14.44 - for _, scope := range scopes { 14.45 - _, values := pan.GetFields(scope) 14.46 - query.Include("("+pan.VariableList(len(values))+")", values...) 14.47 - } 14.48 - return query.FlushExpressions(", ") 14.49 -} 14.50 - 14.51 -func (p *postgres) createScopes(scopes []Scope) error { 14.52 - if len(scopes) < 1 { 14.53 - return nil 14.54 - } 14.55 - query := p.createScopesSQL(scopes) 14.56 - _, err := p.db.Exec(query.String(), query.Args...) 14.57 - if e, ok := err.(*pq.Error); ok && e.Constraint == "scopes_pkey" { 14.58 - err = ErrScopeAlreadyExists 14.59 - } 14.60 - return err 14.61 -} 14.62 - 14.63 -func (p *postgres) getScopesSQL(ids []string) *pan.Query { 14.64 - var scope Scope 14.65 - intids := make([]interface{}, len(ids)) 14.66 - for pos, id := range ids { 14.67 - intids[pos] = id 14.68 - } 14.69 - fields, _ := pan.GetFields(scope) 14.70 - query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(scope)) 14.71 - query.IncludeWhere() 14.72 - query.Include(pan.GetUnquotedColumn(scope, "ID") + " IN") 14.73 - query.Include("("+pan.VariableList(len(ids))+")", intids...) 14.74 - return query.FlushExpressions(" ") 14.75 -} 14.76 - 14.77 -func (p *postgres) getScopes(ids []string) ([]Scope, error) { 14.78 - query := p.getScopesSQL(ids) 14.79 - rows, err := p.db.Query(query.String(), query.Args...) 14.80 - if err != nil { 14.81 - return []Scope{}, err 14.82 - } 14.83 - var scopes []Scope 14.84 - for rows.Next() { 14.85 - var scope Scope 14.86 - err := pan.Unmarshal(rows, &scope) 14.87 - if err != nil { 14.88 - return scopes, err 14.89 - } 14.90 - scopes = append(scopes, scope) 14.91 - } 14.92 - if err = rows.Err(); err != nil { 14.93 - return scopes, err 14.94 - } 14.95 - return scopes, nil 14.96 -} 14.97 - 14.98 -func (p *postgres) updateScopeSQL(id string, change ScopeChange) *pan.Query { 14.99 - var scope Scope 14.100 - query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(scope)+" SET ") 14.101 - query.IncludeIfNotNil(pan.GetUnquotedColumn(scope, "Name")+" = ?", change.Name) 14.102 - query.IncludeIfNotNil(pan.GetUnquotedColumn(scope, "Description")+" = ?", change.Description) 14.103 - query.FlushExpressions(", ") 14.104 - query.IncludeWhere() 14.105 - query.Include(pan.GetUnquotedColumn(scope, "ID")+" = ?", id) 14.106 - return query.FlushExpressions(" ") 14.107 -} 14.108 - 14.109 -func (p *postgres) updateScope(id string, change ScopeChange) error { 14.110 - if change.Empty() { 14.111 - return nil 14.112 - } 14.113 - query := p.updateScopeSQL(id, change) 14.114 - res, err := p.db.Exec(query.String(), query.Args...) 14.115 - if err != nil { 14.116 - return err 14.117 - } 14.118 - rows, err := res.RowsAffected() 14.119 - if err != nil { 14.120 - return err 14.121 - } 14.122 - if rows < 1 { 14.123 - return ErrScopeNotFound 14.124 - } 14.125 - return err 14.126 -} 14.127 - 14.128 -func (p *postgres) removeScopesSQL(ids []string) *pan.Query { 14.129 - var scope Scope 14.130 - intids := make([]interface{}, len(ids)) 14.131 - for pos, id := range ids { 14.132 - intids[pos] = id 14.133 - } 14.134 - query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(scope)) 14.135 - query.IncludeWhere() 14.136 - query.Include(pan.GetUnquotedColumn(scope, "ID") + " IN") 14.137 - query.Include("("+pan.VariableList(len(ids))+")", intids...) 14.138 - return query.FlushExpressions(" ") 14.139 -} 14.140 - 14.141 -func (p *postgres) removeScopes(ids []string) error { 14.142 - query := p.removeScopesSQL(ids) 14.143 - res, err := p.db.Exec(query.String(), query.Args...) 14.144 - if err != nil { 14.145 - return err 14.146 - } 14.147 - rows, err := res.RowsAffected() 14.148 - if err != nil { 14.149 - return err 14.150 - } 14.151 - if rows < 1 { 14.152 - return ErrScopeNotFound 14.153 - } 14.154 - return nil 14.155 -} 14.156 - 14.157 -func (p *postgres) listScopesSQL() *pan.Query { 14.158 - var scope Scope 14.159 - fields, _ := pan.GetFields(scope) 14.160 - query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(scope)) 14.161 - return query.FlushExpressions(" ") 14.162 -} 14.163 - 14.164 -func (p *postgres) listScopes() ([]Scope, error) { 14.165 - query := p.listScopesSQL() 14.166 - rows, err := p.db.Query(query.String(), query.Args...) 14.167 - if err != nil { 14.168 - return []Scope{}, err 14.169 - } 14.170 - var scopes []Scope 14.171 - for rows.Next() { 14.172 - var scope Scope 14.173 - err = pan.Unmarshal(rows, &scope) 14.174 - if err != nil { 14.175 - return scopes, err 14.176 - } 14.177 - scopes = append(scopes, scope) 14.178 - } 14.179 - if err = rows.Err(); err != nil { 14.180 - return scopes, err 14.181 - } 14.182 - return scopes, nil 14.183 -}
15.1 --- a/scope_test.go Sat Jul 18 03:38:27 2015 -0400 15.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 15.3 @@ -1,157 +0,0 @@ 15.4 -package auth 15.5 - 15.6 -import "os" 15.7 -import "testing" 15.8 - 15.9 -func init() { 15.10 - if os.Getenv("PG_TEST_DB") != "" { 15.11 - p, err := NewPostgres(os.Getenv("PG_TEST_DB")) 15.12 - if err != nil { 15.13 - panic(err) 15.14 - } 15.15 - scopeStores = append(scopeStores, &p) 15.16 - } 15.17 -} 15.18 - 15.19 -var scopeStores = []scopeStore{NewMemstore()} 15.20 - 15.21 -func compareScopes(scope1, scope2 Scope) (success bool, field string, val1, val2 interface{}) { 15.22 - if scope1.ID != scope2.ID { 15.23 - return false, "ID", scope1.ID, scope2.ID 15.24 - } 15.25 - if scope1.Name != scope2.Name { 15.26 - return false, "Name", scope1.Name, scope2.Name 15.27 - } 15.28 - if scope1.Description != scope2.Description { 15.29 - return false, "Description", scope1.Description, scope2.Description 15.30 - } 15.31 - return true, "", nil, nil 15.32 -} 15.33 - 15.34 -func TestScopeInScopeStore(t *testing.T) { 15.35 - scope := Scope{ 15.36 - ID: "testscope", 15.37 - Name: "Test Scope", 15.38 - Description: "Access to testing data.", 15.39 - } 15.40 - scope2 := Scope{ 15.41 - ID: "testscope2", 15.42 - Name: "Test Scope 2", 15.43 - Description: "Access to minions.", 15.44 - } 15.45 - scope3 := Scope{ 15.46 - ID: "testscope3", 15.47 - Name: "Test Scope 3", 15.48 - Description: "Access to bananas.", 15.49 - } 15.50 - updatedName := "Updated Scope" 15.51 - updatedDescription := "An updated scope." 15.52 - update := ScopeChange{ 15.53 - Name: &updatedName, 15.54 - } 15.55 - update2 := ScopeChange{ 15.56 - Description: &updatedDescription, 15.57 - } 15.58 - update3 := ScopeChange{ 15.59 - Name: &updatedName, 15.60 - Description: &updatedDescription, 15.61 - } 15.62 - for _, store := range scopeStores { 15.63 - context := Context{scopes: store} 15.64 - retrieved, err := context.GetScopes([]string{scope.ID}) 15.65 - if len(retrieved) != 0 { 15.66 - t.Logf("%+v", retrieved) 15.67 - t.Errorf("Expected %d results, got %d from %T", 0, len(retrieved), store) 15.68 - } 15.69 - err = context.CreateScopes([]Scope{scope}) 15.70 - if err != nil { 15.71 - t.Errorf("Error saving scope to %T: %s", store, err) 15.72 - } 15.73 - err = context.CreateScopes([]Scope{scope}) 15.74 - if err != ErrScopeAlreadyExists { 15.75 - t.Errorf("Expected ErrScopeAlreadyExists, got %s instead for %T", err, store) 15.76 - } 15.77 - retrieved, err = context.GetScopes([]string{scope.ID}) 15.78 - if err != nil { 15.79 - t.Errorf("Unexpected error retrieving scopes from %T: %+v", store, err) 15.80 - } 15.81 - if len(retrieved) != 1 { 15.82 - t.Logf("%+v", retrieved) 15.83 - t.Errorf("Expected %d results, got %d from %T", 1, len(retrieved), store) 15.84 - } 15.85 - success, field, val1, val2 := compareScopes(scope, retrieved[0]) 15.86 - if !success { 15.87 - t.Errorf("Expected %s to be %+v, got %+v from %T", field, val1, val2, store) 15.88 - } 15.89 - err = context.CreateScopes([]Scope{scope2, scope3}) 15.90 - if err != nil { 15.91 - t.Errorf("Unexpected error trying to create scope2 and scope3 in %T: %+v", store, err) 15.92 - } 15.93 - retrieved, err = context.GetScopes([]string{scope.ID, scope2.ID, scope3.ID}) 15.94 - if err != nil { 15.95 - t.Errorf("Unexpected error retrieving scopes from %T: %+v", store, err) 15.96 - } 15.97 - if len(retrieved) != 3 { 15.98 - t.Logf("%+v", retrieved) 15.99 - t.Errorf("Expected %d results, got %d from %T", 3, len(retrieved), store) 15.100 - } 15.101 - for pos, s := range []Scope{scope, scope2, scope3} { 15.102 - success, field, val1, val2 = compareScopes(s, retrieved[pos]) 15.103 - if !success { 15.104 - t.Errorf("Expected %s to be %+v for scope %s, got %+v from %T", field, val1, s.ID, val2, store) 15.105 - } 15.106 - } 15.107 - err = context.UpdateScope(scope.ID, update) 15.108 - if err != nil { 15.109 - t.Errorf("Unexpected error updating scope in %T: %+v", store, err) 15.110 - } 15.111 - scope.ApplyChange(update) 15.112 - err = context.UpdateScope(scope2.ID, update2) 15.113 - if err != nil { 15.114 - t.Errorf("Unexpected error updating scope in %T: %+v", store, err) 15.115 - } 15.116 - scope2.ApplyChange(update2) 15.117 - err = context.UpdateScope(scope3.ID, update3) 15.118 - if err != nil { 15.119 - t.Errorf("Unexpected error updating scope in %T: %+v", store, err) 15.120 - } 15.121 - scope3.ApplyChange(update3) 15.122 - retrieved, err = context.ListScopes() 15.123 - if err != nil { 15.124 - t.Errorf("Unexpected error retrieving scopes from %T: %+v", store, err) 15.125 - } 15.126 - if len(retrieved) != 3 { 15.127 - t.Logf("%+v", retrieved) 15.128 - t.Errorf("Expected %d results, got %d from %T", 3, len(retrieved), store) 15.129 - } 15.130 - for pos, s := range []Scope{scope, scope2, scope3} { 15.131 - success, field, val1, val2 = compareScopes(s, retrieved[pos]) 15.132 - if !success { 15.133 - t.Errorf("Expected %s to be %+v for scope %s, got %+v from %T", field, val1, s.ID, val2, store) 15.134 - } 15.135 - } 15.136 - err = context.RemoveScopes([]string{scope.ID, scope2.ID, scope3.ID}) 15.137 - if err != nil { 15.138 - t.Errorf("Unexpected error removing scopes from %T: %+v", store, err) 15.139 - } 15.140 - retrieved, err = context.ListScopes() 15.141 - if err != nil { 15.142 - t.Errorf("Unexpected error retrieving scopes from %T: %+v", store, err) 15.143 - } 15.144 - if len(retrieved) != 0 { 15.145 - t.Logf("%+v", retrieved) 15.146 - t.Errorf("Expected %d results, got %d from %T", 0, len(retrieved), store) 15.147 - } 15.148 - err = context.RemoveScopes([]string{scope.ID}) 15.149 - if err == nil { 15.150 - t.Errorf("No error returned removing non-existent scopes from %T", store) 15.151 - } 15.152 - if err != ErrScopeNotFound { 15.153 - t.Errorf("Unexpected error removing non-existent scopes from %T: %+v", store, err) 15.154 - } 15.155 - err = context.UpdateScope(scope.ID, update) 15.156 - if err != ErrScopeNotFound { 15.157 - t.Errorf("Unexpected error updating non-existent scopes from %T: %+v", store, err) 15.158 - } 15.159 - } 15.160 -}
16.1 --- a/session.go Sat Jul 18 03:38:27 2015 -0400 16.2 +++ b/session.go Mon Dec 14 04:17:21 2015 -0800 16.3 @@ -14,6 +14,7 @@ 16.4 "time" 16.5 16.6 "code.secondbit.org/pass.hg" 16.7 + "code.secondbit.org/scopes.hg/types" 16.8 "code.secondbit.org/uuid.hg" 16.9 "github.com/gorilla/mux" 16.10 ) 16.11 @@ -413,11 +414,11 @@ 16.12 encode(w, r, http.StatusOK, Response{Sessions: []Session{session}, Errors: errors}) 16.13 } 16.14 16.15 -func credentialsValidate(w http.ResponseWriter, r *http.Request, context Context) (scopes Scopes, profileID uuid.ID, valid bool) { 16.16 +func credentialsValidate(w http.ResponseWriter, r *http.Request, context Context) (scopes scopeTypes.Scopes, profileID uuid.ID, valid bool) { 16.17 enc := json.NewEncoder(w) 16.18 username := r.PostFormValue("username") 16.19 password := r.PostFormValue("password") 16.20 - scopes = stringsToScopes(strings.Split(r.PostFormValue("scope"), " ")) 16.21 + scopes = scopeTypes.StringsToScopes(strings.Split(r.PostFormValue("scope"), " ")) 16.22 profile, err := authenticate(username, password, context) 16.23 if err != nil { 16.24 if isAuthError(err) {
17.1 --- a/token.go Sat Jul 18 03:38:27 2015 -0400 17.2 +++ b/token.go Mon Dec 14 04:17:21 2015 -0800 17.3 @@ -8,6 +8,7 @@ 17.4 "strings" 17.5 "time" 17.6 17.7 + "code.secondbit.org/scopes.hg/types" 17.8 "code.secondbit.org/uuid.hg" 17.9 17.10 "github.com/dgrijalva/jwt-go" 17.11 @@ -46,7 +47,7 @@ 17.12 CreatedFrom string 17.13 ExpiresIn int32 17.14 TokenType string 17.15 - Scopes Scopes 17.16 + Scopes scopeTypes.Scopes 17.17 ProfileID uuid.ID 17.18 ClientID uuid.ID 17.19 Revoked bool 17.20 @@ -180,7 +181,7 @@ 17.21 return tokens, nil 17.22 } 17.23 17.24 -func refreshTokenValidate(w http.ResponseWriter, r *http.Request, context Context) (scopes Scopes, profileID uuid.ID, valid bool) { 17.25 +func refreshTokenValidate(w http.ResponseWriter, r *http.Request, context Context) (scopes scopeTypes.Scopes, profileID uuid.ID, valid bool) { 17.26 enc := json.NewEncoder(w) 17.27 refresh := r.PostFormValue("refresh_token") 17.28 if refresh == "" {
18.1 --- a/token_test.go Sat Jul 18 03:38:27 2015 -0400 18.2 +++ b/token_test.go Mon Dec 14 04:17:21 2015 -0800 18.3 @@ -5,6 +5,7 @@ 18.4 "testing" 18.5 "time" 18.6 18.7 + "code.secondbit.org/scopes.hg/types" 18.8 "code.secondbit.org/uuid.hg" 18.9 ) 18.10 18.11 @@ -64,7 +65,7 @@ 18.12 Created: time.Now().Round(time.Millisecond), 18.13 ExpiresIn: 3600, 18.14 TokenType: "bearer", 18.15 - Scopes: stringsToScopes([]string{"scope"}), 18.16 + Scopes: scopeTypes.StringsToScopes([]string{"scope"}), 18.17 ProfileID: uuid.NewID(), 18.18 } 18.19 for _, store := range tokenStores {