diff --git a/.travis.yml b/.travis.yml index 4332b0f550b..484009f315b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,12 +23,14 @@ before_install: - sudo apt-get install curl install: - - go get -u github.com/go-swagger/go-swagger/cmd/swagger github.com/bradfitz/goimports github.com/mattn/goveralls golang.org/x/tools/cmd/cover github.com/Masterminds/glide github.com/mitchellh/gox github.com/ory/go-acc + - go get -u github.com/go-swagger/go-swagger/cmd/swagger github.com/bradfitz/goimports github.com/mattn/goveralls golang.org/x/tools/cmd/cover github.com/Masterminds/glide github.com/mitchellh/gox github.com/golang/dep/cmd/dep github.com/ory/go-acc - git clone https://github.com/docker-library/official-images.git ~/official-images - glide install - go install github.com/ory/hydra - glide update - go install github.com/ory/hydra + - dep ensure + - go install github.com/ory/hydra script: - ./scripts/test-format.sh diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 00000000000..246f52858c7 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,79 @@ +[[constraint]] + name = "github.com/dgrijalva/jwt-go" + version = "3.0.0" + +[[constraint]] + name = "github.com/go-sql-driver/mysql" + version = "1.3.0" + +[[constraint]] + name = "github.com/gorilla/context" + version = "1.1.0" + +[[constraint]] + name = "github.com/gorilla/sessions" + version = "1.1.0" + +[[constraint]] + name = "github.com/imdario/mergo" + version = "0.2.2" + +[[constraint]] + name = "github.com/julienschmidt/httprouter" + version = "1.1.0" + +[[constraint]] + name = "github.com/oleiade/reflections" + version = "1.0.0" + +[[constraint]] + name = "github.com/ory/fosite" + version = "0.11.3" + +[[constraint]] + name = "github.com/ory/graceful" + version = "0.1.0" + +[[constraint]] + name = "github.com/ory/herodot" + version = "0.1.1" + +[[constraint]] + name = "github.com/ory/ladon" + version = "0.8.2" + +[[constraint]] + name = "github.com/pborman/uuid" + version = "1.0.0" + +[[constraint]] + name = "github.com/pkg/errors" + version = "0.8.0" + +[[constraint]] + name = "github.com/pkg/profile" + version = "1.2.1" + +[[constraint]] + name = "github.com/square/go-jose" + version = "2.1.3" + +[[constraint]] + name = "github.com/stretchr/testify" + version = "1.1.4" + +[[constraint]] + name = "github.com/toqueteos/webbrowser" + version = "1.0.0" + +[[constraint]] + name = "github.com/urfave/negroni" + version = "0.2.0" + +[[constraint]] + name = "github.com/go-resty/resty" + version = "1.0.0" + +[[constraint]] + name = "github.com/segmentio/analytics-go" + version = "2.1.1" diff --git a/cmd/keys_create.go b/cmd/keys_create.go index c0759ddd9c7..8b9fb8f269c 100644 --- a/cmd/keys_create.go +++ b/cmd/keys_create.go @@ -27,6 +27,6 @@ var keysCreateCmd = &cobra.Command{ func init() { keysCmd.AddCommand(keysCreateCmd) - keysCreateCmd.Flags().StringP("alg", "a", "", "REQUIRED name that identifies the algorithm intended for use with the key. Supports: RS256, ES521, HS256") + keysCreateCmd.Flags().StringP("alg", "a", "", "REQUIRED name that identifies the algorithm intended for use with the key. Supports: RS256, ES256, ES521, HS256") } diff --git a/cmd/server/helper_cert.go b/cmd/server/helper_cert.go index bd09d2761ed..da6835ddf67 100644 --- a/cmd/server/helper_cert.go +++ b/cmd/server/helper_cert.go @@ -84,8 +84,8 @@ func getOrCreateTLSCertificate(cmd *cobra.Command, c *config.Config) tls.Certifi private := jwk.First(keys.Key("private")) private.Certificates = []*x509.Certificate{cert} - keys = &jose.JsonWebKeySet{ - Keys: []jose.JsonWebKey{ + keys = &jose.JSONWebKeySet{ + Keys: []jose.JSONWebKey{ *private, *jwk.First(keys.Key("public")), }, diff --git a/docs/api.swagger.json b/docs/api.swagger.json index 0578de0ab7d..a3d9b031162 100644 --- a/docs/api.swagger.json +++ b/docs/api.swagger.json @@ -1487,8 +1487,8 @@ "tags": [ "warden" ], - "summary": "Find groups by member", - "operationId": "findGroupsByMember", + "summary": "List groups", + "operationId": "listGroups", "security": [ { "oauth2": [ @@ -1504,11 +1504,27 @@ "name": "member", "in": "query", "required": true + }, + { + "type": "integer", + "format": "int64", + "x-go-name": "Offset", + "description": "The offset from where to start looking if member isn't specified.", + "name": "offset", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "x-go-name": "Limit", + "description": "The maximum amount of policies returned if member isn't specified.", + "name": "limit", + "in": "query" } ], "responses": { "200": { - "$ref": "#/responses/findGroupsByMemberResponse" + "$ref": "#/responses/listGroupsResponse" }, "401": { "$ref": "#/responses/genericError" @@ -1928,12 +1944,6 @@ "Handler": { "type": "object", "properties": { - "Generators": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/KeyGenerator" - } - }, "H": { "$ref": "#/definitions/Writer" }, @@ -1944,7 +1954,7 @@ "$ref": "#/definitions/Firewall" } }, - "x-go-package": "github.com/ory/hydra/jwk" + "x-go-package": "github.com/ory/hydra/warden/group" }, "KeyGenerator": { "type": "object", @@ -2173,7 +2183,7 @@ ], "properties": { "alg": { - "description": "The algorithm to be used for creating the key. Supports \"RS256\", \"ES521\" and \"HS256\"", + "description": "The algorithm to be used for creating the key. Supports \"RS256\", \"ES256\", \"ES521\" and \"HS256\"", "type": "string", "x-go-name": "Algorithm" }, @@ -3021,15 +3031,6 @@ "emptyResponse": { "description": "An empty response" }, - "findGroupsByMemberResponse": { - "description": "A list of groups the member is belonging to", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/group" - } - } - }, "genericError": { "description": "The standard error format", "schema": { @@ -3094,6 +3095,15 @@ "$ref": "#/definitions/oAuth2TokenIntrospection" } }, + "listGroupsResponse": { + "description": "A list of groups the member is belonging to", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/group" + } + } + }, "oAuth2ClientList": { "description": "A list of clients.", "schema": { diff --git a/glide.yaml b/glide.yaml index 2744db083ac..53c9c63ca20 100644 --- a/glide.yaml +++ b/glide.yaml @@ -3,9 +3,9 @@ import: - package: github.com/sirupsen/logrus version: master - package: github.com/Sirupsen/logrus + version: master repo: https://github.com/sirupsen/logrus.git vcs: git - version: master - package: github.com/dgrijalva/jwt-go version: 3.0.0 - package: github.com/go-sql-driver/mysql @@ -58,9 +58,7 @@ import: - package: github.com/spf13/cobra - package: github.com/spf13/viper - package: github.com/square/go-jose - version: ~1.1.0 - subpackages: - - json + version: 2.1.3 - package: github.com/stretchr/testify version: 1.1.4 subpackages: diff --git a/jwk/cast.go b/jwk/cast.go index 9f58fdad5d5..845a7844b98 100644 --- a/jwk/cast.go +++ b/jwk/cast.go @@ -7,7 +7,7 @@ import ( "github.com/square/go-jose" ) -func MustRSAPublic(key *jose.JsonWebKey) *rsa.PublicKey { +func MustRSAPublic(key *jose.JSONWebKey) *rsa.PublicKey { res, err := ToRSAPublic(key) if err != nil { panic(err.Error()) @@ -16,7 +16,7 @@ func MustRSAPublic(key *jose.JsonWebKey) *rsa.PublicKey { } -func ToRSAPublic(key *jose.JsonWebKey) (*rsa.PublicKey, error) { +func ToRSAPublic(key *jose.JSONWebKey) (*rsa.PublicKey, error) { res, ok := key.Key.(*rsa.PublicKey) if !ok { return res, errors.New("Could not convert key to RSA Private Key.") @@ -24,7 +24,7 @@ func ToRSAPublic(key *jose.JsonWebKey) (*rsa.PublicKey, error) { return res, nil } -func MustRSAPrivate(key *jose.JsonWebKey) *rsa.PrivateKey { +func MustRSAPrivate(key *jose.JSONWebKey) *rsa.PrivateKey { res, err := ToRSAPrivate(key) if err != nil { panic(err.Error()) @@ -32,7 +32,7 @@ func MustRSAPrivate(key *jose.JsonWebKey) *rsa.PrivateKey { return res } -func ToRSAPrivate(key *jose.JsonWebKey) (*rsa.PrivateKey, error) { +func ToRSAPrivate(key *jose.JSONWebKey) (*rsa.PrivateKey, error) { res, ok := key.Key.(*rsa.PrivateKey) if !ok { return res, errors.New("Could not convert key to RSA Private Key.") diff --git a/jwk/generator.go b/jwk/generator.go index 008d024c84c..04e73cefaa1 100644 --- a/jwk/generator.go +++ b/jwk/generator.go @@ -3,5 +3,5 @@ package jwk import "github.com/square/go-jose" type KeyGenerator interface { - Generate(id string) (*jose.JsonWebKeySet, error) + Generate(id string) (*jose.JSONWebKeySet, error) } diff --git a/jwk/generator_ecdsa256.go b/jwk/generator_ecdsa256.go index 1b84697119d..71552fe5e42 100644 --- a/jwk/generator_ecdsa256.go +++ b/jwk/generator_ecdsa256.go @@ -12,14 +12,14 @@ import ( type ECDSA256Generator struct{} -func (g *ECDSA256Generator) Generate(id string) (*jose.JsonWebKeySet, error) { +func (g *ECDSA256Generator) Generate(id string) (*jose.JSONWebKeySet, error) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, errors.Errorf("Could not generate key because %s", err) } - return &jose.JsonWebKeySet{ - Keys: []jose.JsonWebKey{ + return &jose.JSONWebKeySet{ + Keys: []jose.JSONWebKey{ { Key: key, KeyID: ider("private", id), diff --git a/jwk/generator_ecdsa521.go b/jwk/generator_ecdsa521.go index 8f1f5737401..600b5afdb31 100644 --- a/jwk/generator_ecdsa521.go +++ b/jwk/generator_ecdsa521.go @@ -12,14 +12,14 @@ import ( type ECDSA521Generator struct{} -func (g *ECDSA521Generator) Generate(id string) (*jose.JsonWebKeySet, error) { +func (g *ECDSA521Generator) Generate(id string) (*jose.JSONWebKeySet, error) { key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) if err != nil { return nil, errors.Errorf("Could not generate key because %s", err) } - return &jose.JsonWebKeySet{ - Keys: []jose.JsonWebKey{ + return &jose.JSONWebKeySet{ + Keys: []jose.JSONWebKey{ { Key: key, KeyID: ider("private", id), diff --git a/jwk/generator_hs256.go b/jwk/generator_hs256.go index ff9615c09c7..c7dd3a54370 100644 --- a/jwk/generator_hs256.go +++ b/jwk/generator_hs256.go @@ -12,7 +12,7 @@ type HS256Generator struct { Length int } -func (g *HS256Generator) Generate(id string) (*jose.JsonWebKeySet, error) { +func (g *HS256Generator) Generate(id string) (*jose.JSONWebKeySet, error) { if g.Length < 12 { g.Length = 12 } @@ -26,8 +26,8 @@ func (g *HS256Generator) Generate(id string) (*jose.JsonWebKeySet, error) { return nil, errors.Errorf("Could not generate key because %s", err) } - return &jose.JsonWebKeySet{ - Keys: []jose.JsonWebKey{ + return &jose.JSONWebKeySet{ + Keys: []jose.JSONWebKey{ { Algorithm: "HS256", Key: []byte(string(key)), diff --git a/jwk/generator_rs256.go b/jwk/generator_rs256.go index b6d9a4b4e6b..8b13a54cd3f 100644 --- a/jwk/generator_rs256.go +++ b/jwk/generator_rs256.go @@ -14,7 +14,7 @@ type RS256Generator struct { KeyLength int } -func (g *RS256Generator) Generate(id string) (*jose.JsonWebKeySet, error) { +func (g *RS256Generator) Generate(id string) (*jose.JSONWebKeySet, error) { if g.KeyLength < 4096 { g.KeyLength = 4096 } @@ -28,8 +28,8 @@ func (g *RS256Generator) Generate(id string) (*jose.JsonWebKeySet, error) { // jose does not support this... key.Precomputed = rsa.PrecomputedValues{} - return &jose.JsonWebKeySet{ - Keys: []jose.JsonWebKey{ + return &jose.JSONWebKeySet{ + Keys: []jose.JSONWebKey{ { Algorithm: "RS256", Key: key, diff --git a/jwk/generator_test.go b/jwk/generator_test.go index a155bac2722..0d240e1da08 100644 --- a/jwk/generator_test.go +++ b/jwk/generator_test.go @@ -17,23 +17,23 @@ func TestGenerator(t *testing.T) { for k, c := range []struct { g KeyGenerator - check func(*jose.JsonWebKeySet) + check func(*jose.JSONWebKeySet) }{ { g: &RS256Generator{}, - check: func(ks *jose.JsonWebKeySet) { + check: func(ks *jose.JSONWebKeySet) { assert.Len(t, ks, 2) }, }, { g: &ECDSA521Generator{}, - check: func(ks *jose.JsonWebKeySet) { + check: func(ks *jose.JSONWebKeySet) { assert.Len(t, ks, 2) }, }, { g: &ECDSA256Generator{}, - check: func(ks *jose.JsonWebKeySet) { + check: func(ks *jose.JSONWebKeySet) { assert.Len(t, ks, 2) }, }, @@ -41,7 +41,7 @@ func TestGenerator(t *testing.T) { g: &HS256Generator{ Length: 32, }, - check: func(ks *jose.JsonWebKeySet) { + check: func(ks *jose.JSONWebKeySet) { assert.Len(t, ks, 1) }, }, diff --git a/jwk/handler.go b/jwk/handler.go index 65a91543d56..3adb972e7fe 100644 --- a/jwk/handler.go +++ b/jwk/handler.go @@ -28,6 +28,7 @@ func (h *Handler) GetGenerators() map[string]KeyGenerator { if h.Generators == nil || len(h.Generators) == 0 { h.Generators = map[string]KeyGenerator{ "RS256": &RS256Generator{}, + "ES256": &ECDSA256Generator{}, "ES521": &ECDSA521Generator{}, "HS256": &HS256Generator{ Length: 32, @@ -53,7 +54,7 @@ func (h *Handler) SetRoutes(r *httprouter.Router) { // swagger:model jsonWebKeySetGeneratorRequest type createRequest struct { - // The algorithm to be used for creating the key. Supports "RS256", "ES521" and "HS256" + // The algorithm to be used for creating the key. Supports "RS256", "ES256", "ES521" and "HS256" // required: true // in: body Algorithm string `json:"alg"` @@ -354,7 +355,7 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request, ps httprouter.P func (h *Handler) UpdateKeySet(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { var ctx = context.Background() var requests joseWebKeySetRequest - var keySet = new(jose.JsonWebKeySet) + var keySet = new(jose.JSONWebKeySet) var set = ps.ByName("set") if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{ @@ -371,7 +372,7 @@ func (h *Handler) UpdateKeySet(w http.ResponseWriter, r *http.Request, ps httpro } for _, request := range requests.Keys { - key := &jose.JsonWebKey{} + key := &jose.JSONWebKey{} if err := key.UnmarshalJSON(request); err != nil { h.H.WriteError(w, r, errors.WithStack(err)) } @@ -421,7 +422,7 @@ func (h *Handler) UpdateKeySet(w http.ResponseWriter, r *http.Request, ps httpro // 500: genericError func (h *Handler) UpdateKey(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { var ctx = context.Background() - var key jose.JsonWebKey + var key jose.JSONWebKey var set = ps.ByName("set") if err := json.NewDecoder(r.Body).Decode(&key); err != nil { diff --git a/jwk/handler_test.go b/jwk/handler_test.go index f531af4894d..6a52df3970f 100644 --- a/jwk/handler_test.go +++ b/jwk/handler_test.go @@ -18,7 +18,7 @@ import ( ) var testServer *httptest.Server -var IDKS *jose.JsonWebKeySet +var IDKS *jose.JSONWebKeySet func init() { localWarden, _ := compose.NewMockFirewall( @@ -38,7 +38,7 @@ func init() { }, ) router := httprouter.New() - IDKS, _ = testGenerator.Generate("") + IDKS, _ = testGenerators["RS256"].Generate("") h := Handler{ Manager: &MemoryManager{}, @@ -57,7 +57,7 @@ func TestHandlerWellKnown(t *testing.T) { require.NoError(t, err, "problem in http request") defer res.Body.Close() - var known jose.JsonWebKeySet + var known jose.JSONWebKeySet err = json.NewDecoder(res.Body).Decode(&known) require.NoError(t, err, "problem in decoding response") diff --git a/jwk/helper.go b/jwk/helper.go index 67465286929..b522b7b2992 100644 --- a/jwk/helper.go +++ b/jwk/helper.go @@ -10,7 +10,7 @@ import ( "github.com/square/go-jose" ) -func First(keys []jose.JsonWebKey) *jose.JsonWebKey { +func First(keys []jose.JSONWebKey) *jose.JSONWebKey { if len(keys) == 0 { return nil } diff --git a/jwk/manager.go b/jwk/manager.go index b4da6533ef7..efd2a23e11b 100644 --- a/jwk/manager.go +++ b/jwk/manager.go @@ -3,13 +3,13 @@ package jwk import "github.com/square/go-jose" type Manager interface { - AddKey(set string, key *jose.JsonWebKey) error + AddKey(set string, key *jose.JSONWebKey) error - AddKeySet(set string, keys *jose.JsonWebKeySet) error + AddKeySet(set string, keys *jose.JSONWebKeySet) error - GetKey(set, kid string) (*jose.JsonWebKeySet, error) + GetKey(set, kid string) (*jose.JSONWebKeySet, error) - GetKeySet(set string) (*jose.JsonWebKeySet, error) + GetKeySet(set string) (*jose.JSONWebKeySet, error) DeleteKey(set, kid string) error diff --git a/jwk/manager_memory.go b/jwk/manager_memory.go index f77b9a3c9e1..4dcc9356825 100644 --- a/jwk/manager_memory.go +++ b/jwk/manager_memory.go @@ -9,30 +9,30 @@ import ( ) type MemoryManager struct { - Keys map[string]*jose.JsonWebKeySet + Keys map[string]*jose.JSONWebKeySet sync.RWMutex } -func (m *MemoryManager) AddKey(set string, key *jose.JsonWebKey) error { +func (m *MemoryManager) AddKey(set string, key *jose.JSONWebKey) error { m.Lock() defer m.Unlock() m.alloc() if m.Keys[set] == nil { - m.Keys[set] = &jose.JsonWebKeySet{Keys: []jose.JsonWebKey{}} + m.Keys[set] = &jose.JSONWebKeySet{Keys: []jose.JSONWebKey{}} } m.Keys[set].Keys = append(m.Keys[set].Keys, *key) return nil } -func (m *MemoryManager) AddKeySet(set string, keys *jose.JsonWebKeySet) error { +func (m *MemoryManager) AddKeySet(set string, keys *jose.JSONWebKeySet) error { for _, key := range keys.Keys { m.AddKey(set, &key) } return nil } -func (m *MemoryManager) GetKey(set, kid string) (*jose.JsonWebKeySet, error) { +func (m *MemoryManager) GetKey(set, kid string) (*jose.JSONWebKeySet, error) { m.RLock() defer m.RUnlock() @@ -47,12 +47,12 @@ func (m *MemoryManager) GetKey(set, kid string) (*jose.JsonWebKeySet, error) { return nil, errors.Wrap(pkg.ErrNotFound, "") } - return &jose.JsonWebKeySet{ + return &jose.JSONWebKeySet{ Keys: result, }, nil } -func (m *MemoryManager) GetKeySet(set string) (*jose.JsonWebKeySet, error) { +func (m *MemoryManager) GetKeySet(set string) (*jose.JSONWebKeySet, error) { m.RLock() defer m.RUnlock() @@ -72,7 +72,7 @@ func (m *MemoryManager) DeleteKey(set, kid string) error { } m.Lock() - var results []jose.JsonWebKey + var results []jose.JSONWebKey for _, key := range keys.Keys { if key.KeyID != kid { results = append(results) @@ -94,6 +94,6 @@ func (m *MemoryManager) DeleteKeySet(set string) error { func (m *MemoryManager) alloc() { if m.Keys == nil { - m.Keys = make(map[string]*jose.JsonWebKeySet) + m.Keys = make(map[string]*jose.JSONWebKeySet) } } diff --git a/jwk/manager_sql.go b/jwk/manager_sql.go index 278f3490459..d3f8157d536 100644 --- a/jwk/manager_sql.go +++ b/jwk/manager_sql.go @@ -52,7 +52,7 @@ func (s *SQLManager) CreateSchemas() (int, error) { return n, nil } -func (m *SQLManager) AddKey(set string, key *jose.JsonWebKey) error { +func (m *SQLManager) AddKey(set string, key *jose.JSONWebKey) error { out, err := json.Marshal(key) if err != nil { return errors.WithStack(err) @@ -74,7 +74,7 @@ func (m *SQLManager) AddKey(set string, key *jose.JsonWebKey) error { return nil } -func (m *SQLManager) AddKeySet(set string, keys *jose.JsonWebKeySet) error { +func (m *SQLManager) AddKeySet(set string, keys *jose.JSONWebKeySet) error { tx, err := m.DB.Beginx() if err != nil { return errors.WithStack(err) @@ -119,7 +119,7 @@ func (m *SQLManager) AddKeySet(set string, keys *jose.JsonWebKeySet) error { return nil } -func (m *SQLManager) GetKey(set, kid string) (*jose.JsonWebKeySet, error) { +func (m *SQLManager) GetKey(set, kid string) (*jose.JSONWebKeySet, error) { var d sqlData if err := m.DB.Get(&d, m.DB.Rebind("SELECT * FROM hydra_jwk WHERE sid=? AND kid=?"), set, kid); err == sql.ErrNoRows { return nil, errors.Wrap(pkg.ErrNotFound, "") @@ -132,17 +132,17 @@ func (m *SQLManager) GetKey(set, kid string) (*jose.JsonWebKeySet, error) { return nil, errors.WithStack(err) } - var c jose.JsonWebKey + var c jose.JSONWebKey if err := json.Unmarshal(key, &c); err != nil { return nil, errors.WithStack(err) } - return &jose.JsonWebKeySet{ - Keys: []jose.JsonWebKey{c}, + return &jose.JSONWebKeySet{ + Keys: []jose.JSONWebKey{c}, }, nil } -func (m *SQLManager) GetKeySet(set string) (*jose.JsonWebKeySet, error) { +func (m *SQLManager) GetKeySet(set string) (*jose.JSONWebKeySet, error) { var ds []sqlData if err := m.DB.Select(&ds, m.DB.Rebind("SELECT * FROM hydra_jwk WHERE sid=?"), set); err == sql.ErrNoRows { return nil, errors.Wrap(pkg.ErrNotFound, "") @@ -154,14 +154,14 @@ func (m *SQLManager) GetKeySet(set string) (*jose.JsonWebKeySet, error) { return nil, errors.Wrap(pkg.ErrNotFound, "") } - keys := &jose.JsonWebKeySet{Keys: []jose.JsonWebKey{}} + keys := &jose.JSONWebKeySet{Keys: []jose.JSONWebKey{}} for _, d := range ds { key, err := m.Cipher.Decrypt(d.Key) if err != nil { return nil, errors.WithStack(err) } - var c jose.JsonWebKey + var c jose.JSONWebKey if err := json.Unmarshal(key, &c); err != nil { return nil, errors.WithStack(err) } diff --git a/jwk/manager_test.go b/jwk/manager_test.go index 3f4e3ec3844..978a72375ea 100644 --- a/jwk/manager_test.go +++ b/jwk/manager_test.go @@ -15,7 +15,7 @@ var managers = map[string]Manager{ "memory": new(MemoryManager), } -var testGenerator = &RS256Generator{} +var testGenerators = (&Handler{}).GetGenerators() var encryptionKey, _ = RandomBytes(32) @@ -53,22 +53,37 @@ func connectToMySQL() { } func TestManagerKey(t *testing.T) { - ks, _ := testGenerator.Generate("") - - for name, m := range managers { - t.Run(fmt.Sprintf("case=%s", name), func(t *testing.T) { - TestHelperManagerKey(m, ks)(t) - }) + for algo, testGenerator := range testGenerators { + if algo == "HS256" { + // this is a symmetrical algorithm + continue + } + + ks, err := testGenerator.Generate("") + if err != nil { + t.Fatal(err) + } + + for name, m := range managers { + t.Run(fmt.Sprintf("case=%s/%s", algo, name), func(t *testing.T) { + TestHelperManagerKey(m, algo, ks)(t) + }) + } } } func TestManagerKeySet(t *testing.T) { - ks, _ := testGenerator.Generate("") - ks.Key("private") - - for name, m := range managers { - t.Run(fmt.Sprintf("case=%s", name), func(t *testing.T) { - TestHelperManagerKeySet(m, ks)(t) - }) + for algo, testGenerator := range testGenerators { + ks, err := testGenerator.Generate("") + if err != nil { + t.Fatal(err) + } + ks.Key("private") + + for name, m := range managers { + t.Run(fmt.Sprintf("case=%s/%s", algo, name), func(t *testing.T) { + TestHelperManagerKeySet(m, algo, ks)(t) + }) + } } } diff --git a/jwk/manager_test_helpers.go b/jwk/manager_test_helpers.go index e62df5b618b..51beee4c490 100644 --- a/jwk/manager_test_helpers.go +++ b/jwk/manager_test_helpers.go @@ -19,57 +19,57 @@ func RandomBytes(n int) ([]byte, error) { return bytes, nil } -func TestHelperManagerKey(m Manager, keys *jose.JsonWebKeySet) func(t *testing.T) { +func TestHelperManagerKey(m Manager, name string, keys *jose.JSONWebKeySet) func(t *testing.T) { pub := keys.Key("public") priv := keys.Key("private") return func(t *testing.T) { - _, err := m.GetKey("faz", "baz") + _, err := m.GetKey(name+"faz", "baz") assert.NotNil(t, err) - err = m.AddKey("faz", First(priv)) + err = m.AddKey(name+"faz", First(priv)) assert.Nil(t, err) - got, err := m.GetKey("faz", "private") + got, err := m.GetKey(name+"faz", "private") assert.Nil(t, err) assert.Equal(t, priv, got.Keys) - err = m.AddKey("faz", First(pub)) + err = m.AddKey(name+"faz", First(pub)) assert.Nil(t, err) - got, err = m.GetKey("faz", "private") + got, err = m.GetKey(name+"faz", "private") assert.Nil(t, err) assert.Equal(t, priv, got.Keys) - got, err = m.GetKey("faz", "public") + got, err = m.GetKey(name+"faz", "public") assert.Nil(t, err) assert.Equal(t, pub, got.Keys) - err = m.DeleteKey("faz", "public") + err = m.DeleteKey(name+"faz", "public") assert.Nil(t, err) - _, err = m.GetKey("faz", "public") + _, err = m.GetKey(name+"faz", "public") assert.NotNil(t, err) } } -func TestHelperManagerKeySet(m Manager, keys *jose.JsonWebKeySet) func(t *testing.T) { +func TestHelperManagerKeySet(m Manager, name string, keys *jose.JSONWebKeySet) func(t *testing.T) { return func(t *testing.T) { - _, err := m.GetKeySet("foo") + _, err := m.GetKeySet(name + "foo") require.Error(t, err) - err = m.AddKeySet("bar", keys) + err = m.AddKeySet(name+"bar", keys) assert.Nil(t, err) - got, err := m.GetKeySet("bar") + got, err := m.GetKeySet(name + "bar") assert.Nil(t, err) assert.Equal(t, keys.Key("public"), got.Key("public")) assert.Equal(t, keys.Key("private"), got.Key("private")) - err = m.DeleteKeySet("bar") + err = m.DeleteKeySet(name + "bar") assert.Nil(t, err) - _, err = m.GetKeySet("bar") + _, err = m.GetKeySet(name + "bar") assert.NotNil(t, err) } } diff --git a/warden/group/doc.go b/warden/group/doc.go index 6758ed73a66..493a119258a 100644 --- a/warden/group/doc.go +++ b/warden/group/doc.go @@ -4,19 +4,27 @@ package group // A list of groups the member is belonging to -// swagger:response findGroupsByMemberResponse -type swaggerFindGroupsByMemberResponse struct { +// swagger:response listGroupsResponse +type swaggerListGroupsResponse struct { // in: body // type: array Body []Group } -// swagger:parameters findGroupsByMember -type swaggerFindGroupsByMemberParameters struct { +// swagger:parameters listGroups +type swaggerListGroupsParameters struct { // The id of the member to look up. // in: query // required: true Member string `json:"member"` + + // The offset from where to start looking if member isn't specified. + // in: query + Offset int `json:"offset"` + + // The maximum amount of policies returned if member isn't specified. + // in: query + Limit int `json:"limit"` } // swagger:parameters createGroup diff --git a/warden/group/handler.go b/warden/group/handler.go index d2f2e6d7d92..e57194c78b4 100644 --- a/warden/group/handler.go +++ b/warden/group/handler.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" "github.com/julienschmidt/httprouter" "github.com/ory/herodot" @@ -41,9 +42,9 @@ func (h *Handler) SetRoutes(r *httprouter.Router) { r.DELETE(GroupsHandlerPath+"/:id/members", h.RemoveGroupMembers) } -// swagger:route GET /warden/groups warden findGroupsByMember +// swagger:route GET /warden/groups warden listGroups // -// Find groups by member +// List groups // // The subject making the request needs to be assigned to a policy containing: // @@ -67,7 +68,7 @@ func (h *Handler) SetRoutes(r *httprouter.Router) { // oauth2: hydra.groups // // Responses: -// 200: findGroupsByMemberResponse +// 200: listGroupsResponse // 401: genericError // 403: genericError // 500: genericError @@ -75,15 +76,42 @@ func (h *Handler) FindGroupNames(w http.ResponseWriter, r *http.Request, _ httpr var ctx = r.Context() var member = r.URL.Query().Get("member") - if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{ + accessReq := &firewall.TokenAccessRequest{ Resource: GroupsResource, Action: "list", - }, Scope); err != nil { + } + + if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), accessReq, Scope); err != nil { h.H.WriteError(w, r, err) return } - groups, err := h.Manager.FindGroupsByMember(member) + if member != "" { + groups, err := h.Manager.FindGroupsByMember(member) + + if err != nil { + h.H.WriteError(w, r, err) + return + } + + h.H.Write(w, r, groups) + return + } + + limit, err := intFromQuery(r, "limit", 500) + if err != nil { + h.H.WriteError(w, r, errors.WithStack(err)) + return + } + + offset, err := intFromQuery(r, "offset", 0) + if err != nil { + h.H.WriteError(w, r, errors.WithStack(err)) + return + } + + groups, err := h.Manager.ListGroups(limit, offset) + if err != nil { h.H.WriteError(w, r, err) return @@ -92,6 +120,15 @@ func (h *Handler) FindGroupNames(w http.ResponseWriter, r *http.Request, _ httpr h.H.Write(w, r, groups) } +func intFromQuery(r *http.Request, key string, def int64) (int64, error) { + val := r.URL.Query().Get(key) + if val == "" { + return def, nil + } + + return strconv.ParseInt(val, 10, 64) +} + // swagger:route POST /warden/groups warden createGroup // // Create a group diff --git a/warden/group/manager.go b/warden/group/manager.go index 593dfd042f6..a489a573376 100644 --- a/warden/group/manager.go +++ b/warden/group/manager.go @@ -19,5 +19,6 @@ type Manager interface { AddGroupMembers(group string, members []string) error RemoveGroupMembers(group string, members []string) error + ListGroups(limit, offset int64) ([]Group, error) FindGroupsByMember(subject string) ([]Group, error) } diff --git a/warden/group/manager_memory.go b/warden/group/manager_memory.go index d5bae4b2c83..b50fa1d4bd9 100644 --- a/warden/group/manager_memory.go +++ b/warden/group/manager_memory.go @@ -1,6 +1,7 @@ package group import ( + "sort" "sync" "github.com/ory/hydra/pkg" @@ -19,6 +20,8 @@ type MemoryManager struct { sync.RWMutex } +var _ Manager = (*MemoryManager)(nil) + func (m *MemoryManager) CreateGroup(g *Group) error { if g.ID == "" { g.ID = uuid.New() @@ -77,6 +80,40 @@ func (m *MemoryManager) RemoveGroupMembers(group string, subjects []string) erro return m.CreateGroup(g) } +func (m *MemoryManager) ListGroups(limit, offset int64) ([]Group, error) { + if limit <= 0 { + limit = 500 + } + + if offset < 0 { + offset = 0 + } + + if offset >= int64(len(m.Groups)) { + return []Group{}, nil + } + + ids := []string{} + for id := range m.Groups { + ids = append(ids, id) + } + + sort.Strings(ids) + + res := make([]Group, len(ids)) + for i, id := range ids { + res[i] = m.Groups[id] + } + + res = res[offset:] + + if limit < int64(len(res)) { + res = res[:limit] + } + + return res, nil +} + func (m *MemoryManager) FindGroupsByMember(subject string) ([]Group, error) { if m.Groups == nil { m.Groups = map[string]Group{} diff --git a/warden/group/manager_sql.go b/warden/group/manager_sql.go index 86907ddf3ad..675c9cd17d1 100644 --- a/warden/group/manager_sql.go +++ b/warden/group/manager_sql.go @@ -34,6 +34,8 @@ type SQLManager struct { DB *sqlx.DB } +var _ Manager = (*SQLManager)(nil) + func (m *SQLManager) CreateSchemas() (int, error) { migrate.SetTable("hydra_groups_migration") n, err := migrate.Exec(m.DB.DB, m.DB.DriverName(), migrations, migrate.Up) @@ -128,14 +130,7 @@ func (m *SQLManager) RemoveGroupMembers(group string, subjects []string) error { return nil } -func (m *SQLManager) FindGroupsByMember(subject string) ([]Group, error) { - var ids []string - if err := m.DB.Select(&ids, m.DB.Rebind("SELECT group_id from hydra_warden_group_member WHERE member = ? GROUP BY group_id"), subject); err == sql.ErrNoRows { - return nil, errors.WithStack(pkg.ErrNotFound) - } else if err != nil { - return nil, errors.WithStack(err) - } - +func (m *SQLManager) idsToGroups(ids []string) ([]Group, error) { var groups = make([]Group, len(ids)) for k, id := range ids { group, err := m.GetGroup(id) @@ -148,3 +143,31 @@ func (m *SQLManager) FindGroupsByMember(subject string) ([]Group, error) { return groups, nil } + +func (m *SQLManager) ListGroups(limit, offset int64) ([]Group, error) { + if limit <= 0 { + limit = 500 + } + + if offset < 0 { + offset = 0 + } + + var ids []string + if err := m.DB.Select(&ids, m.DB.Rebind("SELECT id from hydra_warden_group ORDER BY id LIMIT ? OFFSET ?"), limit, offset); err != nil { + return nil, errors.WithStack(err) + } + + return m.idsToGroups(ids) +} + +func (m *SQLManager) FindGroupsByMember(subject string) ([]Group, error) { + var ids []string + if err := m.DB.Select(&ids, m.DB.Rebind("SELECT group_id from hydra_warden_group_member WHERE member = ? GROUP BY group_id"), subject); err == sql.ErrNoRows { + return nil, errors.WithStack(pkg.ErrNotFound) + } else if err != nil { + return nil, errors.WithStack(err) + } + + return m.idsToGroups(ids) +} diff --git a/warden/group/manager_test.go b/warden/group/manager_test.go index 01af2da2d99..aaab24959e3 100644 --- a/warden/group/manager_test.go +++ b/warden/group/manager_test.go @@ -52,6 +52,8 @@ func connectToPG() { } func TestManagers(t *testing.T) { + t.Parallel() + for k, m := range clientManagers { t.Run(fmt.Sprintf("case=%s", k), TestHelperManagers(m)) } diff --git a/warden/group/manager_test_helper.go b/warden/group/manager_test_helper.go index 98d116876cc..0be40102cc9 100644 --- a/warden/group/manager_test_helper.go +++ b/warden/group/manager_test_helper.go @@ -9,7 +9,27 @@ import ( func TestHelperManagers(m Manager) func(t *testing.T) { return func(t *testing.T) { - _, err := m.GetGroup("4321") + ds, err := m.ListGroups(0, 0) + assert.NoError(t, err) + assert.Empty(t, ds) + assert.NotNil(t, ds) + + ds, err = m.ListGroups(-1, 0) + assert.NoError(t, err) + assert.Empty(t, ds) + assert.NotNil(t, ds) + + ds, err = m.ListGroups(0, -1) + assert.NoError(t, err) + assert.Empty(t, ds) + assert.NotNil(t, ds) + + ds, err = m.ListGroups(-1, -1) + assert.NoError(t, err) + assert.Empty(t, ds) + assert.NotNil(t, ds) + + _, err = m.GetGroup("4321") assert.NotNil(t, err) c := &Group{ @@ -17,33 +37,94 @@ func TestHelperManagers(m Manager) func(t *testing.T) { Members: []string{"bar", "foo"}, } assert.NoError(t, m.CreateGroup(c)) + ds, err = m.ListGroups(0, 0) + require.NoError(t, err) + assert.Len(t, ds, 1) + assert.NotNil(t, ds) + + ds, err = m.ListGroups(0, 1) + require.NoError(t, err) + assert.Len(t, ds, 0) + assert.NotNil(t, ds) + assert.NoError(t, m.CreateGroup(&Group{ ID: "2", Members: []string{"foo"}, })) + ds, err = m.ListGroups(0, 0) + require.NoError(t, err) + assert.Len(t, ds, 2) + assert.NotNil(t, ds) + + ds, err = m.ListGroups(0, 1) + require.NoError(t, err) + assert.Len(t, ds, 1) + assert.NotNil(t, ds) + + ds, err = m.ListGroups(0, 2) + require.NoError(t, err) + assert.Len(t, ds, 0) + assert.NotNil(t, ds) + + ds, err = m.ListGroups(1, 0) + require.NoError(t, err) + assert.Len(t, ds, 1) + assert.NotNil(t, ds) + + ds, err = m.ListGroups(2, 0) + require.NoError(t, err) + assert.Len(t, ds, 2) + assert.NotNil(t, ds) + + ds, err = m.ListGroups(1, 1) + require.NoError(t, err) + assert.Len(t, ds, 1) + assert.NotNil(t, ds) + + ds, err = m.ListGroups(0, 2) + require.NoError(t, err) + assert.Len(t, ds, 0) + assert.NotNil(t, ds) + + assert.NoError(t, m.CreateGroup(&Group{ + ID: "3", + Members: []string{"bar"}, + })) + ds, err = m.ListGroups(0, 0) + require.NoError(t, err) + assert.Len(t, ds, 3) + assert.NotNil(t, ds) d, err := m.GetGroup("1") require.NoError(t, err) assert.EqualValues(t, c.Members, d.Members) assert.EqualValues(t, c.ID, d.ID) - ds, err := m.FindGroupsByMember("foo") + ds, err = m.FindGroupsByMember("foo") require.NoError(t, err) assert.Len(t, ds, 2) + assert.NotNil(t, ds) assert.NoError(t, m.AddGroupMembers("1", []string{"baz"})) ds, err = m.FindGroupsByMember("baz") require.NoError(t, err) assert.Len(t, ds, 1) + assert.NotNil(t, ds) assert.NoError(t, m.RemoveGroupMembers("1", []string{"baz"})) ds, err = m.FindGroupsByMember("baz") require.NoError(t, err) assert.Len(t, ds, 0) + assert.NotNil(t, ds) assert.NoError(t, m.DeleteGroup("1")) _, err = m.GetGroup("1") require.NotNil(t, err) + + ds, err = m.ListGroups(0, 0) + require.NoError(t, err) + assert.Len(t, ds, 2) + assert.NotNil(t, ds) } }