auth

Paddy 2014-11-11 Parent:http_test.go@41d8f972720c Child:d43c3fbf00f3

73:4ae226929e92 Go to Latest

auth/oauth2_test.go

Rename http.go. We're going to have a lot of HTTP handlers, and I'd rather make it clear that this is taking care of our OAuth2 HTTP logic. So rename the file, and we'll put the API handlers in their files, or something.

History
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/oauth2_test.go	Tue Nov 11 21:22:57 2014 -0500
     1.3 @@ -0,0 +1,451 @@
     1.4 +package auth
     1.5 +
     1.6 +import (
     1.7 +	"bytes"
     1.8 +	"html/template"
     1.9 +	"io/ioutil"
    1.10 +	"net/http"
    1.11 +	"net/http/httptest"
    1.12 +	"net/url"
    1.13 +	"testing"
    1.14 +	"time"
    1.15 +
    1.16 +	"code.secondbit.org/uuid"
    1.17 +)
    1.18 +
    1.19 +const (
    1.20 +	scopeSet = 1 << iota
    1.21 +	stateSet
    1.22 +	uriSet
    1.23 +)
    1.24 +
    1.25 +func stripParam(param string, u *url.URL) {
    1.26 +	q := u.Query()
    1.27 +	q.Del(param)
    1.28 +	u.RawQuery = q.Encode()
    1.29 +}
    1.30 +
    1.31 +func TestGetGrantCodeSuccess(t *testing.T) {
    1.32 +	t.Parallel()
    1.33 +	store := NewMemstore()
    1.34 +	testContext := Context{
    1.35 +		template: template.Must(template.New(getGrantTemplateName).Parse("Get auth grant")),
    1.36 +		clients:  store,
    1.37 +		grants:   store,
    1.38 +		profiles: store,
    1.39 +		tokens:   store,
    1.40 +	}
    1.41 +	client := Client{
    1.42 +		ID:      uuid.NewID(),
    1.43 +		Secret:  "super secret!",
    1.44 +		OwnerID: uuid.NewID(),
    1.45 +		Name:    "My test client",
    1.46 +		Logo:    "https://secondbit.org/logo.png",
    1.47 +		Website: "https://secondbit.org",
    1.48 +		Type:    "public",
    1.49 +	}
    1.50 +	uri, err := url.Parse("https://test.secondbit.org/redirect")
    1.51 +	if err != nil {
    1.52 +		t.Fatal("Can't parse URL:", err)
    1.53 +	}
    1.54 +	endpoint := Endpoint{
    1.55 +		ID:       uuid.NewID(),
    1.56 +		ClientID: client.ID,
    1.57 +		URI:      *uri,
    1.58 +		Added:    time.Now(),
    1.59 +	}
    1.60 +	err = testContext.SaveClient(client)
    1.61 +	if err != nil {
    1.62 +		t.Fatal("Can't store client:", err)
    1.63 +	}
    1.64 +	err = testContext.AddEndpoint(client.ID, endpoint)
    1.65 +	if err != nil {
    1.66 +		t.Fatal("Can't store endpoint:", err)
    1.67 +	}
    1.68 +	req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
    1.69 +	if err != nil {
    1.70 +		t.Fatal("Can't build request:", err)
    1.71 +	}
    1.72 +	for i := 0; i < 1<<3; i++ {
    1.73 +		w := httptest.NewRecorder()
    1.74 +		params := url.Values{}
    1.75 +		// see OAuth 2.0 spec, section 4.1.1
    1.76 +		params.Set("response_type", "code")
    1.77 +		params.Set("client_id", client.ID.String())
    1.78 +		if i&uriSet != 0 {
    1.79 +			params.Set("redirect_uri", endpoint.URI.String())
    1.80 +		}
    1.81 +		if i&scopeSet != 0 {
    1.82 +			params.Set("scope", "testscope")
    1.83 +		}
    1.84 +		if i&stateSet != 0 {
    1.85 +			params.Set("state", "my super secure state string")
    1.86 +		}
    1.87 +		req.URL.RawQuery = params.Encode()
    1.88 +		req.Method = "GET"
    1.89 +		req.Body = nil
    1.90 +		req.Header.Del("Content-Type")
    1.91 +		GetGrantHandler(w, req, testContext)
    1.92 +		if w.Code != http.StatusOK {
    1.93 +			t.Errorf("Expected status code to be %d, got %d for %s", http.StatusOK, w.Code, req.URL.String())
    1.94 +		}
    1.95 +		if w.Body.String() != "Get auth grant" {
    1.96 +			t.Errorf("Expected body to be `%s`, got `%s` for %s", "Get auth grant", w.Body.String(), req.URL.String())
    1.97 +		}
    1.98 +		req.Method = "POST"
    1.99 +		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   1.100 +		w = httptest.NewRecorder()
   1.101 +		data := url.Values{}
   1.102 +		data.Set("grant", "approved")
   1.103 +		body := bytes.NewBufferString(data.Encode())
   1.104 +		req.Body = ioutil.NopCloser(body)
   1.105 +		GetGrantHandler(w, req, testContext)
   1.106 +		if w.Code != http.StatusFound {
   1.107 +			t.Errorf("Expected status code to be %d, got %d for %s", http.StatusFound, w.Code, req.URL.String())
   1.108 +		}
   1.109 +		redirectedTo := w.Header().Get("Location")
   1.110 +		red, err := url.Parse(redirectedTo)
   1.111 +		if err != nil {
   1.112 +			t.Fatalf(`Being redirected to a non-URL "%s" threw error "%s" for "%s"\n`, redirectedTo, err, req.URL.String())
   1.113 +		}
   1.114 +		t.Log("Redirected to", redirectedTo)
   1.115 +		if red.Query().Get("code") == "" {
   1.116 +			t.Fatalf(`Expected code param in redirect URL to be set, but it wasn't for %s`, req.URL.String())
   1.117 +		}
   1.118 +		if _, err := testContext.GetGrant(red.Query().Get("code")); err != nil {
   1.119 +			t.Fatalf(`Unexpected error "%s: retrieving the grant "%s" supplied in the redirect URL for %s`, err, red.Query().Get("code"), req.URL.String())
   1.120 +		}
   1.121 +		err = testContext.DeleteGrant(red.Query().Get("code"))
   1.122 +		if err != nil {
   1.123 +			t.Log(`Unexpected error "%s" deleting grant "%s" for %s`, err, red.Query().Get("code"), req.URL.String())
   1.124 +		}
   1.125 +		stripParam("code", red)
   1.126 +		if red.Query().Get("state") != params.Get("state") {
   1.127 +			t.Errorf(`Expected state param in redirect URL to be "%s", got "%s" for %s`, params.Get("state"), red.Query().Get("state"), req.URL.String())
   1.128 +		}
   1.129 +		stripParam("state", red)
   1.130 +		if red.String() != endpoint.URI.String() {
   1.131 +			t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
   1.132 +		}
   1.133 +	}
   1.134 +}
   1.135 +
   1.136 +func TestGetGrantCodeInvalidClient(t *testing.T) {
   1.137 +	t.Parallel()
   1.138 +	store := NewMemstore()
   1.139 +	testContext := Context{
   1.140 +		template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
   1.141 +		clients:  store,
   1.142 +		grants:   store,
   1.143 +		profiles: store,
   1.144 +		tokens:   store,
   1.145 +	}
   1.146 +	client := Client{
   1.147 +		ID:      uuid.NewID(),
   1.148 +		Secret:  "super secret!",
   1.149 +		OwnerID: uuid.NewID(),
   1.150 +		Name:    "My test client",
   1.151 +		Type:    "public",
   1.152 +	}
   1.153 +	err := testContext.SaveClient(client)
   1.154 +	if err != nil {
   1.155 +		t.Fatal("Can't store client:", err)
   1.156 +	}
   1.157 +	req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
   1.158 +	if err != nil {
   1.159 +		t.Fatal("Can't build request:", err)
   1.160 +	}
   1.161 +	w := httptest.NewRecorder()
   1.162 +	params := url.Values{}
   1.163 +	params.Set("response_type", "code")
   1.164 +	params.Set("redirect_uri", "https://test.secondbit.org/")
   1.165 +	req.URL.RawQuery = params.Encode()
   1.166 +	GetGrantHandler(w, req, testContext)
   1.167 +	if w.Code != http.StatusBadRequest {
   1.168 +		t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
   1.169 +	}
   1.170 +	if w.Body.String() != "Client ID must be specified in the request." {
   1.171 +		t.Errorf(`Expected output to be "%s", got "%s" instead.`, "Client ID must be specified in the request.", w.Body.String())
   1.172 +	}
   1.173 +	w = httptest.NewRecorder()
   1.174 +	params.Set("client_id", "Not an ID")
   1.175 +	req.URL.RawQuery = params.Encode()
   1.176 +	GetGrantHandler(w, req, testContext)
   1.177 +	if w.Code != http.StatusBadRequest {
   1.178 +		t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
   1.179 +	}
   1.180 +	if w.Body.String() != "client_id is not a valid Client ID." {
   1.181 +		t.Errorf(`Expected output to be "%s", got "%s" instead.`, "client_id is not a valid Client ID.", w.Body.String())
   1.182 +	}
   1.183 +	w = httptest.NewRecorder()
   1.184 +	params.Set("client_id", uuid.NewID().String())
   1.185 +	req.URL.RawQuery = params.Encode()
   1.186 +	GetGrantHandler(w, req, testContext)
   1.187 +	if w.Code != http.StatusBadRequest {
   1.188 +		t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
   1.189 +	}
   1.190 +	if w.Body.String() != "The specified Client couldn&rsquo;t be found." {
   1.191 +		t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The specified Client couldn&rsquo;t be found.", w.Body.String())
   1.192 +	}
   1.193 +}
   1.194 +
   1.195 +func TestGetGrantCodeInvalidURI(t *testing.T) {
   1.196 +	t.Parallel()
   1.197 +	store := NewMemstore()
   1.198 +	testContext := Context{
   1.199 +		template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
   1.200 +		clients:  store,
   1.201 +		grants:   store,
   1.202 +		profiles: store,
   1.203 +		tokens:   store,
   1.204 +	}
   1.205 +	client := Client{
   1.206 +		ID:      uuid.NewID(),
   1.207 +		Secret:  "super secret!",
   1.208 +		OwnerID: uuid.NewID(),
   1.209 +		Name:    "My test client",
   1.210 +		Type:    "public",
   1.211 +	}
   1.212 +	uri, err := url.Parse("https://test.secondbit.org/redirect")
   1.213 +	if err != nil {
   1.214 +		t.Fatal("Can't parse URL:", err)
   1.215 +	}
   1.216 +	err = testContext.SaveClient(client)
   1.217 +	if err != nil {
   1.218 +		t.Fatal("Can't store client:", err)
   1.219 +	}
   1.220 +	req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
   1.221 +	if err != nil {
   1.222 +		t.Fatal("Can't build request:", err)
   1.223 +	}
   1.224 +	w := httptest.NewRecorder()
   1.225 +	params := url.Values{}
   1.226 +	params.Set("response_type", "code")
   1.227 +	params.Set("client_id", client.ID.String())
   1.228 +	req.URL.RawQuery = params.Encode()
   1.229 +	GetGrantHandler(w, req, testContext)
   1.230 +	if w.Code != http.StatusBadRequest {
   1.231 +		t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
   1.232 +	}
   1.233 +	if w.Body.String() != "The redirect_uri specified is not valid." {
   1.234 +		t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
   1.235 +	}
   1.236 +	endpoint := Endpoint{
   1.237 +		ID:       uuid.NewID(),
   1.238 +		ClientID: client.ID,
   1.239 +		URI:      *uri,
   1.240 +		Added:    time.Now(),
   1.241 +	}
   1.242 +	err = testContext.AddEndpoint(client.ID, endpoint)
   1.243 +	if err != nil {
   1.244 +		t.Fatal("Can't store endpoint:", err)
   1.245 +	}
   1.246 +	w = httptest.NewRecorder()
   1.247 +	params.Set("redirect_uri", "https://test.secondbit.org/wrong")
   1.248 +	req.URL.RawQuery = params.Encode()
   1.249 +	GetGrantHandler(w, req, testContext)
   1.250 +	if w.Code != http.StatusBadRequest {
   1.251 +		t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
   1.252 +	}
   1.253 +	if w.Body.String() != "The redirect_uri specified is not valid." {
   1.254 +		t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
   1.255 +	}
   1.256 +	endpoint2 := Endpoint{
   1.257 +		ID:       uuid.NewID(),
   1.258 +		ClientID: client.ID,
   1.259 +		URI:      *uri,
   1.260 +		Added:    time.Now(),
   1.261 +	}
   1.262 +	err = testContext.AddEndpoint(client.ID, endpoint2)
   1.263 +	if err != nil {
   1.264 +		t.Fatal("Can't store endpoint:", err)
   1.265 +	}
   1.266 +	w = httptest.NewRecorder()
   1.267 +	params.Set("redirect_uri", "")
   1.268 +	req.URL.RawQuery = params.Encode()
   1.269 +	GetGrantHandler(w, req, testContext)
   1.270 +	if w.Code != http.StatusBadRequest {
   1.271 +		t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
   1.272 +	}
   1.273 +	if w.Body.String() != "The redirect_uri specified is not valid." {
   1.274 +		t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
   1.275 +	}
   1.276 +	w = httptest.NewRecorder()
   1.277 +	params.Set("redirect_uri", "://not a URL")
   1.278 +	req.URL.RawQuery = params.Encode()
   1.279 +	GetGrantHandler(w, req, testContext)
   1.280 +	if w.Code != http.StatusBadRequest {
   1.281 +		t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
   1.282 +	}
   1.283 +	if w.Body.String() != "The redirect_uri specified is not valid." {
   1.284 +		t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
   1.285 +	}
   1.286 +}
   1.287 +
   1.288 +func TestGetGrantCodeInvalidResponseType(t *testing.T) {
   1.289 +	t.Parallel()
   1.290 +	store := NewMemstore()
   1.291 +	testContext := Context{
   1.292 +		template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
   1.293 +		clients:  store,
   1.294 +		grants:   store,
   1.295 +		profiles: store,
   1.296 +		tokens:   store,
   1.297 +	}
   1.298 +	client := Client{
   1.299 +		ID:      uuid.NewID(),
   1.300 +		Secret:  "super secret!",
   1.301 +		OwnerID: uuid.NewID(),
   1.302 +		Name:    "My test client",
   1.303 +		Logo:    "https://secondbit.org/logo.png",
   1.304 +		Website: "https://secondbit.org",
   1.305 +		Type:    "public",
   1.306 +	}
   1.307 +	uri, err := url.Parse("https://test.secondbit.org/redirect")
   1.308 +	if err != nil {
   1.309 +		t.Fatal("Can't parse URL:", err)
   1.310 +	}
   1.311 +	endpoint := Endpoint{
   1.312 +		ID:       uuid.NewID(),
   1.313 +		ClientID: client.ID,
   1.314 +		URI:      *uri,
   1.315 +		Added:    time.Now(),
   1.316 +	}
   1.317 +	err = testContext.SaveClient(client)
   1.318 +	if err != nil {
   1.319 +		t.Fatal("Can't store client:", err)
   1.320 +	}
   1.321 +	err = testContext.AddEndpoint(client.ID, endpoint)
   1.322 +	if err != nil {
   1.323 +		t.Fatal("Can't store endpoint:", err)
   1.324 +	}
   1.325 +	req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
   1.326 +	if err != nil {
   1.327 +		t.Fatal("Can't build request:", err)
   1.328 +	}
   1.329 +	params := url.Values{}
   1.330 +	params.Set("response_type", "totally not code")
   1.331 +	params.Set("client_id", client.ID.String())
   1.332 +	params.Set("redirect_uri", endpoint.URI.String())
   1.333 +	params.Set("scope", "testscope")
   1.334 +	params.Set("state", "my super secure state string")
   1.335 +	req.URL.RawQuery = params.Encode()
   1.336 +	w := httptest.NewRecorder()
   1.337 +	GetGrantHandler(w, req, testContext)
   1.338 +	if w.Code != http.StatusFound {
   1.339 +		t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
   1.340 +	}
   1.341 +	redirectedTo := w.Header().Get("Location")
   1.342 +	red, err := url.Parse(redirectedTo)
   1.343 +	if err != nil {
   1.344 +		t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
   1.345 +	}
   1.346 +	if red.Query().Get("error") != "invalid_request" {
   1.347 +		t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error"))
   1.348 +	}
   1.349 +	stripParam("error", red)
   1.350 +	if red.Query().Get("state") != params.Get("state") {
   1.351 +		t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
   1.352 +	}
   1.353 +	stripParam("state", red)
   1.354 +	if red.String() != endpoint.URI.String() {
   1.355 +		t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
   1.356 +	}
   1.357 +	stripParam("response_type", req.URL)
   1.358 +	w = httptest.NewRecorder()
   1.359 +	GetGrantHandler(w, req, testContext)
   1.360 +	if w.Code != http.StatusFound {
   1.361 +		t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
   1.362 +	}
   1.363 +	redirectedTo = w.Header().Get("Location")
   1.364 +	red, err = url.Parse(redirectedTo)
   1.365 +	if err != nil {
   1.366 +		t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
   1.367 +	}
   1.368 +	if red.Query().Get("error") != "invalid_request" {
   1.369 +		t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error"))
   1.370 +	}
   1.371 +	stripParam("error", red)
   1.372 +	if red.Query().Get("state") != params.Get("state") {
   1.373 +		t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
   1.374 +	}
   1.375 +	stripParam("state", red)
   1.376 +	if red.String() != endpoint.URI.String() {
   1.377 +		t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
   1.378 +	}
   1.379 +}
   1.380 +
   1.381 +func TestGetGrantCodeDenied(t *testing.T) {
   1.382 +	t.Parallel()
   1.383 +	store := NewMemstore()
   1.384 +	testContext := Context{
   1.385 +		template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
   1.386 +		clients:  store,
   1.387 +		grants:   store,
   1.388 +		profiles: store,
   1.389 +		tokens:   store,
   1.390 +	}
   1.391 +	client := Client{
   1.392 +		ID:      uuid.NewID(),
   1.393 +		Secret:  "super secret!",
   1.394 +		OwnerID: uuid.NewID(),
   1.395 +		Name:    "My test client",
   1.396 +		Logo:    "https://secondbit.org/logo.png",
   1.397 +		Website: "https://secondbit.org",
   1.398 +		Type:    "public",
   1.399 +	}
   1.400 +	uri, err := url.Parse("https://test.secondbit.org/redirect")
   1.401 +	if err != nil {
   1.402 +		t.Fatal("Can't parse URL:", err)
   1.403 +	}
   1.404 +	endpoint := Endpoint{
   1.405 +		ID:       uuid.NewID(),
   1.406 +		ClientID: client.ID,
   1.407 +		URI:      *uri,
   1.408 +		Added:    time.Now(),
   1.409 +	}
   1.410 +	err = testContext.SaveClient(client)
   1.411 +	if err != nil {
   1.412 +		t.Fatal("Can't store client:", err)
   1.413 +	}
   1.414 +	err = testContext.AddEndpoint(client.ID, endpoint)
   1.415 +	if err != nil {
   1.416 +		t.Fatal("Can't store endpoint:", err)
   1.417 +	}
   1.418 +	req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
   1.419 +	if err != nil {
   1.420 +		t.Fatal("Can't build request:", err)
   1.421 +	}
   1.422 +	params := url.Values{}
   1.423 +	params.Set("response_type", "code")
   1.424 +	params.Set("client_id", client.ID.String())
   1.425 +	params.Set("redirect_uri", endpoint.URI.String())
   1.426 +	params.Set("scope", "testscope")
   1.427 +	params.Set("state", "my super secure state string")
   1.428 +	data := url.Values{}
   1.429 +	data.Set("grant", "denied")
   1.430 +	req.URL.RawQuery = params.Encode()
   1.431 +	req.Body = ioutil.NopCloser(bytes.NewBufferString(data.Encode()))
   1.432 +	req.Method = "POST"
   1.433 +	w := httptest.NewRecorder()
   1.434 +	GetGrantHandler(w, req, testContext)
   1.435 +	if w.Code != http.StatusFound {
   1.436 +		t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
   1.437 +	}
   1.438 +	redirectedTo := w.Header().Get("Location")
   1.439 +	red, err := url.Parse(redirectedTo)
   1.440 +	if err != nil {
   1.441 +		t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
   1.442 +	}
   1.443 +	if red.Query().Get("error") != "access_denied" {
   1.444 +		t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "access_denied", red.Query().Get("error"))
   1.445 +	}
   1.446 +	stripParam("error", red)
   1.447 +	if red.Query().Get("state") != params.Get("state") {
   1.448 +		t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
   1.449 +	}
   1.450 +	stripParam("state", red)
   1.451 +	if red.String() != endpoint.URI.String() {
   1.452 +		t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
   1.453 +	}
   1.454 +}