257 lines
7.7 KiB
Go
257 lines
7.7 KiB
Go
|
package server
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"io/ioutil"
|
||
|
"net/http"
|
||
|
"net/http/httptest"
|
||
|
"net/url"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/coreos/dex/client"
|
||
|
schema "github.com/coreos/dex/schema/workerschema"
|
||
|
"github.com/coreos/go-oidc/oidc"
|
||
|
)
|
||
|
|
||
|
func makeBody(s string) io.ReadCloser {
|
||
|
return ioutil.NopCloser(strings.NewReader(s))
|
||
|
}
|
||
|
|
||
|
func TestCreateInvalidRequest(t *testing.T) {
|
||
|
u := &url.URL{Scheme: "http", Host: "example.com", Path: "clients"}
|
||
|
h := http.Header{"Content-Type": []string{"application/json"}}
|
||
|
repo := client.NewClientIdentityRepo(nil)
|
||
|
res := &clientResource{repo: repo}
|
||
|
tests := []struct {
|
||
|
req *http.Request
|
||
|
wantCode int
|
||
|
wantBody string
|
||
|
}{
|
||
|
// invalid content-type
|
||
|
{
|
||
|
req: &http.Request{Method: "POST", URL: u, Header: http.Header{"Content-Type": []string{"application/xml"}}},
|
||
|
wantCode: http.StatusBadRequest,
|
||
|
wantBody: `{"error":"invalid_request","error_description":"unsupported content-type"}`,
|
||
|
},
|
||
|
// invalid method
|
||
|
{
|
||
|
req: &http.Request{Method: "DELETE", URL: u, Header: h},
|
||
|
wantCode: http.StatusMethodNotAllowed,
|
||
|
wantBody: `{"error":"invalid_request","error_description":"HTTP DELETE method not supported for this resource"}`,
|
||
|
},
|
||
|
// invalid method
|
||
|
{
|
||
|
req: &http.Request{Method: "PUT", URL: u, Header: h},
|
||
|
wantCode: http.StatusMethodNotAllowed,
|
||
|
wantBody: `{"error":"invalid_request","error_description":"HTTP PUT method not supported for this resource"}`,
|
||
|
},
|
||
|
// invalid method
|
||
|
{
|
||
|
req: &http.Request{Method: "HEAD", URL: u, Header: h},
|
||
|
wantCode: http.StatusMethodNotAllowed,
|
||
|
wantBody: `{"error":"invalid_request","error_description":"HTTP HEAD method not supported for this resource"}`,
|
||
|
},
|
||
|
// unserializable body
|
||
|
{
|
||
|
req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody("asdf")},
|
||
|
wantCode: http.StatusBadRequest,
|
||
|
wantBody: `{"error":"invalid_request","error_description":"unable to decode request body"}`,
|
||
|
},
|
||
|
// empty body
|
||
|
{
|
||
|
req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody("")},
|
||
|
wantCode: http.StatusBadRequest,
|
||
|
wantBody: `{"error":"invalid_request","error_description":"unable to decode request body"}`,
|
||
|
},
|
||
|
// missing url field
|
||
|
{
|
||
|
req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"id":"foo"}`)},
|
||
|
wantCode: http.StatusBadRequest,
|
||
|
wantBody: `{"error":"invalid_client_metadata","error_description":"zero redirect URLs"}`,
|
||
|
},
|
||
|
// empty url array
|
||
|
{
|
||
|
req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":[]}`)},
|
||
|
wantCode: http.StatusBadRequest,
|
||
|
wantBody: `{"error":"invalid_client_metadata","error_description":"zero redirect URLs"}`,
|
||
|
},
|
||
|
// array with empty string
|
||
|
{
|
||
|
req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":[""]}`)},
|
||
|
wantCode: http.StatusBadRequest,
|
||
|
wantBody: `{"error":"invalid_client_metadata","error_description":"missing or invalid field: redirectURIs"}`,
|
||
|
},
|
||
|
// uri with unusable scheme
|
||
|
{
|
||
|
req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":["asdf.com"]}`)},
|
||
|
wantCode: http.StatusBadRequest,
|
||
|
wantBody: `{"error":"invalid_client_metadata","error_description":"invalid redirect URL: scheme not http/https"}`,
|
||
|
},
|
||
|
// uri missing host
|
||
|
{
|
||
|
req: &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":["http://"]}`)},
|
||
|
wantCode: http.StatusBadRequest,
|
||
|
wantBody: `{"error":"invalid_client_metadata","error_description":"invalid redirect URL: host empty"}`,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, tt := range tests {
|
||
|
w := httptest.NewRecorder()
|
||
|
res.ServeHTTP(w, tt.req)
|
||
|
|
||
|
if w.Code != tt.wantCode {
|
||
|
t.Errorf("case %d: invalid response code, want=%d, got=%d", i, tt.wantCode, w.Code)
|
||
|
}
|
||
|
|
||
|
gotBody := w.Body.String()
|
||
|
if gotBody != tt.wantBody {
|
||
|
t.Errorf("case %d: invalid response body, want=%s, got=%s", i, tt.wantBody, gotBody)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCreate(t *testing.T) {
|
||
|
repo := client.NewClientIdentityRepo(nil)
|
||
|
res := &clientResource{repo: repo}
|
||
|
tests := [][]string{
|
||
|
[]string{"http://example.com"},
|
||
|
[]string{"https://example.com"},
|
||
|
[]string{"http://example.com/foo"},
|
||
|
[]string{"http://example.com/bar", "http://example.com/foo"},
|
||
|
}
|
||
|
endpoint := "http://example.com/clients"
|
||
|
|
||
|
for i, tt := range tests {
|
||
|
body := strings.NewReader(fmt.Sprintf(`{"redirectURIs":["%s"]}`, strings.Join(tt, `","`)))
|
||
|
r, err := http.NewRequest("POST", endpoint, body)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed creating http.Request: %v", err)
|
||
|
}
|
||
|
r.Header.Set("content-type", "application/json")
|
||
|
w := httptest.NewRecorder()
|
||
|
res.ServeHTTP(w, r)
|
||
|
|
||
|
if w.Code != http.StatusCreated {
|
||
|
t.Errorf("case %d: invalid response code, want=%d, got=%d", i, http.StatusCreated, w.Code)
|
||
|
}
|
||
|
|
||
|
var client schema.ClientWithSecret
|
||
|
if err := json.Unmarshal(w.Body.Bytes(), &client); err != nil {
|
||
|
t.Errorf("case %d: unexpected error=%v", i, err)
|
||
|
}
|
||
|
if len(client.RedirectURIs) != len(tt) {
|
||
|
t.Errorf("case %d: unexpected number of redirect URIs, want=%d, got=%d", i, len(tt), len(client.RedirectURIs))
|
||
|
}
|
||
|
|
||
|
if !reflect.DeepEqual(tt, client.RedirectURIs) {
|
||
|
t.Errorf("case %d: unexpected client redirect URIs: want=%v got=%v", i, tt, client.RedirectURIs)
|
||
|
}
|
||
|
|
||
|
if client.Id == "" {
|
||
|
t.Errorf("case %d: empty client ID in response", i)
|
||
|
}
|
||
|
|
||
|
if client.Secret == "" {
|
||
|
t.Errorf("case %d: empty client secret in response", i)
|
||
|
}
|
||
|
|
||
|
wantLoc := fmt.Sprintf("%s/%s", endpoint, client.Id)
|
||
|
gotLoc := w.Header().Get("Location")
|
||
|
if gotLoc != wantLoc {
|
||
|
t.Errorf("case %d: invalid location header, want=%v, got=%v", i, wantLoc, gotLoc)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestList(t *testing.T) {
|
||
|
tests := []struct {
|
||
|
cs []oidc.ClientIdentity
|
||
|
want []*schema.Client
|
||
|
}{
|
||
|
// empty repo
|
||
|
{
|
||
|
cs: nil,
|
||
|
want: nil,
|
||
|
},
|
||
|
// single client
|
||
|
{
|
||
|
cs: []oidc.ClientIdentity{
|
||
|
oidc.ClientIdentity{
|
||
|
Credentials: oidc.ClientCredentials{ID: "foo", Secret: "bar"},
|
||
|
Metadata: oidc.ClientMetadata{
|
||
|
RedirectURLs: []url.URL{
|
||
|
url.URL{Scheme: "http", Host: "example.com"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
want: []*schema.Client{
|
||
|
&schema.Client{
|
||
|
Id: "foo",
|
||
|
RedirectURIs: []string{"http://example.com"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
// multi client
|
||
|
{
|
||
|
cs: []oidc.ClientIdentity{
|
||
|
oidc.ClientIdentity{
|
||
|
Credentials: oidc.ClientCredentials{ID: "foo", Secret: "bar"},
|
||
|
Metadata: oidc.ClientMetadata{
|
||
|
RedirectURLs: []url.URL{
|
||
|
url.URL{Scheme: "http", Host: "example.com"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
oidc.ClientIdentity{
|
||
|
Credentials: oidc.ClientCredentials{ID: "biz", Secret: "bang"},
|
||
|
Metadata: oidc.ClientMetadata{
|
||
|
RedirectURLs: []url.URL{
|
||
|
url.URL{Scheme: "https", Host: "example.com", Path: "one/two/three"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
want: []*schema.Client{
|
||
|
&schema.Client{
|
||
|
Id: "biz",
|
||
|
RedirectURIs: []string{"https://example.com/one/two/three"},
|
||
|
},
|
||
|
&schema.Client{
|
||
|
Id: "foo",
|
||
|
RedirectURIs: []string{"http://example.com"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, tt := range tests {
|
||
|
repo := client.NewClientIdentityRepo(tt.cs)
|
||
|
res := &clientResource{repo: repo}
|
||
|
|
||
|
r, err := http.NewRequest("GET", "http://example.com/clients", nil)
|
||
|
if err != nil {
|
||
|
t.Fatalf("Failed creating http.Request: %v", err)
|
||
|
}
|
||
|
w := httptest.NewRecorder()
|
||
|
res.ServeHTTP(w, r)
|
||
|
|
||
|
if w.Code != http.StatusOK {
|
||
|
t.Errorf("case %d: invalid response code, want=%d, got=%d", i, http.StatusOK, w.Code)
|
||
|
}
|
||
|
|
||
|
var resp schema.ClientPage
|
||
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||
|
t.Errorf("case %d: unexpected error=%v", i, err)
|
||
|
}
|
||
|
|
||
|
if !reflect.DeepEqual(tt.want, resp.Clients) {
|
||
|
t.Errorf("case %d: invalid response body, want=%#v, got=%#v", i, tt.want, resp.Clients)
|
||
|
}
|
||
|
}
|
||
|
}
|