Skip to content

Commit

Permalink
Finished testing the auth package
Browse files Browse the repository at this point in the history
  • Loading branch information
eedrummer committed Aug 30, 2016
1 parent c08fe4c commit 82d9004
Show file tree
Hide file tree
Showing 186 changed files with 29,882 additions and 1,121 deletions.
112 changes: 91 additions & 21 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion server/heart_auth.go → auth/heart_auth.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package server
package auth

import (
"fmt"
Expand Down
5 changes: 4 additions & 1 deletion server/heart_auth_test.go → auth/heart_auth_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package server
package auth

import (
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/gin-gonic/gin"
"github.com/pebbe/util"
Expand All @@ -13,6 +14,8 @@ import (
type HEARTScopesSuite struct {
}

func Test(t *testing.T) { TestingT(t) }

var _ = Suite(&HEARTScopesSuite{})

func (s *HEARTScopesSuite) SetUpTest(c *C) {
Expand Down
71 changes: 71 additions & 0 deletions auth/oauth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package auth

import (
"encoding/json"
"net/http"
"net/url"
"strings"

"github.com/gin-gonic/gin"
"github.com/juju/errors"
"github.com/mitre/heart"
)

// OAuthIntrospectionHandler creates a gin.HandlerFunc that can be used to
// introspect OAuth 2.0 tokens provided in the request.
//
// This middleware will abort any requests that do not have an Authorization header. It will
// also halt requests if the provided bearer token is inactive or expired.
//
// If a valid token is provided, the gin.Context is augmented by setting the following variables:
// scopes will be a []string containing all scopes valid for the provided token. subject will be
// an identifier for the user who delegated the authority represented by the token. clientID will
// contain the identifier for the client issuing the request.
//
// clientID is the identifier for the OAuth 2.0 client allowed to access the
// token introspection endpoint
// clientSecret is the secret for the registered client
// endpoint is the URL for the token introspection endpoint at the OAuth 2.0
// authorization server.
//
// This is for performing token introspection using a "plain" OAuth 2.0 client.
// For HEART profiled OAuth 2.0 see:
// https://github.com/mitre/heart/blob/master/middleware.go
func OAuthIntrospectionHandler(clientID, clientSecret, endpoint string) gin.HandlerFunc {
return func(c *gin.Context) {
auth := c.Request.Header.Get("Authorization")
if auth == "" {
c.String(http.StatusForbidden, "No Authorization header provided")
c.Abort()
return
}
token := strings.TrimPrefix(auth, "Bearer ")
if token == auth {
c.String(http.StatusForbidden, "Could not find bearer token in Authorization header")
c.Abort()
return
}
values := url.Values{"client_id": {clientID}, "client_secret": {clientSecret}, "token": {token}}
resp, err := http.PostForm(endpoint, values)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, errors.Annotate(err, "Couldn't connect to the introspection endpoint"))
return
}
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
ir := heart.IntrospectionResponse{}
err = decoder.Decode(&ir)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, errors.Annotate(err, "Couldn't decode the introspection response"))
return
}
if !ir.Active {
c.String(http.StatusForbidden, "Provided token is no longer active or valid")
c.Abort()
return
}
c.Set("scopes", ir.SplitScope())
c.Set("subject", ir.SUB)
c.Set("clientID", ir.ClientID)
}
}
59 changes: 59 additions & 0 deletions auth/oauth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package auth

import (
"encoding/json"
"net/http"
"net/http/httptest"
"time"

"github.com/gin-gonic/gin"
"github.com/mitre/heart"
"github.com/pebbe/util"

. "gopkg.in/check.v1"
)

type OAuthSuite struct {
}

var _ = Suite(&OAuthSuite{})

func (o *OAuthSuite) TestIntrospection(c *C) {
server := httptest.NewServer(mockIntrospectionEndpoint(c))
defer server.Close()
rr := oauthRequest("my_client_id", "sekret", server.URL, c)
c.Assert(rr.Code, Equals, http.StatusOK)
}

func mockIntrospectionEndpoint(c *C) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.Assert(r.Method, Equals, "POST")
clientID := r.FormValue("client_id")
c.Assert(clientID, Equals, "my_client_id")
token := r.FormValue("token")
c.Assert(token, Equals, "foo")
ir := &heart.IntrospectionResponse{Active: true, Scope: "foo bar", EXP: time.Now().Unix(), SUB: "steve", ClientID: "heart-watch"}
encoder := json.NewEncoder(w)
encoder.Encode(ir)
})
}

func oauthRequest(clientID, clientSecret, endpoint string, c *C) *httptest.ResponseRecorder {
r, err := http.NewRequest("GET", "/", nil)
util.CheckErr(err)
r.Header.Add("Content-Type", "application/json")
r.Header.Add("Authorization", "Bearer foo")

e := gin.New()
rw := httptest.NewRecorder()
noop := func(ctx *gin.Context) {
scopes, _ := ctx.Get("scopes")
c.Assert("bar", Equals, scopes.([]string)[1])
ctx.String(http.StatusOK, "Hello")
}

e.GET("/", OAuthIntrospectionHandler(clientID, clientSecret, endpoint), noop)

e.ServeHTTP(rw, r)
return rw
}
Loading

0 comments on commit 82d9004

Please sign in to comment.