Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 43 additions & 12 deletions context/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package context
import (
"database/sql"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/ory-am/dockertest"
"github.com/ory-am/hydra/jwt"
hjwt "github.com/ory-am/hydra/jwt"
"github.com/ory-am/ladon/policy"
ladon "github.com/ory-am/ladon/policy/postgres"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -47,32 +49,32 @@ func TestNewContextFromAuthorization(t *testing.T) {
}{
{
"1",
[]byte(jwt.TestCertificates[0][1]),
[]byte(jwt.TestCertificates[1][1]),
[]byte(hjwt.TestCertificates[0][1]),
[]byte(hjwt.TestCertificates[1][1]),
// {"foo": "bar"}
"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
false,
},
{
"2",
[]byte(jwt.TestCertificates[0][1]),
[]byte(jwt.TestCertificates[1][1]),
[]byte(hjwt.TestCertificates[0][1]),
[]byte(hjwt.TestCertificates[1][1]),
// {"subject": "nonexistent"}
"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWJqZWN0Ijoibm9uZXhpc3RlbnQifQ.jDUnvVMQHrhuIRUr8qAJ0g-ZKArdiJ21LAPDktmV56KFknX712Yxdder78YjEjxvGOvgtxLpCiay0cV5pvcWLuFW65Ys1P1SwdmdebtWfiGQwBy2Ggm3MrHjD_-r5JNAxFZjFZfZ1Fk-JlSZ97r8S7gYfDSAkxhpDmDy5Bm8e5_xsGDNp8dByuXop7QEtJb_igaa0APWa2ZOp3oTgxjD4CP6ZX6N5fGjtwjJWx5wHt7JaKXq8CRG8elm7LnNezYyJxeHECVctQGVv3HUjJxKf0l7wZXbG87BrG2M7otT8Py2sJP8X4wYL0DEsbErkEieV4D-KEBqpkvfXOrDGMFNRQ",
false,
},
{
"3",
[]byte(jwt.TestCertificates[0][1]),
[]byte(jwt.TestCertificates[1][1]),
[]byte(hjwt.TestCertificates[0][1]),
[]byte(hjwt.TestCertificates[1][1]),
// not a valid token
"Bearer eyJ0eXAaOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWJqZWN0IjoiMTMyIn0.WDC51GK5wIQPd00MqLjFy3AU3gNsvsCpWk5e8RObVxBqcYAdv-UwMfEUAFE6Y50C5pQ1t8_LHfxJYNfcW3fj_x5FXckdbqvpXHi-psxuDwk_rancpjZQegcutqYRH37_lnJ8lIq65ZgxnyYnQKGOMl3w7etK1gOvqEcP_eHn8HG0jeVk0SDZm82x0JXSk0lrVEEjWmWYtXEsLz0E4clNPUW37K9eyjYFKnyVCIPfmGwTlkDLjANsyu0P6kFiV28V1_XedtJXDI3MmG2SxSHogDhZJLb298JBwod0d6wTyygI9mUbX-C0PklTJTxIhSs7Pc6unNlWnbyL8Z4FJrdSEw",
false,
},
{
"4",
[]byte(jwt.TestCertificates[0][1]),
[]byte(jwt.TestCertificates[1][1]),
[]byte(hjwt.TestCertificates[0][1]),
[]byte(hjwt.TestCertificates[1][1]),
// {
// "exp": "2099-10-31T15:03:52.4620974+01:00",
// "iat": "2014-10-31T13:03:52.4620974+01:00",
Expand All @@ -84,16 +86,16 @@ func TestNewContextFromAuthorization(t *testing.T) {
},
{
"5",
[]byte(jwt.TestCertificates[0][1]),
[]byte(jwt.TestCertificates[1][1]),
[]byte(hjwt.TestCertificates[0][1]),
[]byte(hjwt.TestCertificates[1][1]),
"",
false,
},
} {
message := "ok"
ctx := context.Background()

j := jwt.New(c.privateKey, c.publicKey)
j := hjwt.New(c.privateKey, c.publicKey)

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx = NewContextFromAuthorization(ctx, r, j, ladonStore)
Expand All @@ -116,3 +118,32 @@ func TestNewContextFromAuthorization(t *testing.T) {
assert.Equal(t, message+"\n", string(result))
}
}

func TestGetters(t *testing.T) {
assert.False(t, IsAuthenticatedFromContext(context.Background()))
_, err := PoliciesFromContext(context.Background())
assert.NotNil(t, err)
_, err = SubjectFromContext(context.Background())
assert.NotNil(t, err)
_, err = TokenFromContext(context.Background())
assert.NotNil(t, err)

ctx := context.Background()
claims := hjwt.ClaimsCarrier{"sub": "peter"}
token := &jwt.Token{Valid: true}
policies := []policy.Policy{}
ctx = NewContextFromAuthValues(ctx, claims, token, policies)

assert.True(t, IsAuthenticatedFromContext(ctx))
policiesContext, err := PoliciesFromContext(ctx)
assert.Nil(t, err)
assert.Equal(t, policies, policiesContext)

subjectContext, err := SubjectFromContext(ctx)
assert.Nil(t, err)
assert.Equal(t, claims.GetSubject(), subjectContext)

tokenContext, err := TokenFromContext(ctx)
assert.Nil(t, err)
assert.Equal(t, token, tokenContext)
}
6 changes: 4 additions & 2 deletions jwt/claims_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package jwt_test
package jwt

import (
. "github.com/ory-am/hydra/jwt"
"github.com/pborman/uuid"
"github.com/stretchr/testify/assert"
"testing"
Expand All @@ -26,5 +25,8 @@ func TestClaimsCarrier(t *testing.T) {
assert.Equal(t, c.audience, carrier.GetAudience(), "Case %d", k)
assert.Equal(t, c.notBefore, carrier.GetNotBefore(), "Case %d", k)
assert.Equal(t, c.issuedAt, carrier.GetIssuedAt(), "Case %d", k)
assert.Empty(t, carrier.getAsString("doesnotexist"), "Case %d", k)
assert.Equal(t, time.Time{}, carrier.getAsTime("doesnotexist"), "Case %d", k)
assert.NotEmpty(t, carrier.String(), "Case %d", k)
}
}
6 changes: 6 additions & 0 deletions jwt/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ func TestSignRejectsAlgAndTypHeader(t *testing.T) {
}
}

func TestVerifyPassesHeaderAlgInjection(t *testing.T) {
j := New([]byte(TestCertificates[0][1]), []byte(TestCertificates[1][1]))
_, err := j.VerifyToken([]byte("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.BZXqpeQKnhMtyln2NnoNTUoz_BmyNR-vPHmCxfEpnzCegPZJeCPQiFmn6k7hYhYeWFhH0NhH7-c22-bAf656Esy5qdcxCrwgYSyXAbGQ4C9YsinGcliXeQYcYgOmj8gS2K5Xbj4g9StOB7KywZ_QTJc6FVOqqcgikYVtVA6bMKRrYB4ZS6ZFPdWYTWZ-qOyEg6V7o6-IWmCpEZXlyBgyfAanQkTISMyYuJFPCnFhjnmBUyz0JrWE4gQutOk1-Yw2ikym4GQDrkxrKnnmC_lSJ5I1daxq09oMNj4WRsckktOU64Wuk0PRq_CEpSIA7uHE-Ecgn4ZvRgyLaR1B8S2pAw"))
assert.NotNil(t, err)
}

func TestSignAndVerify(t *testing.T) {
for i, c := range []struct {
private []byte
Expand Down
2 changes: 1 addition & 1 deletion oauth/connection/handler/handler.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package handler

import (
"code.google.com/p/go-uuid/uuid"
"encoding/json"
"fmt"
"github.com/asaskevich/govalidator"
Expand All @@ -10,6 +9,7 @@ import (
"github.com/ory-am/hydra/middleware"
. "github.com/ory-am/hydra/oauth/connection"
. "github.com/ory-am/hydra/pkg"
"github.com/pborman/uuid"
"golang.org/x/net/context"
"net/http"
)
Expand Down
12 changes: 12 additions & 0 deletions oauth/connection/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ var data = map[string]*DefaultConnection{
LocalSubject: "steve",
},
"fail": &DefaultConnection{},
"fail-validation": &DefaultConnection{
Provider: "",
RemoteSubject: "",
LocalSubject: "",
},
}

func TestCreateGetDeleteGet(t *testing.T) {
Expand All @@ -112,6 +117,7 @@ func TestCreateGetDeleteGet(t *testing.T) {
{subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["fail"]}, createData: data["fail"], statusCreate: http.StatusForbidden},
{subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["pass-create"]}, createData: data["ok-max"], statusCreate: http.StatusOK, statusGet: http.StatusForbidden},
{subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["pass-create"]}, createData: data["fail"], statusCreate: http.StatusBadRequest},
{subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["pass-create"]}, createData: data["fail-validation"], statusCreate: http.StatusBadRequest},
{subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["pass-create"], policies["pass-get"]}, createData: data["ok-zac"], statusCreate: http.StatusOK, statusGet: http.StatusOK, statusDelete: http.StatusForbidden},
{subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["pass-all"]}, createData: data["ok-steve"], statusCreate: http.StatusOK, statusGet: http.StatusOK, statusDelete: http.StatusAccepted, statusGetAfterDelete: http.StatusNotFound},
} {
Expand Down Expand Up @@ -146,6 +152,9 @@ func TestCreateGetDeleteGet(t *testing.T) {
return
}

resp, body, _ = request.Post(connectionsURL).Send(*c.createData).End()
require.Equal(t, http.StatusInternalServerError, resp.StatusCode, "case %d: %s", k, body)

resp, body, _ = request.Delete(fmt.Sprintf("%s/oauth2/connections/%s", ts.URL, conn.ID)).End()
require.Equal(t, c.statusDelete, resp.StatusCode, "case %d: %s", k, body)
if resp.StatusCode != http.StatusAccepted {
Expand All @@ -154,6 +163,9 @@ func TestCreateGetDeleteGet(t *testing.T) {

resp, body, _ = request.Get(fmt.Sprintf("%s/oauth2/connections/%s", ts.URL, conn.ID)).End()
require.Equal(t, c.statusGetAfterDelete, resp.StatusCode, "case %d: %s", k, body)

resp, body, _ = request.Delete(fmt.Sprintf("%s/oauth2/connections/%s", ts.URL, conn.ID)).End()
require.Equal(t, http.StatusNotFound, resp.StatusCode, "case %d: %s", k, body)
}()
}
}
4 changes: 2 additions & 2 deletions oauth/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request) {
bearer := osin.CheckBearerAuth(r)
if bearer == nil {
log.WithField("introspect", "fail").Warn("No authorization header given.")
http.Error(w, "No bearer given.", http.StatusForbidden)
http.Error(w, "No bearer given.", http.StatusUnauthorized)
return
} else if bearer.Code == "" {
log.WithField("introspect", "fail").Warn("No authorization bearer is empty.")
http.Error(w, "No bearer token given.", http.StatusForbidden)
http.Error(w, "No bearer token given.", http.StatusUnauthorized)
return
}

Expand Down
85 changes: 64 additions & 21 deletions oauth/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func TestIntrospect(t *testing.T) {
ts := httptest.NewServer(router)
defer ts.Close()

config := *configs["working"]
config := configs["working"]
user := logins["working"]
clientConfig := clientcredentials.Config{
ClientID: config.ClientID,
Expand All @@ -286,28 +286,71 @@ func TestIntrospect(t *testing.T) {
}
config.Endpoint = oauth2.Endpoint{AuthURL: ts.URL + "/oauth2/auth", TokenURL: ts.URL + "/oauth2/token"}

access, err := clientConfig.Token(oauth2.NoContext)
require.Nil(t, err)
verify, err := config.PasswordCredentialsToken(oauth2.NoContext, user.Username, user.Password)
require.Nil(t, err)
access, _ := clientConfig.Token(oauth2.NoContext)
verify, _ := config.PasswordCredentialsToken(oauth2.NoContext, user.Username, user.Password)

client := &http.Client{}
form := url.Values{}
form.Add("token", access.AccessToken)

req, err := http.NewRequest("POST", ts.URL+"/oauth2/introspect", strings.NewReader(form.Encode()))
require.Nil(t, err)
for k, c := range []*struct {
accessToken string
code int
pass bool
}{
{"Bearer " + verify.AccessToken, http.StatusOK, true},
{"", http.StatusUnauthorized, false},
{"Bearer ", http.StatusUnauthorized, false},
{"Bearer invalid", http.StatusForbidden, false},
{"Bearer invalid", http.StatusForbidden, false},
{"Bearer invalid", http.StatusForbidden, false},

// "exp": "2012-04-23T18:25:43.511Z"
{"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMDEyLTA0LTIzVDE4OjI1OjQzLjUxMVoifQ.YPCfgNDs-UT6vNqh6095cXiMe0jcA9HjHuLi6hK6YBPsEHwHFniFGXAYt1PpPabBHAz7lQQ8zZao6LrVXkfz7PLbeQZl3KY0SUb-Wb0eEDjX4naEdm20whrYMZQ36VcTMT-FsGk5MB-nIYKq3iX6FMhumV8StjpC0jrM14488lPwLXihC1uITQBNVFEyXV_emhfuyojWEcEq899oE_vVRd7pTOmIhU8dFEAonoLZyPTKzSfvqaurPeySA5ttA-TTMTxZNzGVxWV4cwYHlhTXfS57zoSF_EN_PULTqMepUe8RC9AFnwyvNAa5e4nxQG5yO6b7cUGa0vSCD5FPbNBh-w", http.StatusForbidden, false},

// {
// "exp": "2099-04-23T18:25:43.511Z",
// "nbf": "2099-04-23T18:25:43.511Z"
// }
{"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJuYmYiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoifQ.hCuvBuiwEjjTbL8NMfEe6exDaRUeQIHodTNc5uBdY1lxmJWfFPh2zykuEvinqTprQe2CPRmL3Dk6jX3pcnigg7IjMX-EZueOnJc229gwjmJJiIGuUJOV3bLc-0xQ3cu6FCRc2NgOEh6Nq6Jh8G7ko4Du4gGrFsn97kbzAUYyns98T8442p0YXdQF-KVCc87fCkdr6OTsbfomy7jUDLCWptyJqREOoBll-nzyFWTxGHgoH_DmHft64SwvsvRafqZv9Q48bRzr857ps6OjEPncjRTriAsJa-p7aPKO2e7LXLKpopcaNwC09RNteAO4XPc2_M-IrYf6a02UzgSmOkIZUg", http.StatusForbidden, false},

// {
// "exp": "2099-04-23T18:25:43.511Z",
// "iat": "2000-04-23T18:25:43.511Z",
// "nbf": "2099-04-23T18:25:43.511Z"
// }
{"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJpYXQiOiIyMDAwLTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJuYmYiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoifQ.WtRurXoCy4kHPxnaL5ccPaeHIaDogXRFE6mqyF8nVTSsv6E7FaJg4IiYylxa44ty8GRMYn7c2CSyQefTVauqjJm8b0Rpu4biIeyCQRzwTZZzqZbc6irdWYsJu4DkwfAU0yP2EaLEtQOG3scnDpmtyCp7NvDAi8XlVeytOSHjqyJMWzqO_z5eU4e2Ap-3wkLo4P9_W1W3Tx_V0xQR2VaOXtVjEa_VS36rAMBy6WAvYQrYNlvBAA6OBfqg2uvKUfmEoE6MchkFxHFTSGBmI2boDfF2XGlyLn0di7gIBG-udXDv_zaVp4BtuswygTskV5d2i3pvLGP6UuJJhc7VVOAoPw", http.StatusForbidden, false},

// {
// "exp": "2099-04-23T18:25:43.511Z",
// "iat": "2000-04-23T18:25:43.511Z",
// "nbf": "2000-04-23T18:25:43.511Z",
// "aud": "wrong-audience"
// }
{"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJpYXQiOiIyMDAwLTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJuYmYiOiIyMDAwLTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJhdWQiOiJ3cm9uZy1hdWRpZW5jZSJ9.rF4JqVpawgHcg_H2hAAsEI2GUxzxCote4pUlruK9hLF-Dv-YSeEmMcFBhfxgsFuDCJotUCG6v8EhwI4u2wxGQHzLz70a-0AEZLQBccCfF_V4qAk8B7M5z2fO7xtEy8RkB2pZKCHbJ1f_6MSM_EyV6r4oiwedveBSsLKcjDhWE3_wExmtmtZaujJy53gR8Wh7BnUt6pl95_d7OMFjGEp1C_N0f3xd9SizIZ-qlIwHiX4xLHtvTZIjdmfyzXxPm_MK_aMOXmX0F6DQn5tgMzAggEdKSD6YdU8HM256zLQeddczrrDI5P3SASiBJ6MCUM4AzbvoFuFAilQi0WzpLpmlJw", http.StatusOK, false},

// {
// "exp": "2099-04-23T18:25:43.511Z",
// "iat": "2000-04-23T18:25:43.511Z",
// "nbf": "2000-04-23T18:25:43.511Z",
// "aud": "tests"
// }
{"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJpYXQiOiIyMDAwLTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJuYmYiOiIyMDAwLTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJhdWQiOiJ0ZXN0cyJ9.NQZCoKU2qoC-_VFi-_8fQDzObeQrnld9wyaqF0jYHL_wqROn5VumCDVl1oxMN7g-L9wqo5U-xUXf1HS_Ae6CLDFlkbd6dI-h1_l7_ALn_L_GoxQsEo2lQUDQ-Q4eqlLabc764cTYFXd5EwcsZMHWs5ZFCeMOv3exfeTmg8E9e1FiyuTuKVjvMxL-ZCh113nzXEGFr6GRzqjL6VSnJPDX0Pv78R9tnL6CqWbCuDBlIPOccbpWLuWF0yKjV-OyvcWpjkLIVtAbrimi3A7cNUI_V3EJm9Y4tr8e6hv9zViPNbhycmqvOp-vur2k64PrzeMcbuj7TFRCJg2V3moPJF3NtQ", http.StatusOK, true},
} {

req.Header.Add("Authorization", "Bearer "+verify.AccessToken)
res, err := client.Do(req)
require.Nil(t, err)
client := &http.Client{}
form := url.Values{}
form.Add("token", access.AccessToken)

body, err := ioutil.ReadAll(res.Body)
require.Nil(t, err)
require.Equal(t, http.StatusOK, res.StatusCode, "%v", body)
req, _ := http.NewRequest("POST", ts.URL+"/oauth2/introspect", strings.NewReader(form.Encode()))
if c.accessToken != "" {
req.Header.Add("Authorization", c.accessToken)
}
res, _ := client.Do(req)
body, _ := ioutil.ReadAll(res.Body)
require.Equal(t, c.code, res.StatusCode, "Case %d: %s", k, body)
if res.StatusCode != http.StatusOK {
continue
}

var result map[string]interface{}
require.Nil(t, json.Unmarshal(body, &result))
assert.True(t, result["active"].(bool))
t.Logf("Got token: %s", body)
var result map[string]interface{}
require.Nil(t, json.Unmarshal(body, &result))
assert.Equal(t, c.pass, result["active"].(bool), "Case %d", k)
}
}
30 changes: 30 additions & 0 deletions oauth/provider/provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package provider

import (
"github.com/RangelReale/osin"
"github.com/stretchr/testify/assert"
"golang.org/x/oauth2"
"testing"
)

func TestGetAuthCodeURL(t *testing.T) {
conf := oauth2.Config{
ClientID: "remote-client",
ClientSecret: "secret",
Endpoint: oauth2.Endpoint{
AuthURL: "http://third-party/oauth2/auth",
TokenURL: "http://third-party/oauth2/token",
},
RedirectURL: "http://hydra/oauth2/auth",
Scopes: []string{"scope"},
}
ar := &osin.AuthorizeRequest{
Type: osin.CODE,
Client: &osin.DefaultClient{Id: "client", Secret: "secret"},
Scope: "scope",
RedirectUri: "http://remote/callback",
State: "state",
}
url := GetAuthCodeURL(conf, ar, "foo")
assert.Equal(t, "http://third-party/oauth2/auth?client_id=remote-client&redirect_uri=http%3A%2F%2Fhydra%2Foauth2%2Fauth%3Focl%3Dclient%26opr%3Dfoo%26ord%3Dhttp%253A%252F%252Fremote%252Fcallback%26osc%3Dscope%26ost%3Dstate%26otp%3Dcode&response_type=code&scope=scope&state=state", url)
}
1 change: 1 addition & 0 deletions pkg/writeJSON.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ func WriteJSON(rw http.ResponseWriter, connection interface{}) {

rw.Header().Set("Content-Type", "application/json")
rw.Write(js)
rw.WriteHeader(http.StatusOK)
}
42 changes: 42 additions & 0 deletions pkg/writeJSON_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package pkg

import (
"github.com/stretchr/testify/assert"
"net/http"
"testing"
)

type rw struct {
written []byte
code int
header http.Header
}

func (r *rw) Header() http.Header {
return r.header
}

func (r *rw) Write(w []byte) (int, error) {
r.written = w
return 0, nil
}

func (r *rw) WriteHeader(c int) {
r.code = c
}

func TestWriteJSON(t *testing.T) {
r := &rw{header: http.Header{}}
js := struct {
Foo string `json:"foo"`
}{
Foo: "bar",
}
WriteJSON(r, js)
assert.Equal(t, http.StatusOK, r.code)
assert.Equal(t, `{"foo":"bar"}`, string(r.written))
assert.Equal(t, r.Header().Get("Content-Type"), "application/json")

WriteJSON(r, func() {})
assert.Equal(t, http.StatusInternalServerError, r.code)
}