Skip to content

Commit

Permalink
Add api endpoint to list all authorized clients by user
Browse files Browse the repository at this point in the history
Resolves #953

Signed-off-by: Jan Beckmann <king-jan1999@hotmail.de>
  • Loading branch information
kingjan1999 committed Jul 29, 2018
1 parent e79014d commit 46720ba
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 8 deletions.
8 changes: 8 additions & 0 deletions consent/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,11 @@ type swaggerRejectRequest struct {
// in: body
Body RequestDeniedError
}

// A list of handled consent requests.
// swagger:response handledConsentRequestList
type swaggerListHandledConsentRequestsResult struct {
// in: body
// type: array
Body []HandledConsentRequest
}
42 changes: 42 additions & 0 deletions consent/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func (h *Handler) SetRoutes(r *httprouter.Router) {
r.PUT(ConsentPath+"/:challenge/reject", h.RejectConsentRequest)

r.DELETE("/oauth2/auth/sessions/login/:user", h.DeleteLoginSession)
r.GET("/oauth2/auth/sessions/consent/:user", h.GetConsentSessions)
r.DELETE("/oauth2/auth/sessions/consent/:user", h.DeleteUserConsentSession)
r.DELETE("/oauth2/auth/sessions/consent/:user/:client", h.DeleteUserClientConsentSession)
}
Expand Down Expand Up @@ -133,6 +134,47 @@ func (h *Handler) DeleteUserClientConsentSession(w http.ResponseWriter, r *http.
w.WriteHeader(http.StatusNoContent)
}

// swagger:route GET /oauth2/auth/sessions/consent/{user} oAuth2 listUserClientConsentSessions
//
// List all consent sessions of a user
//
// This endpoint lists all user's granted consent sessions, including client and granted scope
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 200: handledConsentRequestList
// 401: genericError
// 403: genericError
// 500: genericError

func (h *Handler) GetConsentSessions(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
user := ps.ByName("user")
if user == "" {
h.H.WriteError(w, r, errors.WithStack(fosite.ErrInvalidRequest.WithDebug("Parameter user is not defined")))
return
}

sessions, err := h.M.FindPreviouslyGrantedConsentRequestsByUser(user)

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

for _, session := range sessions {
session.ConsentRequest.Client = sanitizeClient(session.ConsentRequest.Client)
}

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

// swagger:route DELETE /oauth2/auth/sessions/login/{user} oAuth2 revokeAuthenticationSession
//
// Invalidates a user's authentication session
Expand Down
1 change: 1 addition & 0 deletions consent/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Manager interface {

VerifyAndInvalidateConsentRequest(verifier string) (*HandledConsentRequest, error)
FindPreviouslyGrantedConsentRequests(client string, user string) ([]HandledConsentRequest, error)
FindPreviouslyGrantedConsentRequestsByUser(user string) ([]HandledConsentRequest, error)

// Cookie management
GetAuthenticationSession(id string) (*AuthenticationSession, error)
Expand Down
20 changes: 16 additions & 4 deletions consent/manager_memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,22 @@ func (m *MemoryManager) VerifyAndInvalidateConsentRequest(verifier string) (*Han
}

func (m *MemoryManager) FindPreviouslyGrantedConsentRequests(client string, subject string) ([]HandledConsentRequest, error) {
var rs []HandledConsentRequest
filteredByUser, _ := m.FindPreviouslyGrantedConsentRequestsByUser(subject)
for _, c := range filteredByUser {
if client != c.ConsentRequest.Client.GetID() {
continue
}
rs = append(rs, c)
}
if len(rs) == 0 {
return []HandledConsentRequest{}, nil
}

return rs, nil
}

func (m *MemoryManager) FindPreviouslyGrantedConsentRequestsByUser(subject string) ([]HandledConsentRequest, error) {
var rs []HandledConsentRequest
for _, c := range m.handledConsentRequests {
cr, err := m.GetConsentRequest(c.Challenge)
Expand All @@ -171,10 +187,6 @@ func (m *MemoryManager) FindPreviouslyGrantedConsentRequests(client string, subj
return nil, err
}

if client != cr.Client.GetID() {
continue
}

if subject != cr.Subject {
continue
}
Expand Down
31 changes: 28 additions & 3 deletions consent/manager_sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,34 @@ WHERE
return nil, sqlcon.HandleError(err)
}

return m.resolveHandledConsentRequests(a)
}

func (m *SQLManager) FindPreviouslyGrantedConsentRequestsByUser(subject string) ([]HandledConsentRequest, error) {
var a []sqlHandledConsentRequest

if err := m.db.Select(&a, m.db.Rebind(`SELECT h.* FROM
hydra_oauth2_consent_request_handled as h
JOIN
hydra_oauth2_consent_request as r ON (h.challenge = r.challenge)
WHERE
r.subject=? AND r.skip=FALSE
AND
(h.error='{}' AND h.remember=TRUE)
`), subject); err != nil {
if err == sql.ErrNoRows {
return nil, errors.WithStack(errNoPreviousConsentFound)
}
return nil, sqlcon.HandleError(err)
}

return m.resolveHandledConsentRequests(a)

}

func (m *SQLManager) resolveHandledConsentRequests(requests []sqlHandledConsentRequest) ([]HandledConsentRequest, error) {
var aa []HandledConsentRequest
for _, v := range a {
for _, v := range requests {
r, err := m.GetConsentRequest(v.Challenge)
if err != nil {
return nil, err
Expand All @@ -376,12 +402,10 @@ WHERE
if v.RememberFor > 0 && v.RequestedAt.Add(time.Duration(v.RememberFor)*time.Second).Before(time.Now().UTC()) {
continue
}

va, err := v.toHandledConsentRequest(r)
if err != nil {
return nil, err
}

aa = append(aa, *va)
}

Expand All @@ -390,4 +414,5 @@ WHERE
}

return aa, nil

}
47 changes: 47 additions & 0 deletions consent/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,53 @@ func TestManagers(t *testing.T) {
})
}
})

t.Run("case=list-handled-consent-requests", func(t *testing.T) {
for k, m := range managers {
cr1, hcr1 := mockConsentRequest("rv1", true, 0, false, false, false)
cr2, hcr2 := mockConsentRequest("rv2", false, 0, false, false, false)
clientManager.CreateClient(cr1.Client)
clientManager.CreateClient(cr2.Client)

require.NoError(t, m.CreateConsentRequest(cr1))
require.NoError(t, m.CreateConsentRequest(cr2))
_, err := m.HandleConsentRequest("challengerv1", hcr1)
require.NoError(t, err)
_, err = m.HandleConsentRequest("challengerv2", hcr2)
require.NoError(t, err)

t.Run("manager="+k, func(t *testing.T) {
for i, tc := range []struct {
subject string
challenges []string
clients []string
}{
{
subject: "subjectrv1",
challenges: []string{"challengerv1"},
clients: []string{"clientrv1"},
},
{
subject: "subjectrv2",
challenges: []string{},
clients: []string{},
},
} {
t.Run(fmt.Sprintf("case=%d/subject=%s", i, tc.subject), func(t *testing.T) {
consents, _ := m.FindPreviouslyGrantedConsentRequestsByUser(tc.subject)

assert.Equal(t, len(tc.challenges), len(consents))

for _, consent := range consents {
assert.Contains(t, tc.challenges, consent.Challenge)
assert.Contains(t, tc.clients, consent.ConsentRequest.Client.ClientID)
}
})
}
})
}

})
}

func compareAuthenticationRequest(t *testing.T, a, b *AuthenticationRequest) {
Expand Down
2 changes: 1 addition & 1 deletion consent/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ type HandledConsentRequest struct {
// authorization will be remembered indefinitely.
RememberFor int `json:"remember_for"`

ConsentRequest *ConsentRequest `json:"-"`
ConsentRequest *ConsentRequest `json:"consent_request"`
Error *RequestDeniedError `json:"-"`
Challenge string `json:"-"`
RequestedAt time.Time `json:"-"`
Expand Down
44 changes: 44 additions & 0 deletions docs/api.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,38 @@
}
},
"/oauth2/auth/sessions/consent/{user}": {
"get": {
"description": "This endpoint lists all user's granted consent sessions, including client and granted scope",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"schemes": [
"http",
"https"
],
"tags": [
"oAuth2"
],
"summary": "List all consent sessions of a user",
"operationId": "listUserClientConsentSessions",
"responses": {
"200": {
"$ref": "#/responses/handledConsentRequestList"
},
"401": {
"$ref": "#/responses/genericError"
},
"403": {
"$ref": "#/responses/genericError"
},
"500": {
"$ref": "#/responses/genericError"
}
}
},
"delete": {
"description": "This endpoint revokes a user's granted consent sessions and invalidates all associated OAuth 2.0 Access Tokens.",
"consumes": [
Expand Down Expand Up @@ -1925,6 +1957,9 @@
"type": "object",
"title": "The request payload used to accept a consent request.",
"properties": {
"consent_request": {
"$ref": "#/definitions/consentRequest"
},
"grant_scope": {
"description": "GrantScope sets the scope the user authorized the client to use. Should be a subset of `requested_scope`",
"type": "array",
Expand Down Expand Up @@ -3015,6 +3050,15 @@
}
}
},
"handledConsentRequestList": {
"description": "A list of handled consent requests.",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/acceptConsentRequest"
}
}
},
"oAuth2ClientList": {
"description": "A list of clients.",
"schema": {
Expand Down

0 comments on commit 46720ba

Please sign in to comment.