Skip to content

Commit

Permalink
warden/groups: add ability to list all groups (#612)
Browse files Browse the repository at this point in the history
* add ability to list all groups

Signed-off-by: Joshua Rubin <jrubin@zvelo.com>

* update based on review comments

Signed-off-by: Joshua Rubin <jrubin@zvelo.com>

* a few more updates from review

Signed-off-by: Joshua Rubin <jrubin@zvelo.com>

* ensure group lists dont return nil

Signed-off-by: Joshua Rubin <jrubin@zvelo.com>
  • Loading branch information
joshuarubin authored and arekkas committed Oct 23, 2017
1 parent f2f8f7a commit e248e83
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 23 deletions.
22 changes: 17 additions & 5 deletions docs/api.swagger.yaml
Expand Up @@ -1384,7 +1384,7 @@ paths:
{
"resources": ["rn:hydra:warden:groups:<member>"],
"resources": ["rn:hydra:warden:groups"],
"actions": ["get"],
Expand All @@ -1403,8 +1403,8 @@ paths:
tags:
- warden
- groups
summary: Find group IDs by member
operationId: findGroupsByMember
summary: List group IDs
operationId: listGroups
security:
- oauth2:
- hydra.groups
Expand All @@ -1415,9 +1415,21 @@ paths:
description: The id of the member to look up.
name: member
in: query
- 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'
'403':
Expand Down Expand Up @@ -2721,7 +2733,7 @@ responses:
headers:
description:
type: string
findGroupsByMemberResponse:
listGroupsResponse:
description: A list of groups the member is belonging to
schema:
type: array
Expand Down
16 changes: 12 additions & 4 deletions warden/group/doc.go
Expand Up @@ -2,17 +2,25 @@
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
Body []string
}

// swagger:parameters findGroupsByMember
type swaggerFindGroupsByMemberParameters struct {
// swagger:parameters listGroups
type swaggerListGroupsParameters struct {
// The id of the member to look up.
// in: query
Member int `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 getGroup deleteGroup
Expand Down
57 changes: 47 additions & 10 deletions warden/group/handler.go
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"strconv"

"github.com/julienschmidt/httprouter"
"github.com/ory/herodot"
Expand Down Expand Up @@ -40,15 +41,15 @@ func (h *Handler) SetRoutes(r *httprouter.Router) {
r.DELETE(GroupsHandlerPath+"/:id/members", h.RemoveGroupMembers)
}

// swagger:route GET /warden/groups warden groups findGroupsByMember
// swagger:route GET /warden/groups warden groups listGroups
//
// Find group IDs by member
// List group IDs
//
// The subject making the request needs to be assigned to a policy containing:
//
// ```
// {
// "resources": ["rn:hydra:warden:groups:<member>"],
// "resources": ["rn:hydra:warden:groups"],
// "actions": ["get"],
// "effect": "allow"
// }
Expand All @@ -66,31 +67,67 @@ func (h *Handler) SetRoutes(r *httprouter.Router) {
// oauth2: hydra.groups
//
// Responses:
// 200: findGroupsByMemberResponse
// 200: listGroupsResponse
// 401: genericError
// 403: genericError
// 500: genericError
func (h *Handler) FindGroupNames(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
var ctx = r.Context()
var member = r.URL.Query().Get("member")

g, err := h.Manager.FindGroupNames(member)
if err != nil {
accessReq := &firewall.TokenAccessRequest{
Resource: GroupsResource,
Action: "get",
}

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), accessReq, Scope); err != nil {
h.H.WriteError(w, r, err)
return
}

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: fmt.Sprintf(GroupResource, member),
Action: "get",
}, Scope); err != nil {
if member != "" {
g, err := h.Manager.FindGroupNames(member)

if err != nil {
h.H.WriteError(w, r, err)
return
}

h.H.Write(w, r, g)
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
}

g, err := h.Manager.ListGroups(limit, offset)

if err != nil {
h.H.WriteError(w, r, err)
return
}

h.H.Write(w, r, g)
}

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 groups createGroup
//
// Create a group
Expand Down
1 change: 1 addition & 0 deletions warden/group/manager.go
Expand Up @@ -19,5 +19,6 @@ type Manager interface {
AddGroupMembers(group string, members []string) error
RemoveGroupMembers(group string, members []string) error

ListGroups(limit, offset int64) ([]string, error)
FindGroupNames(subject string) ([]string, error)
}
16 changes: 16 additions & 0 deletions warden/group/manager_http.go
@@ -1,6 +1,7 @@
package group

import (
"fmt"
"net/http"
"net/url"

Expand All @@ -19,6 +20,8 @@ type HTTPManager struct {
Dry bool
}

var _ Manager = (*HTTPManager)(nil)

func (m *HTTPManager) CreateGroup(g *Group) error {
var r = pkg.NewSuperAgent(m.Endpoint.String())
r.Client = m.Client
Expand Down Expand Up @@ -91,6 +94,19 @@ func (m *HTTPManager) RemoveGroupMembers(group string, members []string) error {
return nil
}

func (m *HTTPManager) ListGroups(limit, offset int64) ([]string, error) {
var g []string
var r = pkg.NewSuperAgent(m.Endpoint.String() + fmt.Sprintf("?limit=%d&offset=%d", limit, offset))
r.Client = m.Client
r.Dry = m.Dry
r.FakeTLSTermination = m.FakeTLSTermination
if err := r.Get(&g); err != nil {
return nil, err
}

return g, nil
}

func (m *HTTPManager) FindGroupNames(subject string) ([]string, error) {
var g []string
var r = pkg.NewSuperAgent(m.Endpoint.String() + "?member=" + subject)
Expand Down
35 changes: 34 additions & 1 deletion warden/group/manager_memory.go
@@ -1,6 +1,7 @@
package group

import (
"sort"
"sync"

"github.com/pborman/uuid"
Expand All @@ -18,6 +19,8 @@ type MemoryManager struct {
sync.RWMutex
}

var _ Manager = (*MemoryManager)(nil)

func (m *MemoryManager) CreateGroup(g *Group) error {
if g.ID == "" {
g.ID = uuid.New()
Expand Down Expand Up @@ -76,12 +79,42 @@ func (m *MemoryManager) RemoveGroupMembers(group string, subjects []string) erro
return m.CreateGroup(g)
}

func (m *MemoryManager) ListGroups(limit, offset int64) ([]string, error) {
if limit <= 0 {
limit = 500
}

if offset < 0 {
offset = 0
}

res := []string{}

if offset >= int64(len(m.Groups)) {
return res, nil
}

for _, g := range m.Groups {
res = append(res, g.ID)
}

sort.Strings(res)

res = res[offset:]

if limit < int64(len(res)) {
res = res[:limit]
}

return res, nil
}

func (m *MemoryManager) FindGroupNames(subject string) ([]string, error) {
if m.Groups == nil {
m.Groups = map[string]Group{}
}

var res []string
res := []string{}
for _, g := range m.Groups {
for _, s := range g.Members {
if s == subject {
Expand Down
22 changes: 21 additions & 1 deletion warden/group/manager_sql.go
Expand Up @@ -31,6 +31,8 @@ type SQLManager struct {
DB *sqlx.DB
}

var _ Manager = (*SQLManager)(nil)

func (s *SQLManager) CreateSchemas() (int, error) {
migrate.SetTable("hydra_groups_migration")
n, err := migrate.Exec(s.DB.DB, s.DB.DriverName(), migrations, migrate.Up)
Expand Down Expand Up @@ -123,8 +125,26 @@ func (m *SQLManager) RemoveGroupMembers(group string, subjects []string) error {
return nil
}

func (m *SQLManager) ListGroups(limit, offset int64) ([]string, error) {
if limit <= 0 {
limit = 500
}

if offset < 0 {
offset = 0
}

q := []string{}

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

return q, nil
}

func (m *SQLManager) FindGroupNames(subject string) ([]string, error) {
var q []string
q := []string{}
if err := m.DB.Select(&q, m.DB.Rebind("SELECT group_id from hydra_warden_group_member WHERE member = ? GROUP BY group_id"), subject); err != nil {
return nil, errors.WithStack(err)
}
Expand Down
2 changes: 2 additions & 0 deletions warden/group/manager_test.go
Expand Up @@ -85,6 +85,8 @@ func connectToPG() {
}

func TestManagers(t *testing.T) {
t.Parallel()

for k, m := range clientManagers {
t.Run(fmt.Sprintf("case=%s", k), TestHelperManagers(m))
}
Expand Down

0 comments on commit e248e83

Please sign in to comment.