Skip to content

Commit

Permalink
* oauth2/introspect: make endpoint rfc7662 compatible - closes #289
Browse files Browse the repository at this point in the history
* warden: make it clear that ladon.Request.Subject is not required or break bc and remove it - closes #270
  • Loading branch information
Aeneas Rekkas (arekkas) committed Oct 16, 2016
1 parent bc9e614 commit 547558c
Show file tree
Hide file tree
Showing 15 changed files with 108 additions and 247 deletions.
12 changes: 6 additions & 6 deletions client/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request, _ httprouter.Pa
return
}

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: ClientsResource,
Action: "create",
Context: ladon.Context{
Context: map[string]interface{}{
"owner": c.Owner,
},
}, Scope); err != nil {
Expand Down Expand Up @@ -93,7 +93,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request, ps httprouter.P
return
}

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: ClientsResource,
Action: "update",
Context: ladon.Context{
Expand All @@ -120,7 +120,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request, ps httprouter.P
func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var ctx = herodot.NewContext()

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: ClientsResource,
Action: "get",
}, Scope); err != nil {
Expand Down Expand Up @@ -152,7 +152,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request, ps httprouter.Para
return
}

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: fmt.Sprintf(ClientResource, id),
Action: "get",
Context: ladon.Context{
Expand All @@ -171,7 +171,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request, ps httprouter.P
var ctx = herodot.NewContext()
var id = ps.ByName("id")

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: fmt.Sprintf(ClientResource, id),
Action: "delete",
}, Scope); err != nil {
Expand Down
10 changes: 5 additions & 5 deletions cmd/cli/handler_warden.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ import (

"github.com/ory-am/hydra/config"
"github.com/ory-am/hydra/pkg"
"github.com/ory-am/hydra/warden"
"github.com/spf13/cobra"
"golang.org/x/net/context"
"github.com/ory-am/hydra/oauth2"
)

type WardenHandler struct {
Config *config.Config
M *warden.HTTPWarden
M *oauth2.HTTPIntrospector
}

func newWardenHandler(c *config.Config) *WardenHandler {
return &WardenHandler{
Config: c,
M: &warden.HTTPWarden{},
M: &oauth2.HTTPIntrospector{},
}
}

Expand All @@ -34,11 +34,11 @@ func (h *WardenHandler) IsAuthorized(cmd *cobra.Command, args []string) {
}

scopes, _ := cmd.Flags().GetStringSlice("scopes")
res, err := h.M.TokenValid(context.Background(), args[0], scopes...)
res, err := h.M.IntrospectToken(context.Background(), args[0], scopes...)
pkg.Must(err, "Could not validate token: %s", err)

out, err := json.MarshalIndent(res, "", "\t")
pkg.Must(err, "Could not marshall keys: %s", err)
pkg.Must(err, "Could not prettify token: %s", err)

fmt.Printf("%s\n", out)
}
11 changes: 5 additions & 6 deletions connection/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/julienschmidt/httprouter"
"github.com/ory-am/hydra/firewall"
"github.com/ory-am/hydra/herodot"
"github.com/ory-am/ladon"
"github.com/pborman/uuid"
"golang.org/x/net/context"
)
Expand Down Expand Up @@ -52,7 +51,7 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request, ps httprouter.P
var conn Connection
var ctx = context.Background()

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: connectionsResource,
Action: "create",
}, scope); err != nil {
Expand Down Expand Up @@ -85,7 +84,7 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request, ps httprouter.P
func (h *Handler) FindLocal(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var ctx = context.Background()

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: connectionsResource,
Action: "find",
}, scope); err != nil {
Expand All @@ -105,7 +104,7 @@ func (h *Handler) FindLocal(w http.ResponseWriter, r *http.Request, ps httproute
func (h *Handler) FindRemote(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var ctx = context.Background()

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: connectionsResource,
Action: "find",
}, scope); err != nil {
Expand All @@ -126,7 +125,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request, ps httprouter.Para
var ctx = context.Background()
var id = ps.ByName("id")

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: fmt.Sprintf(connectionResource, id),
Action: "get",
}, scope); err != nil {
Expand All @@ -147,7 +146,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request, ps httprouter.P
var ctx = context.Background()
var id = ps.ByName("id")

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: fmt.Sprintf(connectionResource, id),
Action: "delete",
}, scope); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion docs/sdk/go.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func anyHttpHandler(w http.ResponseWriter, r *http.Request) {
fmt.Sprintf("%s", ctx.Subject)

// Check if a token is valid and the token's subject fulfills the policy based access request.
ctx, err := hydra.Warden.TokenAllowed(context.Background(), "access-token", &ladon.Request{
ctx, err := hydra.Warden.TokenAllowed(context.Background(), "access-token", &firewall.TokenAccessRequest{
Resource: "matrix",
Action: "create",
Context: ladon.Context{},
Expand Down
42 changes: 30 additions & 12 deletions firewall/warden.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"net/http"
"time"

"github.com/ory-am/ladon"
"golang.org/x/net/context"
)

Expand Down Expand Up @@ -34,37 +33,56 @@ type Context struct {
Extra map[string]interface{} `json:"ext"`
}

// AccessRequest is the warden's request object.
type AccessRequest struct {
// Resource is the resource that access is requested to.
Resource string `json:"resource"`

// Action is the action that is requested on the resource.
Action string `json:"action"`

// Subejct is the subject that is requesting access.
Subject string `json:"subject"`

// Context is the request's environmental context.
Context map[string]interface{} `json:"context"`
}

type TokenAccessRequest struct {
// Resource is the resource that access is requested to.
Resource string `json:"resource"`

// Action is the action that is requested on the resource.
Action string `json:"action"`

// Context is the request's environmental context.
Context map[string]interface{} `json:"context"`
}

// Firewall offers various validation strategies for access tokens.
type Firewall interface {
// TokenValid checks if the given token is valid and if the requested scopes are satisfied. Returns
// a context if the token is valid and an error if not.
//
// ctx, err := firewall.TokenValid(context.Background(), "access-token", "photos", "files")
// fmt.Sprintf("%s", ctx.Subject)
TokenValid(ctx context.Context, token string, scopes ...string) (*Context, error)

// IsAllowed uses policies to return nil if the access request can be fulfilled or an error if not.
//
// ctx, err := firewall.IsAllowed(context.Background(), &ladon.Request{
// ctx, err := firewall.IsAllowed(context.Background(), &AccessRequest{
// Subject: "alice",
// Resource: "matrix",
// Action: "create",
// Context: ladon.Context{},
// }, "photos", "files")
//
// fmt.Sprintf("%s", ctx.Subject)
IsAllowed(ctx context.Context, accessRequest *ladon.Request) error
IsAllowed(ctx context.Context, accessRequest *AccessRequest) error

// TokenAllowed uses policies and a token to return a context and no error if the access request can be fulfilled or an error if not.
//
// ctx, err := firewall.TokenAllowed(context.Background(), "access-token", &ladon.Request{
// ctx, err := firewall.TokenAllowed(context.Background(), "access-token", &TokenAccessRequest{
// Resource: "matrix",
// Action: "create",
// Context: ladon.Context{},
// }, "photos", "files")
//
// fmt.Sprintf("%s", ctx.Subject)
TokenAllowed(ctx context.Context, token string, accessRequest *ladon.Request, scopes ...string) (*Context, error)
TokenAllowed(ctx context.Context, token string, accessRequest *TokenAccessRequest, scopes ...string) (*Context, error)

// TokenFromRequest returns an access token from the HTTP Authorization header.
//
Expand Down
17 changes: 8 additions & 9 deletions jwk/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/julienschmidt/httprouter"
"github.com/ory-am/hydra/firewall"
"github.com/ory-am/hydra/herodot"
"github.com/ory-am/ladon"
"github.com/square/go-jose"
"golang.org/x/net/context"
)
Expand Down Expand Up @@ -60,7 +59,7 @@ func (h *Handler) DeleteKey(w http.ResponseWriter, r *http.Request, ps httproute
var setName = ps.ByName("set")
var keyName = ps.ByName("key")

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: "rn:hydra:keys:" + setName + ":" + keyName,
Action: "delete",
}, "hydra.keys.delete"); err != nil {
Expand All @@ -80,7 +79,7 @@ func (h *Handler) DeleteKeySet(w http.ResponseWriter, r *http.Request, ps httpro
var ctx = context.Background()
var setName = ps.ByName("set")

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: "rn:hydra:keys:" + setName,
Action: "delete",
}, "hydra.keys.delete"); err != nil {
Expand All @@ -101,7 +100,7 @@ func (h *Handler) Create(w http.ResponseWriter, r *http.Request, ps httprouter.P
var keyRequest createRequest
var set = ps.ByName("set")

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: "rn:hydra:keys:" + set,
Action: "create",
}, "hydra.keys.create"); err != nil {
Expand Down Expand Up @@ -139,7 +138,7 @@ func (h *Handler) UpdateKeySet(w http.ResponseWriter, r *http.Request, ps httpro
var keySet = new(jose.JsonWebKeySet)
var set = ps.ByName("set")

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: "rn:hydra:keys:" + set,
Action: "update",
}, "hydra.keys.update"); err != nil {
Expand Down Expand Up @@ -178,7 +177,7 @@ func (h *Handler) UpdateKey(w http.ResponseWriter, r *http.Request, ps httproute
return
}

if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: "rn:hydra:keys:" + set + ":" + key.KeyID,
Action: "update",
}, "hydra.keys.update"); err != nil {
Expand All @@ -199,13 +198,13 @@ func (h *Handler) GetKey(w http.ResponseWriter, r *http.Request, ps httprouter.P
var setName = ps.ByName("set")
var keyName = ps.ByName("key")

if err := h.W.IsAllowed(ctx, &ladon.Request{
if err := h.W.IsAllowed(ctx, &firewall.AccessRequest{
Subject: "",
Resource: "rn:hydra:keys:" + setName + ":" + keyName,
Action: "get",
}); err == nil {
// Allow unauthorized requests to access this resource if it is enabled by policies
} else if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
} else if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: "rn:hydra:keys:" + setName + ":" + keyName,
Action: "get",
}, "hydra.keys.get"); err != nil {
Expand Down Expand Up @@ -233,7 +232,7 @@ func (h *Handler) GetKeySet(w http.ResponseWriter, r *http.Request, ps httproute
}

for _, key := range keys.Keys {
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &ladon.Request{
if _, err := h.W.TokenAllowed(ctx, h.W.TokenFromRequest(r), &firewall.TokenAccessRequest{
Resource: "rn:hydra:keys:" + setName + ":" + key.KeyID,
Action: "get",
}, "hydra.keys.get"); err != nil {
Expand Down
9 changes: 3 additions & 6 deletions oauth2/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/ory-am/hydra/firewall"
"github.com/ory-am/hydra/herodot"
"github.com/ory-am/hydra/pkg"
"strings"
)

const (
Expand Down Expand Up @@ -47,8 +48,7 @@ func (h *Handler) Introspect(w http.ResponseWriter, r *http.Request, _ httproute
var inactive = map[string]bool{"active": false}

ctx := herodot.NewContext()
clientCtx, err := h.Firewall.TokenValid(ctx, h.Firewall.TokenFromRequest(r))
if err != nil {
if _, err := h.Introspector.IntrospectToken(ctx, h.Firewall.TokenFromRequest(r)); err != nil {
h.H.WriteError(ctx, w, r, err)
return
}
Expand All @@ -58,13 +58,10 @@ func (h *Handler) Introspect(w http.ResponseWriter, r *http.Request, _ httproute
return
}

auth, err := h.Introspector.IntrospectToken(ctx, r.PostForm.Get("token"))
auth, err := h.Introspector.IntrospectToken(ctx, r.PostForm.Get("token"), strings.Split(r.PostForm.Get("scope"), " ")...)
if err != nil {
h.H.Write(ctx, w, r, &inactive)
return
} else if clientCtx.Subject != auth.Audience {
h.H.Write(ctx, w, r, &inactive)
return
}

h.H.Write(ctx, w, r, auth)
Expand Down
2 changes: 1 addition & 1 deletion oauth2/introspector.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,5 @@ type Introspector interface {
// ctx, err := introspector.IntrospectToken(context.Background(), introspector.TokenFromRequest(r), "photos", "files")
// fmt.Sprintf("%s", ctx.Subject)
// }
IntrospectToken(ctx context.Context, token string) (*Introspection, error)
IntrospectToken(ctx context.Context, token string, scopes ...string) (*Introspection, error)
}
5 changes: 3 additions & 2 deletions oauth2/introspector_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
)

type HTTPIntrospector struct {
Expand All @@ -31,12 +32,12 @@ func (this *HTTPIntrospector) SetClient(c *clientcredentials.Config) {
// IntrospectToken is capable of introspecting tokens according to https://tools.ietf.org/html/rfc7662
//
// The HTTP API is documented at http://docs.hdyra.apiary.io/#reference/oauth2/oauth2-token-introspection
func (this *HTTPIntrospector) IntrospectToken(ctx context.Context, token string) (*Introspection, error) {
func (this *HTTPIntrospector) IntrospectToken(ctx context.Context, token string, scopes ...string) (*Introspection, error) {
var resp = new(Introspection)
var ep = *this.Endpoint
ep.Path = IntrospectPath

data := url.Values{"token": []string{token}}
data := url.Values{"token": []string{token}, "scope": []string{strings.Join(scopes, " ")}}
hreq, err := http.NewRequest("POST", ep.String(), bytes.NewBufferString(data.Encode()))
if err != nil {
return nil, errors.Wrap(err, "")
Expand Down
4 changes: 2 additions & 2 deletions oauth2/introspector_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ func (w *LocalIntrospector) TokenFromRequest(r *http.Request) string {
return fosite.AccessTokenFromRequest(r)
}

func (w *LocalIntrospector) IntrospectToken(ctx context.Context, token string) (*Introspection, error) {
func (w *LocalIntrospector) IntrospectToken(ctx context.Context, token string, scopes ...string) (*Introspection, error) {
var session = new(Session)
var auth, err = w.OAuth2.ValidateToken(ctx, token, fosite.AccessToken, session)
var auth, err = w.OAuth2.ValidateToken(ctx, token, fosite.AccessToken, session, scopes...)
if err != nil {
logrus.WithError(err).Infof("Token introspection failed")
return &Introspection{
Expand Down
Loading

0 comments on commit 547558c

Please sign in to comment.