Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

client: Introduces pagination to client management #774

Merged
merged 2 commits into from
Feb 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions client/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ type swaggerUpdateClientPayload struct {
Body Client
}

// swagger:parameters listOAuth2Clients
type swaggerListClientsParameter struct {

// The maximum amount of policies returned.
// in: query
Limit int `json:"limit"`

// The offset from where to start looking.
// in: query
Offset int `json:"offset"`
}

// A list of clients.
// swagger:response oAuth2ClientList
type swaggerListClientsResult struct {
Expand Down
4 changes: 3 additions & 1 deletion client/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/ory/hydra/firewall"
"github.com/ory/hydra/rand/sequence"
"github.com/ory/ladon"
"github.com/ory/pagination"
"github.com/pkg/errors"
)

Expand Down Expand Up @@ -290,7 +291,8 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request, ps httprouter.Par
return
}

c, err := h.Manager.GetClients()
limit, offset := pagination.Parse(r, 100, 0, 500)
c, err := h.Manager.GetClients(limit, offset)
if err != nil {
h.H.WriteError(w, r, err)
return
Expand Down
2 changes: 1 addition & 1 deletion client/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type Storage interface {

DeleteClient(id string) error

GetClients() (map[string]Client, error)
GetClients(limit, offset int) (map[string]Client, error)

GetConcreteClient(id string) (*Client, error)
}
52 changes: 37 additions & 15 deletions client/manager_memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import (
"github.com/imdario/mergo"
"github.com/ory/fosite"
"github.com/ory/hydra/pkg"
"github.com/ory/pagination"
"github.com/pborman/uuid"
"github.com/pkg/errors"
)

type MemoryManager struct {
Clients map[string]Client
Clients []Client
Hasher fosite.Hasher
sync.RWMutex
}
Expand All @@ -37,7 +38,7 @@ func NewMemoryManager(hasher fosite.Hasher) *MemoryManager {
}

return &MemoryManager{
Clients: map[string]Client{},
Clients: []Client{},
Hasher: hasher,
}
}
Expand All @@ -46,11 +47,13 @@ func (m *MemoryManager) GetConcreteClient(id string) (*Client, error) {
m.RLock()
defer m.RUnlock()

c, ok := m.Clients[id]
if !ok {
return nil, errors.Wrap(pkg.ErrNotFound, "")
for _, c := range m.Clients {
if c.GetID() == id {
return &c, nil
}
}
return &c, nil

return nil, errors.Wrap(pkg.ErrNotFound, "")
}

func (m *MemoryManager) GetClient(_ context.Context, id string) (fosite.Client, error) {
Expand All @@ -76,27 +79,38 @@ func (m *MemoryManager) UpdateClient(c *Client) error {
return errors.WithStack(err)
}

m.Clients[c.GetID()] = *c
m.Lock()
defer m.Unlock()
for k, f := range m.Clients {
if f.GetID() == c.ID {
m.Clients[k] = *c
}
}

return nil
}

func (m *MemoryManager) Authenticate(id string, secret []byte) (*Client, error) {
m.RLock()
defer m.RUnlock()

c, ok := m.Clients[id]
if !ok {
return nil, errors.Wrap(pkg.ErrNotFound, "")
c, err := m.GetConcreteClient(id)
if err != nil {
return nil, err
}

if err := m.Hasher.Compare(c.GetHashedSecret(), secret); err != nil {
return nil, errors.WithStack(err)
}

return &c, nil
return c, nil
}

func (m *MemoryManager) CreateClient(c *Client) error {
if _, err := m.GetConcreteClient(c.ID); err == nil {
return errors.Errorf("Client %s already exists", c.ID)
}

m.Lock()
defer m.Unlock()

Expand All @@ -110,23 +124,31 @@ func (m *MemoryManager) CreateClient(c *Client) error {
}
c.Secret = string(hash)

m.Clients[c.GetID()] = *c
m.Clients = append(m.Clients, *c)
return nil
}

func (m *MemoryManager) DeleteClient(id string) error {
m.Lock()
defer m.Unlock()

delete(m.Clients, id)
for k, f := range m.Clients {
if f.GetID() == id {
m.Clients = append(m.Clients[:k], m.Clients[k+1:]...)
return nil
}
}

return nil
}

func (m *MemoryManager) GetClients() (clients map[string]Client, err error) {
func (m *MemoryManager) GetClients(limit, offset int) (clients map[string]Client, err error) {
m.RLock()
defer m.RUnlock()
clients = make(map[string]Client)
for _, c := range m.Clients {

start, end := pagination.Index(limit, offset, len(m.Clients))
for _, c := range m.Clients[start:end] {
clients[c.ID] = c
}

Expand Down
6 changes: 3 additions & 3 deletions client/manager_sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,11 @@ func (m *SQLManager) DeleteClient(id string) error {
return nil
}

func (m *SQLManager) GetClients() (clients map[string]Client, err error) {
var d = []sqlData{}
func (m *SQLManager) GetClients(limit, offset int) (clients map[string]Client, err error) {
d := make([]sqlData, 0)
clients = make(map[string]Client)

if err := m.DB.Select(&d, "SELECT * FROM hydra_client"); err != nil {
if err := m.DB.Select(&d, m.DB.Rebind("SELECT * FROM hydra_client ORDER BY id LIMIT ? OFFSET ?"), limit, offset); err != nil {
return nil, errors.WithStack(err)
}

Expand Down
5 changes: 1 addition & 4 deletions client/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ import (
var clientManagers = map[string]Manager{}

func init() {
clientManagers["memory"] = &MemoryManager{
Clients: map[string]Client{},
Hasher: &fosite.BCrypt{},
}
clientManagers["memory"] = NewMemoryManager(&fosite.BCrypt{})
}

func TestMain(m *testing.M) {
Expand Down
10 changes: 9 additions & 1 deletion client/manager_test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,19 @@ func TestHelperCreateGetDeleteClient(k string, m Storage) func(t *testing.T) {
compare(t, d, k)
}

ds, err := m.GetClients()
ds, err := m.GetClients(100, 0)
assert.NoError(t, err)
assert.Len(t, ds, 2)
assert.NotEqual(t, ds["1234"].ID, ds["2-1234"].ID)

ds, err = m.GetClients(1, 0)
assert.NoError(t, err)
assert.Len(t, ds, 1)

ds, err = m.GetClients(100, 100)
assert.NoError(t, err)
assert.Len(t, ds, 0)

err = m.UpdateClient(&Client{
ID: "2-1234",
Name: "name-new",
Expand Down
2 changes: 1 addition & 1 deletion client/sdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func TestClientSDK(t *testing.T) {
result, _, err = c.GetOAuth2Client(createClient.Id)
assert.EqualValues(t, compareClient, *result)

results, _, err := c.ListOAuth2Clients()
results, _, err := c.ListOAuth2Clients(100, 0)
require.NoError(t, err)
assert.Len(t, results, 1)
assert.EqualValues(t, compareClient, results[0])
Expand Down
5 changes: 1 addition & 4 deletions cmd/server/handler_client_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ func newClientManager(c *config.Config) client.Manager {

switch con := ctx.Connection.(type) {
case *config.MemoryConnection:
return &client.MemoryManager{
Clients: map[string]client.Client{},
Hasher: ctx.Hasher,
}
return client.NewMemoryManager(ctx.Hasher)
case *config.SQLConnection:
return &client.SQLManager{
DB: con.GetDatabase(),
Expand Down
2 changes: 1 addition & 1 deletion cmd/server/helper_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
func (h *Handler) createRootIfNewInstall(c *config.Config) {
ctx := c.Context()

clients, err := h.Clients.Manager.GetClients()
clients, err := h.Clients.Manager.GetClients(100, 0)
pkg.Must(err, "Could not fetch client list: %s", err)
if len(clients) != 0 {
return
Expand Down
18 changes: 18 additions & 0 deletions docs/api.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,24 @@
],
"summary": "List OAuth 2.0 Clients",
"operationId": "listOAuth2Clients",
"parameters": [
{
"type": "integer",
"format": "int64",
"x-go-name": "Limit",
"description": "The maximum amount of policies returned.",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"format": "int64",
"x-go-name": "Offset",
"description": "The offset from where to start looking.",
"name": "offset",
"in": "query"
}
],
"responses": {
"200": {
"$ref": "#/responses/oAuth2ClientList"
Expand Down
2 changes: 1 addition & 1 deletion oauth2/fosite_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (

var clientManagers = map[string]pkg.FositeStorer{}
var clientManager = &client.MemoryManager{
Clients: map[string]client.Client{"foobar": {ID: "foobar"}},
Clients: []client.Client{{ID: "foobar"}},
Hasher: &fosite.BCrypt{},
}

Expand Down
9 changes: 3 additions & 6 deletions oauth2/oauth2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,7 @@ import (
var hasher = &fosite.BCrypt{}

var store = &FositeMemoryStore{
Manager: &hc.MemoryManager{
Clients: map[string]hc.Client{},
Hasher: hasher,
},
Manager: hc.NewMemoryManager(hasher),
AuthorizeCodes: make(map[string]fosite.Requester),
IDSessions: make(map[string]fosite.Requester),
AccessTokens: make(map[string]fosite.Requester),
Expand Down Expand Up @@ -121,14 +118,14 @@ func init() {
c, _ := url.Parse(ts.URL + "/consent")
handler.ConsentURL = *c

store.Manager.(*hc.MemoryManager).Clients["app-client"] = hc.Client{
store.Manager.(*hc.MemoryManager).Clients = append(store.Manager.(*hc.MemoryManager).Clients, hc.Client{
ID: "app-client",
Secret: string(h),
RedirectURIs: []string{ts.URL + "/callback"},
ResponseTypes: []string{"id_token", "code", "token"},
GrantTypes: []string{"implicit", "refresh_token", "authorization_code", "password", "client_credentials"},
Scope: "hydra.* offline openid",
}
})

oauthConfig = &oauth2.Config{
ClientID: "app-client",
Expand Down
2 changes: 1 addition & 1 deletion sdk/go/hydra/sdk_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ type OAuth2API interface {
GetOAuth2ConsentRequest(id string) (*swagger.OAuth2ConsentRequest, *swagger.APIResponse, error)
GetWellKnown() (*swagger.WellKnown, *swagger.APIResponse, error)
IntrospectOAuth2Token(token string, scope string) (*swagger.OAuth2TokenIntrospection, *swagger.APIResponse, error)
ListOAuth2Clients() ([]swagger.OAuth2Client, *swagger.APIResponse, error)
ListOAuth2Clients(limit int64, offset int64) ([]swagger.OAuth2Client, *swagger.APIResponse, error)
RejectOAuth2ConsentRequest(id string, body swagger.ConsentRequestRejection) (*swagger.APIResponse, error)
RevokeOAuth2Token(token string) (*swagger.APIResponse, error)
UpdateOAuth2Client(id string, body swagger.OAuth2Client) (*swagger.OAuth2Client, *swagger.APIResponse, error)
Expand Down
8 changes: 6 additions & 2 deletions sdk/go/hydra/swagger/docs/OAuth2Api.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,19 @@ Name | Type | Description | Notes
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

# **ListOAuth2Clients**
> []OAuth2Client ListOAuth2Clients()
> []OAuth2Client ListOAuth2Clients($limit, $offset)

List OAuth 2.0 Clients

This endpoint never returns passwords. The subject making the request needs to be assigned to a policy containing: ``` { \"resources\": [\"rn:hydra:clients\"], \"actions\": [\"get\"], \"effect\": \"allow\" } ```


### Parameters
This endpoint does not need any parameter.

Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**limit** | **int64**| The maximum amount of policies returned. | [optional]
**offset** | **int64**| The offset from where to start looking. | [optional]

### Return type

Expand Down
6 changes: 5 additions & 1 deletion sdk/go/hydra/swagger/o_auth2_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,9 +573,11 @@ func (a OAuth2Api) IntrospectOAuth2Token(token string, scope string) (*OAuth2Tok
* List OAuth 2.0 Clients
* This endpoint never returns passwords. The subject making the request needs to be assigned to a policy containing: ``` { \"resources\": [\"rn:hydra:clients\"], \"actions\": [\"get\"], \"effect\": \"allow\" } ```
*
* @param limit The maximum amount of policies returned.
* @param offset The offset from where to start looking.
* @return []OAuth2Client
*/
func (a OAuth2Api) ListOAuth2Clients() ([]OAuth2Client, *APIResponse, error) {
func (a OAuth2Api) ListOAuth2Clients(limit int64, offset int64) ([]OAuth2Client, *APIResponse, error) {

var localVarHttpMethod = strings.ToUpper("Get")
// create path and map variables
Expand All @@ -596,6 +598,8 @@ func (a OAuth2Api) ListOAuth2Clients() ([]OAuth2Client, *APIResponse, error) {
for key := range a.Configuration.DefaultHeader {
localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
}
localVarQueryParams.Add("limit", a.Configuration.APIClient.ParameterToString(limit, ""))
localVarQueryParams.Add("offset", a.Configuration.APIClient.ParameterToString(offset, ""))

// to determine the Content-Type header
localVarHttpContentTypes := []string{"application/json"}
Expand Down
Loading