Skip to content

Commit

Permalink
Merge pull request #18 from ory-am/unstaged
Browse files Browse the repository at this point in the history
client: now tries to refresh when token is invalid
  • Loading branch information
Aeneas committed Dec 8, 2015
2 parents c32ac10 + 29c16dc commit 292b548
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 30 deletions.
87 changes: 60 additions & 27 deletions client/http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import (
"fmt"
"github.com/RangelReale/osin"
"github.com/go-errors/errors"
"github.com/ory-am/common/pkg"
. "github.com/ory-am/hydra/client"
"github.com/ory-am/hydra/middleware"
"github.com/parnurzeal/gorequest"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
"log"
"net/http"
"net/url"
"strconv"
Expand All @@ -20,17 +23,26 @@ var isAllowed struct {
}

type HTTPClient struct {
ep string
clientToken *oauth2.Token
ep string
clientToken *oauth2.Token
clientConfig *clientcredentials.Config
}

func New(endpoint string, token *oauth2.Token) *HTTPClient {
func New(endpoint, client, secret string) *HTTPClient {
return &HTTPClient{
ep: endpoint,
clientToken: token,
ep: endpoint,
clientConfig: &clientcredentials.Config{
ClientSecret: secret,
ClientID: client,
TokenURL: pkg.JoinURL(endpoint, "oauth2/token"),
},
}
}

func (c *HTTPClient) SetClientToken(token *oauth2.Token) {
c.clientToken = token
}

func (c *HTTPClient) IsRequestAllowed(req *http.Request, resource, permission, owner string) (bool, error) {
var token *osin.BearerAuth
if token = osin.CheckBearerAuth(req); token == nil {
Expand All @@ -44,29 +56,18 @@ func (c *HTTPClient) IsRequestAllowed(req *http.Request, resource, permission, o
}

func (c *HTTPClient) IsAllowed(ar *AuthorizeRequest) (bool, error) {
request := gorequest.New()
resp, body, errs := request.Post(c.ep+"/guard/allowed").Set("Authorization", c.clientToken.Type()+" "+c.clientToken.AccessToken).Set("Content-Type", "application/json").Send(*ar).End()
if len(errs) > 0 {
return false, errors.Errorf("Got errors: %v", errs)
} else if resp.StatusCode != http.StatusOK {
return false, errors.Errorf("Status code %d is not 200: %s", resp.StatusCode, body)
}

if err := json.Unmarshal([]byte(body), &isAllowed); err != nil {
return false, errors.Errorf("Could not unmarshall body because %s", err.Error())
}

if !isAllowed.Allowed {
return false, errors.New("Authroization denied")
}
return isAllowed.Allowed, nil
return isValidAuthorizeRequest(c, ar, true)
}

func (c *HTTPClient) IsAuthenticated(token string) (bool, error) {
return isValidAuthenticationRequest(c, token, true)
}

func isValidAuthenticationRequest(c *HTTPClient, token string, retry bool) (bool, error) {
data := url.Values{}
client := &http.Client{}
data.Set("token", token)
r, err := http.NewRequest("POST", c.ep+"/oauth2/introspect", bytes.NewBufferString(data.Encode()))
r, err := http.NewRequest("POST", pkg.JoinURL(c.ep, "/oauth2/introspect"), bytes.NewBufferString(data.Encode()))
if err != nil {
return false, err
}
Expand All @@ -78,9 +79,15 @@ func (c *HTTPClient) IsAuthenticated(token string) (bool, error) {
if err != nil {
return false, err
}

defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {

if retry && resp.StatusCode == http.StatusUnauthorized {
var err error
if c.clientToken, err = c.clientConfig.Token(oauth2.NoContext); err != nil {
return false, errors.New(err)
}
return isValidAuthenticationRequest(c, token, false)
} else if resp.StatusCode != http.StatusOK {
return false, fmt.Errorf("Status code %d is not 200", resp.StatusCode)
}

Expand All @@ -90,10 +97,36 @@ func (c *HTTPClient) IsAuthenticated(token string) (bool, error) {

if err := json.NewDecoder(resp.Body).Decode(&introspect); err != nil {
return false, err
}

if !introspect.Active {
} else if !introspect.Active {
return false, errors.New("Authentication denied")
}
return introspect.Active, nil
}

func isValidAuthorizeRequest(c *HTTPClient, ar *AuthorizeRequest, retry bool) (bool, error) {
log.Println(c.clientConfig.TokenURL)
log.Printf("Getting", pkg.JoinURL(c.ep, "/guard/allowed"))
request := gorequest.New()
resp, body, errs := request.Post(pkg.JoinURL(c.ep, "/guard/allowed")).Set("Authorization", c.clientToken.Type()+" "+c.clientToken.AccessToken).Set("Content-Type", "application/json").Send(*ar).End()
if len(errs) > 0 {
return false, errors.Errorf("Got errors: %v", errs)
} else if retry && resp.StatusCode == http.StatusUnauthorized {
var err error
if c.clientToken, err = c.clientConfig.Token(oauth2.NoContext); err != nil {
return false, errors.New(err)
}
log.Printf("Wow, refresd token %s", c.clientToken.AccessToken)
return isValidAuthorizeRequest(c, ar, false)
} else if resp.StatusCode != http.StatusOK {
return false, errors.Errorf("Status code %d is not 200: %s", resp.StatusCode, body)
}

if err := json.Unmarshal([]byte(body), &isAllowed); err != nil {
return false, errors.Errorf("Could not unmarshall body because %s", err.Error())
}

if !isAllowed.Allowed {
return false, errors.New("Authroization denied")
}
return isAllowed.Allowed, nil
}
131 changes: 128 additions & 3 deletions client/http/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package http_test

import (
"encoding/json"
"fmt"
"github.com/RangelReale/osin"
"github.com/go-errors/errors"
"github.com/gorilla/mux"
Expand All @@ -10,13 +11,27 @@ import (
. "github.com/ory-am/hydra/client/http"
"github.com/ory-am/hydra/policy/handler"
"github.com/ory-am/ladon/guard/operator"
"github.com/pborman/uuid"
"github.com/stretchr/testify/assert"
"golang.org/x/oauth2"
"net/http"
"net/http/httptest"
"testing"
)

func tokenHandler(rw http.ResponseWriter, req *http.Request) {
pkg.WriteJSON(rw, struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
}{
AccessToken: uuid.New(),
TokenType: "Bearer",
ExpiresIn: 9600,
})
}

func TestIsRequestAllowed(t *testing.T) {
router := mux.NewRouter()
router.HandleFunc("/guard/allowed", func(rw http.ResponseWriter, req *http.Request) {
Expand All @@ -28,19 +43,127 @@ func TestIsRequestAllowed(t *testing.T) {
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, &oauth2.Token{TokenType: "bearer", AccessToken: "foobar"})
c := New(ts.URL, "irrelevant", "irrelephant")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "foobar"})
allowed, err := c.IsRequestAllowed(&http.Request{Header: http.Header{"Authorization": []string{"Bearer token"}}}, "", "", "")
assert.Nil(t, err)
assert.True(t, allowed)
}

func TestIsAllowedRetriesOnlyOnceWhenTokenIsInvalid(t *testing.T) {
var count int
router := mux.NewRouter()
router.HandleFunc("/oauth2/token", tokenHandler).Methods("POST")
router.HandleFunc("/guard/allowed", func(rw http.ResponseWriter, req *http.Request) {
http.Error(rw, fmt.Sprintf("token invalid try ", count), http.StatusUnauthorized)
count++
}).Methods("POST")
ts := httptest.NewServer(router)
defer ts.Close()
c := New(ts.URL, "irrelevant", "irrelephant")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "foobar"})

allowed, err := c.IsAllowed(&AuthorizeRequest{
Permission: "foo",
Token: "bar",
Resource: "res",
Context: &operator.Context{
Owner: "foo",
},
})
assert.NotNil(t, err)
assert.False(t, allowed)
assert.Equal(t, 2, count)
}

func TestIsAllowedRetriesWhenTokenIsExpired(t *testing.T) {
var try int
router := mux.NewRouter()
router.HandleFunc("/guard/allowed", func(rw http.ResponseWriter, req *http.Request) {
try++
if try == 1 {
t.Logf("token invalid try ", try)
http.Error(rw, fmt.Sprintf("token invalid try ", try), http.StatusUnauthorized)
return
}

pkg.WriteJSON(rw, struct {
Allowed bool `json:"allowed"`
}{Allowed: true})
}).Methods("POST")
router.HandleFunc("/oauth2/token", tokenHandler).Methods("POST")
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, "irrelevant", "irrelephant")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "foobar"})

allowed, err := c.IsAllowed(&AuthorizeRequest{
Permission: "foo",
Token: "bar",
Resource: "res",
Context: &operator.Context{
Owner: "foo",
},
})
assert.Nil(t, err, "%s", err)
assert.True(t, allowed)
assert.Equal(t, 2, try)
}

func TestIsAuthenticatedRetriesWhenTokenIsExpired(t *testing.T) {
var try int
router := mux.NewRouter()
router.HandleFunc("/oauth2/introspect", func(rw http.ResponseWriter, req *http.Request) {
try++
if try == 1 {
http.Error(rw, fmt.Sprintf("token invalid try ", try), http.StatusUnauthorized)
return
}

pkg.WriteJSON(rw, struct {
Active bool `json:"active"`
}{Active: true})
}).Methods("POST")
router.HandleFunc("/oauth2/token", tokenHandler).Methods("POST")
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, "irrelevant", "irrelephant")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "client"})
active, err := c.IsAuthenticated("federated.token")
assert.Nil(t, err, "%s", err)
assert.True(t, active)
assert.Equal(t, 2, try)
}

func TestIsAuthenticatedRetriesOnlyOnceWhenTokenIsExpired(t *testing.T) {
var count int
router := mux.NewRouter()
router.HandleFunc("/oauth2/introspect", func(rw http.ResponseWriter, req *http.Request) {
count++
http.Error(rw, fmt.Sprintf("token invalid try ", count), http.StatusUnauthorized)
}).Methods("POST")
router.HandleFunc("/oauth2/token", tokenHandler).Methods("POST")
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, "irrelevant", "irrelephant")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "client"})
active, err := c.IsAuthenticated("federated.token")
assert.NotNil(t, err)
assert.False(t, active)
assert.Equal(t, 2, count)
}

func TestIsAllowed(t *testing.T) {
router := mux.NewRouter()
router.HandleFunc("/guard/allowed", func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, "Bearer foobar", req.Header.Get("Authorization"))
var p handler.GrantedPayload
decoder := json.NewDecoder(req.Body)
if err := decoder.Decode(&p); err != nil {
t.Logf("Could not decode body %s", err)
pkg.HttpError(rw, errors.New(err), http.StatusBadRequest)
return
}
Expand All @@ -56,7 +179,8 @@ func TestIsAllowed(t *testing.T) {
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, &oauth2.Token{TokenType: "bearer", AccessToken: "foobar"})
c := New(ts.URL, "irrelevant", "irrelephant")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "foobar"})
allowed, err := c.IsAllowed(&AuthorizeRequest{Permission: "foo", Token: "bar", Resource: "res", Context: &operator.Context{Owner: "foo"}})
assert.Nil(t, err)
assert.True(t, allowed)
Expand All @@ -79,7 +203,8 @@ func TestIsAuthenticated(t *testing.T) {
ts := httptest.NewServer(router)
defer ts.Close()

c := New(ts.URL, &oauth2.Token{TokenType: "bearer", AccessToken: "client"})
c := New(ts.URL, "irrelevant", "irrelephant")
c.SetClientToken(&oauth2.Token{TokenType: "bearer", AccessToken: "client"})
active, err := c.IsAuthenticated("federated.token")
assert.Nil(t, err)
assert.True(t, active)
Expand Down

0 comments on commit 292b548

Please sign in to comment.