Skip to content

Commit

Permalink
consent: Adds ability to revoke consent and login sessions
Browse files Browse the repository at this point in the history
Closes #856

Signed-off-by: arekkas <aeneas@ory.am>
  • Loading branch information
arekkas committed Jul 7, 2018
1 parent 45d769a commit 0b6620c
Show file tree
Hide file tree
Showing 17 changed files with 1,648 additions and 14 deletions.
25 changes: 25 additions & 0 deletions consent/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,31 @@ type swaggerGetRequestByChallenge struct {
Challenge string `json:"challenge"`
}

// swagger:parameters revokeAllUserConsentSessions
type swaggerRevokeAllUserConsentSessionsPayload struct {
// in: path
// required: true
User string `json:"user"`
}

// swagger:parameters revokeUserClientConsentSessions
type swaggerRevokeUserClientConsentSessionsPayload struct {
// in: path
// required: true
User string `json:"user"`

// in: path
// required: true
Client string `json:"client"`
}

// swagger:parameters revokeAuthenticationSession
type swaggerRevokeAuthenticationSessionPayload struct {
// in: path
// required: true
User string `json:"user"`
}

// swagger:parameters acceptLoginRequest
type swaggerAcceptAuthenticationRequest struct {
// in: path
Expand Down
101 changes: 101 additions & 0 deletions consent/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"time"

"github.com/julienschmidt/httprouter"
"github.com/ory/fosite"
"github.com/ory/go-convenience/urlx"
"github.com/ory/herodot"
"github.com/pkg/errors"
Expand Down Expand Up @@ -61,6 +62,106 @@ func (h *Handler) SetRoutes(r *httprouter.Router) {
r.GET(ConsentPath+"/:challenge", h.GetConsentRequest)
r.PUT(ConsentPath+"/:challenge/accept", h.AcceptConsentRequest)
r.PUT(ConsentPath+"/:challenge/reject", h.RejectConsentRequest)

r.DELETE("/oauth2/auth/sessions/login/:user", h.DeleteLoginSession)
r.DELETE("/oauth2/auth/sessions/consent/:user", h.DeleteUserConsentSession)
r.DELETE("/oauth2/auth/sessions/consent/:user/:client", h.DeleteUserClientConsentSession)
}

// swagger:route DELETE /oauth2/auth/sessions/consent/{user} oAuth2 revokeAllUserConsentSessions
//
// Revokes all previous consent sessions of a user
//
// This endpoint revokes a user's granted consent sessions and invalidates all associated OAuth 2.0 Access Tokens.
//
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 204: emptyResponse
// 404: genericError
// 500: genericError
func (h *Handler) DeleteUserConsentSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
user := ps.ByName("user")
if err := h.M.RevokeUserConsentSession(user); err != nil {
h.H.WriteError(w, r, err)
return
}

w.WriteHeader(http.StatusNoContent)
}

// swagger:route DELETE /oauth2/auth/sessions/consent/{user}/{client} oAuth2 revokeUserClientConsentSessions
//
// Revokes consent sessions of a user for a specific OAuth 2.0 Client
//
// This endpoint revokes a user's granted consent sessions for a specific OAuth 2.0 Client and invalidates all
// associated OAuth 2.0 Access Tokens.
//
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 204: emptyResponse
// 404: genericError
// 500: genericError
func (h *Handler) DeleteUserClientConsentSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
client := ps.ByName("client")
user := ps.ByName("user")
if client == "" {
h.H.WriteError(w, r, errors.WithStack(fosite.ErrInvalidRequest.WithDebug("Parameter client is not defined")))
return
}

if err := h.M.RevokeUserClientConsentSession(user, client); err != nil {
h.H.WriteError(w, r, err)
return
}

w.WriteHeader(http.StatusNoContent)
}

// swagger:route DELETE /oauth2/auth/sessions/login/{user} oAuth2 revokeAuthenticationSession
//
// Invalidates a user's authentication session
//
// This endpoint invalidates a user's authentication session. After revoking the authentication session, the user
// has to re-authenticate at ORY Hydra. This endpoint does not invalidate any tokens.
//
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 204: emptyResponse
// 404: genericError
// 500: genericError
func (h *Handler) DeleteLoginSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
user := ps.ByName("user")

if err := h.M.RevokeUserAuthenticationSession(user); err != nil {
h.H.WriteError(w, r, err)
return
}

w.WriteHeader(http.StatusNoContent)
}

// swagger:route GET /oauth2/auth/requests/login/{challenge} oAuth2 getLoginRequest
Expand Down
3 changes: 3 additions & 0 deletions consent/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type Manager interface {
CreateConsentRequest(*ConsentRequest) error
GetConsentRequest(challenge string) (*ConsentRequest, error)
HandleConsentRequest(challenge string, r *HandledConsentRequest) (*ConsentRequest, error)
RevokeUserConsentSession(user string) error
RevokeUserClientConsentSession(user, client string) error

VerifyAndInvalidateConsentRequest(verifier string) (*HandledConsentRequest, error)
FindPreviouslyGrantedConsentRequests(client string, user string) ([]HandledConsentRequest, error)
Expand All @@ -32,6 +34,7 @@ type Manager interface {
GetAuthenticationSession(id string) (*AuthenticationSession, error)
CreateAuthenticationSession(*AuthenticationSession) error
DeleteAuthenticationSession(id string) error
RevokeUserAuthenticationSession(user string) error

CreateAuthenticationRequest(*AuthenticationRequest) error
GetAuthenticationRequest(challenge string) (*AuthenticationRequest, error)
Expand Down
60 changes: 60 additions & 0 deletions consent/manager_memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,66 @@ func NewMemoryManager() *MemoryManager {
}
}

func (m *MemoryManager) RevokeUserConsentSession(user string) error {
m.m["handledConsentRequests"].Lock()
defer m.m["handledConsentRequests"].Unlock()
m.m["consentRequests"].Lock()
defer m.m["consentRequests"].Unlock()

var found bool
for k, c := range m.handledConsentRequests {
if c.ConsentRequest.Subject == user {
delete(m.handledConsentRequests, k)
delete(m.consentRequests, k)
found = true
}
}

if !found {
return errors.WithStack(pkg.ErrNotFound)
}
return nil
}

func (m *MemoryManager) RevokeUserClientConsentSession(user, client string) error {
m.m["handledConsentRequests"].Lock()
defer m.m["handledConsentRequests"].Unlock()
m.m["consentRequests"].Lock()
defer m.m["consentRequests"].Unlock()

var found bool
for k, c := range m.handledConsentRequests {
if c.ConsentRequest.Subject == user && c.ConsentRequest.Client.ID == client {
delete(m.handledConsentRequests, k)
delete(m.consentRequests, k)
found = true
}
}

if !found {
return errors.WithStack(pkg.ErrNotFound)
}
return nil
}

func (m *MemoryManager) RevokeUserAuthenticationSession(user string) error {
m.m["authSessions"].Lock()
defer m.m["authSessions"].Unlock()

var found bool
for k, c := range m.authSessions {
if c.Subject == user {
delete(m.authSessions, k)
found = true
}
}

if !found {
return errors.WithStack(pkg.ErrNotFound)
}
return nil
}

func (m *MemoryManager) CreateConsentRequest(c *ConsentRequest) error {
m.m["consentRequests"].Lock()
defer m.m["consentRequests"].Unlock()
Expand Down
79 changes: 79 additions & 0 deletions consent/manager_sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/jmoiron/sqlx"
"github.com/ory/fosite"
"github.com/ory/hydra/client"
"github.com/ory/hydra/pkg"
"github.com/ory/sqlcon"
"github.com/pkg/errors"
"github.com/rubenv/sql-migrate"
Expand All @@ -55,6 +56,75 @@ func (m *SQLManager) CreateSchemas() (int, error) {
return n, nil
}

func (m *SQLManager) RevokeUserConsentSession(user string) error {
return m.revokeConsentSession(user, "")
}

func (m *SQLManager) RevokeUserClientConsentSession(user, client string) error {
return m.revokeConsentSession(user, client)
}

func (m *SQLManager) revokeConsentSession(user, client string) error {
args := []interface{}{user}
part := "r.subject=?"
if client != "" {
part += " AND r.client_id=?"
args = append(args, client)
}

var queries []string

switch m.db.DriverName() {
case "mysql":
queries = append(queries,
fmt.Sprintf(`DELETE h, r FROM hydra_oauth2_consent_request_handled as h
JOIN hydra_oauth2_consent_request as r ON
r.challenge = h.challenge
WHERE %s`, part),
)
default:
queries = append(queries,
fmt.Sprintf(`DELETE FROM hydra_oauth2_consent_request_handled
WHERE challenge IN (SELECT r.challenge FROM hydra_oauth2_consent_request as r WHERE %s)`, part),
fmt.Sprintf(`DELETE FROM hydra_oauth2_consent_request as r WHERE %s`, part),
)
}

for _, q := range queries {
rows, err := m.db.Exec(m.db.Rebind(q), args...)
if err != nil {
if err == sql.ErrNoRows {
return errors.WithStack(pkg.ErrNotFound)
}
return sqlcon.HandleError(err)
}

if count, _ := rows.RowsAffected(); count == 0 {
return errors.WithStack(pkg.ErrNotFound)
}
}
return nil
}

func (m *SQLManager) RevokeUserAuthenticationSession(user string) error {
rows, err := m.db.Exec(
m.db.Rebind("DELETE FROM hydra_oauth2_authentication_session WHERE subject=?"),
user,
)
if err != nil {
if err == sql.ErrNoRows {
return errors.WithStack(pkg.ErrNotFound)
}
return sqlcon.HandleError(err)
}

count, _ := rows.RowsAffected()
if count == 0 {
return errors.WithStack(pkg.ErrNotFound)
}
return nil
}

func (m *SQLManager) CreateConsentRequest(c *ConsentRequest) error {
d, err := newSQLConsentRequest(c)
if err != nil {
Expand All @@ -76,6 +146,9 @@ func (m *SQLManager) GetConsentRequest(challenge string) (*ConsentRequest, error
var d sqlRequest

if err := m.db.Get(&d, m.db.Rebind("SELECT * FROM hydra_oauth2_consent_request WHERE challenge=?"), challenge); err != nil {
if err == sql.ErrNoRows {
return nil, errors.WithStack(pkg.ErrNotFound)
}
return nil, sqlcon.HandleError(err)
}

Expand Down Expand Up @@ -108,6 +181,9 @@ func (m *SQLManager) GetAuthenticationRequest(challenge string) (*Authentication
var d sqlRequest

if err := m.db.Get(&d, m.db.Rebind("SELECT * FROM hydra_oauth2_authentication_request WHERE challenge=?"), challenge); err != nil {
if err == sql.ErrNoRows {
return nil, errors.WithStack(pkg.ErrNotFound)
}
return nil, sqlcon.HandleError(err)
}

Expand Down Expand Up @@ -216,6 +292,9 @@ func (m *SQLManager) VerifyAndInvalidateAuthenticationRequest(verifier string) (
func (m *SQLManager) GetAuthenticationSession(id string) (*AuthenticationSession, error) {
var a AuthenticationSession
if err := m.db.Get(&a, m.db.Rebind("SELECT * FROM hydra_oauth2_authentication_session WHERE id=?"), id); err != nil {
if err == sql.ErrNoRows {
return nil, errors.WithStack(pkg.ErrNotFound)
}
return nil, sqlcon.HandleError(err)
}

Expand Down

0 comments on commit 0b6620c

Please sign in to comment.