auth

Paddy 2014-11-19 Parent:d43c3fbf00f3 Child:eb3f2938a319

78:a9936cf794ba Go to Latest

auth/oauth2_test.go

More tests, login redirect bugfix. Add tests for our cookie checking helper and our helper for generating login redirection URIs. Fix a bug where the URL to redirect to was being URL-encoded twice when included in the login redirect URI.

History
paddy@52 1 package auth
paddy@52 2
paddy@52 3 import (
paddy@66 4 "bytes"
paddy@52 5 "html/template"
paddy@66 6 "io/ioutil"
paddy@52 7 "net/http"
paddy@52 8 "net/http/httptest"
paddy@53 9 "net/url"
paddy@52 10 "testing"
paddy@56 11 "time"
paddy@56 12
paddy@56 13 "code.secondbit.org/uuid"
paddy@52 14 )
paddy@52 15
paddy@53 16 const (
paddy@53 17 scopeSet = 1 << iota
paddy@53 18 stateSet
paddy@53 19 uriSet
paddy@53 20 )
paddy@53 21
paddy@65 22 func stripParam(param string, u *url.URL) {
paddy@65 23 q := u.Query()
paddy@65 24 q.Del(param)
paddy@65 25 u.RawQuery = q.Encode()
paddy@65 26 }
paddy@65 27
paddy@52 28 func TestGetGrantCodeSuccess(t *testing.T) {
paddy@52 29 t.Parallel()
paddy@52 30 store := NewMemstore()
paddy@52 31 testContext := Context{
paddy@52 32 template: template.Must(template.New(getGrantTemplateName).Parse("Get auth grant")),
paddy@52 33 clients: store,
paddy@52 34 grants: store,
paddy@52 35 profiles: store,
paddy@52 36 tokens: store,
paddy@77 37 sessions: store,
paddy@52 38 }
paddy@56 39 client := Client{
paddy@56 40 ID: uuid.NewID(),
paddy@56 41 Secret: "super secret!",
paddy@56 42 OwnerID: uuid.NewID(),
paddy@56 43 Name: "My test client",
paddy@56 44 Logo: "https://secondbit.org/logo.png",
paddy@56 45 Website: "https://secondbit.org",
paddy@56 46 Type: "public",
paddy@56 47 }
paddy@56 48 uri, err := url.Parse("https://test.secondbit.org/redirect")
paddy@56 49 if err != nil {
paddy@56 50 t.Fatal("Can't parse URL:", err)
paddy@56 51 }
paddy@56 52 endpoint := Endpoint{
paddy@56 53 ID: uuid.NewID(),
paddy@56 54 ClientID: client.ID,
paddy@56 55 URI: *uri,
paddy@56 56 Added: time.Now(),
paddy@56 57 }
paddy@56 58 err = testContext.SaveClient(client)
paddy@56 59 if err != nil {
paddy@56 60 t.Fatal("Can't store client:", err)
paddy@56 61 }
paddy@56 62 err = testContext.AddEndpoint(client.ID, endpoint)
paddy@56 63 if err != nil {
paddy@56 64 t.Fatal("Can't store endpoint:", err)
paddy@56 65 }
paddy@77 66 session := Session{
paddy@77 67 ID: "testsession",
paddy@77 68 Active: true,
paddy@77 69 }
paddy@77 70 err = testContext.CreateSession(session)
paddy@77 71 if err != nil {
paddy@77 72 t.Fatal("Can't store session:", err)
paddy@77 73 }
paddy@52 74 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@52 75 if err != nil {
paddy@52 76 t.Fatal("Can't build request:", err)
paddy@52 77 }
paddy@77 78 cookie := &http.Cookie{
paddy@77 79 Name: authCookieName,
paddy@77 80 Value: session.ID,
paddy@77 81 }
paddy@77 82 req.AddCookie(cookie)
paddy@58 83 for i := 0; i < 1<<3; i++ {
paddy@53 84 w := httptest.NewRecorder()
paddy@53 85 params := url.Values{}
paddy@53 86 // see OAuth 2.0 spec, section 4.1.1
paddy@53 87 params.Set("response_type", "code")
paddy@56 88 params.Set("client_id", client.ID.String())
paddy@53 89 if i&uriSet != 0 {
paddy@58 90 params.Set("redirect_uri", endpoint.URI.String())
paddy@53 91 }
paddy@53 92 if i&scopeSet != 0 {
paddy@53 93 params.Set("scope", "testscope")
paddy@53 94 }
paddy@53 95 if i&stateSet != 0 {
paddy@53 96 params.Set("state", "my super secure state string")
paddy@53 97 }
paddy@53 98 req.URL.RawQuery = params.Encode()
paddy@66 99 req.Method = "GET"
paddy@66 100 req.Body = nil
paddy@66 101 req.Header.Del("Content-Type")
paddy@53 102 GetGrantHandler(w, req, testContext)
paddy@53 103 if w.Code != http.StatusOK {
paddy@53 104 t.Errorf("Expected status code to be %d, got %d for %s", http.StatusOK, w.Code, req.URL.String())
paddy@53 105 }
paddy@53 106 if w.Body.String() != "Get auth grant" {
paddy@53 107 t.Errorf("Expected body to be `%s`, got `%s` for %s", "Get auth grant", w.Body.String(), req.URL.String())
paddy@53 108 }
paddy@66 109 req.Method = "POST"
paddy@66 110 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
paddy@66 111 w = httptest.NewRecorder()
paddy@66 112 data := url.Values{}
paddy@66 113 data.Set("grant", "approved")
paddy@66 114 body := bytes.NewBufferString(data.Encode())
paddy@66 115 req.Body = ioutil.NopCloser(body)
paddy@66 116 GetGrantHandler(w, req, testContext)
paddy@66 117 if w.Code != http.StatusFound {
paddy@66 118 t.Errorf("Expected status code to be %d, got %d for %s", http.StatusFound, w.Code, req.URL.String())
paddy@66 119 }
paddy@66 120 redirectedTo := w.Header().Get("Location")
paddy@66 121 red, err := url.Parse(redirectedTo)
paddy@66 122 if err != nil {
paddy@66 123 t.Fatalf(`Being redirected to a non-URL "%s" threw error "%s" for "%s"\n`, redirectedTo, err, req.URL.String())
paddy@66 124 }
paddy@66 125 t.Log("Redirected to", redirectedTo)
paddy@66 126 if red.Query().Get("code") == "" {
paddy@66 127 t.Fatalf(`Expected code param in redirect URL to be set, but it wasn't for %s`, req.URL.String())
paddy@66 128 }
paddy@67 129 if _, err := testContext.GetGrant(red.Query().Get("code")); err != nil {
paddy@67 130 t.Fatalf(`Unexpected error "%s: retrieving the grant "%s" supplied in the redirect URL for %s`, err, red.Query().Get("code"), req.URL.String())
paddy@66 131 }
paddy@66 132 err = testContext.DeleteGrant(red.Query().Get("code"))
paddy@66 133 if err != nil {
paddy@66 134 t.Log(`Unexpected error "%s" deleting grant "%s" for %s`, err, red.Query().Get("code"), req.URL.String())
paddy@66 135 }
paddy@66 136 stripParam("code", red)
paddy@66 137 if red.Query().Get("state") != params.Get("state") {
paddy@66 138 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())
paddy@66 139 }
paddy@66 140 stripParam("state", red)
paddy@66 141 if red.String() != endpoint.URI.String() {
paddy@66 142 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
paddy@66 143 }
paddy@52 144 }
paddy@52 145 }
paddy@56 146
paddy@62 147 func TestGetGrantCodeInvalidClient(t *testing.T) {
paddy@62 148 t.Parallel()
paddy@62 149 store := NewMemstore()
paddy@62 150 testContext := Context{
paddy@62 151 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
paddy@62 152 clients: store,
paddy@62 153 grants: store,
paddy@62 154 profiles: store,
paddy@62 155 tokens: store,
paddy@77 156 sessions: store,
paddy@62 157 }
paddy@62 158 client := Client{
paddy@62 159 ID: uuid.NewID(),
paddy@62 160 Secret: "super secret!",
paddy@62 161 OwnerID: uuid.NewID(),
paddy@62 162 Name: "My test client",
paddy@62 163 Type: "public",
paddy@62 164 }
paddy@62 165 err := testContext.SaveClient(client)
paddy@62 166 if err != nil {
paddy@62 167 t.Fatal("Can't store client:", err)
paddy@62 168 }
paddy@77 169 session := Session{
paddy@77 170 ID: "testsession",
paddy@77 171 Active: true,
paddy@77 172 }
paddy@77 173 err = testContext.CreateSession(session)
paddy@77 174 if err != nil {
paddy@77 175 t.Fatal("Can't store session:", err)
paddy@77 176 }
paddy@62 177 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@62 178 if err != nil {
paddy@62 179 t.Fatal("Can't build request:", err)
paddy@62 180 }
paddy@62 181 w := httptest.NewRecorder()
paddy@62 182 params := url.Values{}
paddy@62 183 params.Set("response_type", "code")
paddy@62 184 params.Set("redirect_uri", "https://test.secondbit.org/")
paddy@62 185 req.URL.RawQuery = params.Encode()
paddy@77 186 cookie := &http.Cookie{
paddy@77 187 Name: authCookieName,
paddy@77 188 Value: session.ID,
paddy@77 189 }
paddy@77 190 req.AddCookie(cookie)
paddy@62 191 GetGrantHandler(w, req, testContext)
paddy@62 192 if w.Code != http.StatusBadRequest {
paddy@62 193 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@62 194 }
paddy@62 195 if w.Body.String() != "Client ID must be specified in the request." {
paddy@62 196 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "Client ID must be specified in the request.", w.Body.String())
paddy@62 197 }
paddy@62 198 w = httptest.NewRecorder()
paddy@62 199 params.Set("client_id", "Not an ID")
paddy@62 200 req.URL.RawQuery = params.Encode()
paddy@62 201 GetGrantHandler(w, req, testContext)
paddy@62 202 if w.Code != http.StatusBadRequest {
paddy@62 203 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@62 204 }
paddy@62 205 if w.Body.String() != "client_id is not a valid Client ID." {
paddy@62 206 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "client_id is not a valid Client ID.", w.Body.String())
paddy@62 207 }
paddy@62 208 w = httptest.NewRecorder()
paddy@62 209 params.Set("client_id", uuid.NewID().String())
paddy@62 210 req.URL.RawQuery = params.Encode()
paddy@62 211 GetGrantHandler(w, req, testContext)
paddy@62 212 if w.Code != http.StatusBadRequest {
paddy@62 213 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@62 214 }
paddy@62 215 if w.Body.String() != "The specified Client couldn&rsquo;t be found." {
paddy@62 216 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The specified Client couldn&rsquo;t be found.", w.Body.String())
paddy@62 217 }
paddy@62 218 }
paddy@62 219
paddy@56 220 func TestGetGrantCodeInvalidURI(t *testing.T) {
paddy@56 221 t.Parallel()
paddy@56 222 store := NewMemstore()
paddy@56 223 testContext := Context{
paddy@56 224 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
paddy@56 225 clients: store,
paddy@56 226 grants: store,
paddy@56 227 profiles: store,
paddy@56 228 tokens: store,
paddy@77 229 sessions: store,
paddy@56 230 }
paddy@56 231 client := Client{
paddy@56 232 ID: uuid.NewID(),
paddy@56 233 Secret: "super secret!",
paddy@56 234 OwnerID: uuid.NewID(),
paddy@56 235 Name: "My test client",
paddy@56 236 Type: "public",
paddy@56 237 }
paddy@56 238 uri, err := url.Parse("https://test.secondbit.org/redirect")
paddy@56 239 if err != nil {
paddy@56 240 t.Fatal("Can't parse URL:", err)
paddy@56 241 }
paddy@56 242 err = testContext.SaveClient(client)
paddy@56 243 if err != nil {
paddy@56 244 t.Fatal("Can't store client:", err)
paddy@56 245 }
paddy@77 246 session := Session{
paddy@77 247 ID: "testsession",
paddy@77 248 Active: true,
paddy@77 249 }
paddy@77 250 err = testContext.CreateSession(session)
paddy@77 251 if err != nil {
paddy@77 252 t.Fatal("Can't store session:", err)
paddy@77 253 }
paddy@56 254 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@56 255 if err != nil {
paddy@56 256 t.Fatal("Can't build request:", err)
paddy@56 257 }
paddy@77 258 cookie := &http.Cookie{
paddy@77 259 Name: authCookieName,
paddy@77 260 Value: session.ID,
paddy@77 261 }
paddy@77 262 req.AddCookie(cookie)
paddy@56 263 w := httptest.NewRecorder()
paddy@56 264 params := url.Values{}
paddy@56 265 params.Set("response_type", "code")
paddy@56 266 params.Set("client_id", client.ID.String())
paddy@64 267 req.URL.RawQuery = params.Encode()
paddy@64 268 GetGrantHandler(w, req, testContext)
paddy@64 269 if w.Code != http.StatusBadRequest {
paddy@64 270 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@64 271 }
paddy@64 272 if w.Body.String() != "The redirect_uri specified is not valid." {
paddy@64 273 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
paddy@64 274 }
paddy@64 275 endpoint := Endpoint{
paddy@64 276 ID: uuid.NewID(),
paddy@64 277 ClientID: client.ID,
paddy@64 278 URI: *uri,
paddy@64 279 Added: time.Now(),
paddy@64 280 }
paddy@64 281 err = testContext.AddEndpoint(client.ID, endpoint)
paddy@64 282 if err != nil {
paddy@64 283 t.Fatal("Can't store endpoint:", err)
paddy@64 284 }
paddy@64 285 w = httptest.NewRecorder()
paddy@56 286 params.Set("redirect_uri", "https://test.secondbit.org/wrong")
paddy@56 287 req.URL.RawQuery = params.Encode()
paddy@56 288 GetGrantHandler(w, req, testContext)
paddy@56 289 if w.Code != http.StatusBadRequest {
paddy@56 290 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@56 291 }
paddy@56 292 if w.Body.String() != "The redirect_uri specified is not valid." {
paddy@56 293 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
paddy@56 294 }
paddy@64 295 endpoint2 := Endpoint{
paddy@64 296 ID: uuid.NewID(),
paddy@64 297 ClientID: client.ID,
paddy@64 298 URI: *uri,
paddy@64 299 Added: time.Now(),
paddy@64 300 }
paddy@64 301 err = testContext.AddEndpoint(client.ID, endpoint2)
paddy@60 302 if err != nil {
paddy@64 303 t.Fatal("Can't store endpoint:", err)
paddy@60 304 }
paddy@60 305 w = httptest.NewRecorder()
paddy@64 306 params.Set("redirect_uri", "")
paddy@64 307 req.URL.RawQuery = params.Encode()
paddy@64 308 GetGrantHandler(w, req, testContext)
paddy@64 309 if w.Code != http.StatusBadRequest {
paddy@64 310 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@64 311 }
paddy@64 312 if w.Body.String() != "The redirect_uri specified is not valid." {
paddy@64 313 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
paddy@64 314 }
paddy@64 315 w = httptest.NewRecorder()
paddy@64 316 params.Set("redirect_uri", "://not a URL")
paddy@60 317 req.URL.RawQuery = params.Encode()
paddy@60 318 GetGrantHandler(w, req, testContext)
paddy@60 319 if w.Code != http.StatusBadRequest {
paddy@60 320 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@60 321 }
paddy@60 322 if w.Body.String() != "The redirect_uri specified is not valid." {
paddy@60 323 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
paddy@60 324 }
paddy@56 325 }
paddy@65 326
paddy@65 327 func TestGetGrantCodeInvalidResponseType(t *testing.T) {
paddy@65 328 t.Parallel()
paddy@65 329 store := NewMemstore()
paddy@65 330 testContext := Context{
paddy@65 331 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
paddy@65 332 clients: store,
paddy@65 333 grants: store,
paddy@65 334 profiles: store,
paddy@65 335 tokens: store,
paddy@77 336 sessions: store,
paddy@65 337 }
paddy@65 338 client := Client{
paddy@65 339 ID: uuid.NewID(),
paddy@65 340 Secret: "super secret!",
paddy@65 341 OwnerID: uuid.NewID(),
paddy@65 342 Name: "My test client",
paddy@65 343 Logo: "https://secondbit.org/logo.png",
paddy@65 344 Website: "https://secondbit.org",
paddy@65 345 Type: "public",
paddy@65 346 }
paddy@65 347 uri, err := url.Parse("https://test.secondbit.org/redirect")
paddy@65 348 if err != nil {
paddy@65 349 t.Fatal("Can't parse URL:", err)
paddy@65 350 }
paddy@65 351 endpoint := Endpoint{
paddy@65 352 ID: uuid.NewID(),
paddy@65 353 ClientID: client.ID,
paddy@65 354 URI: *uri,
paddy@65 355 Added: time.Now(),
paddy@65 356 }
paddy@65 357 err = testContext.SaveClient(client)
paddy@65 358 if err != nil {
paddy@65 359 t.Fatal("Can't store client:", err)
paddy@65 360 }
paddy@65 361 err = testContext.AddEndpoint(client.ID, endpoint)
paddy@65 362 if err != nil {
paddy@65 363 t.Fatal("Can't store endpoint:", err)
paddy@65 364 }
paddy@77 365 session := Session{
paddy@77 366 ID: "testsession",
paddy@77 367 Active: true,
paddy@77 368 }
paddy@77 369 err = testContext.CreateSession(session)
paddy@77 370 if err != nil {
paddy@77 371 t.Fatal("Can't store session:", err)
paddy@77 372 }
paddy@65 373 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@65 374 if err != nil {
paddy@65 375 t.Fatal("Can't build request:", err)
paddy@65 376 }
paddy@77 377 cookie := &http.Cookie{
paddy@77 378 Name: authCookieName,
paddy@77 379 Value: session.ID,
paddy@77 380 }
paddy@77 381 req.AddCookie(cookie)
paddy@65 382 params := url.Values{}
paddy@65 383 params.Set("response_type", "totally not code")
paddy@65 384 params.Set("client_id", client.ID.String())
paddy@65 385 params.Set("redirect_uri", endpoint.URI.String())
paddy@65 386 params.Set("scope", "testscope")
paddy@65 387 params.Set("state", "my super secure state string")
paddy@65 388 req.URL.RawQuery = params.Encode()
paddy@65 389 w := httptest.NewRecorder()
paddy@65 390 GetGrantHandler(w, req, testContext)
paddy@65 391 if w.Code != http.StatusFound {
paddy@65 392 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
paddy@65 393 }
paddy@65 394 redirectedTo := w.Header().Get("Location")
paddy@65 395 red, err := url.Parse(redirectedTo)
paddy@65 396 if err != nil {
paddy@65 397 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
paddy@65 398 }
paddy@65 399 if red.Query().Get("error") != "invalid_request" {
paddy@65 400 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error"))
paddy@65 401 }
paddy@65 402 stripParam("error", red)
paddy@65 403 if red.Query().Get("state") != params.Get("state") {
paddy@65 404 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
paddy@65 405 }
paddy@65 406 stripParam("state", red)
paddy@65 407 if red.String() != endpoint.URI.String() {
paddy@65 408 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
paddy@65 409 }
paddy@65 410 stripParam("response_type", req.URL)
paddy@65 411 w = httptest.NewRecorder()
paddy@65 412 GetGrantHandler(w, req, testContext)
paddy@65 413 if w.Code != http.StatusFound {
paddy@65 414 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
paddy@65 415 }
paddy@65 416 redirectedTo = w.Header().Get("Location")
paddy@65 417 red, err = url.Parse(redirectedTo)
paddy@65 418 if err != nil {
paddy@65 419 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
paddy@65 420 }
paddy@65 421 if red.Query().Get("error") != "invalid_request" {
paddy@65 422 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error"))
paddy@65 423 }
paddy@65 424 stripParam("error", red)
paddy@65 425 if red.Query().Get("state") != params.Get("state") {
paddy@65 426 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
paddy@65 427 }
paddy@65 428 stripParam("state", red)
paddy@65 429 if red.String() != endpoint.URI.String() {
paddy@65 430 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
paddy@65 431 }
paddy@65 432 }
paddy@66 433
paddy@66 434 func TestGetGrantCodeDenied(t *testing.T) {
paddy@66 435 t.Parallel()
paddy@66 436 store := NewMemstore()
paddy@66 437 testContext := Context{
paddy@66 438 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
paddy@66 439 clients: store,
paddy@66 440 grants: store,
paddy@66 441 profiles: store,
paddy@66 442 tokens: store,
paddy@77 443 sessions: store,
paddy@66 444 }
paddy@66 445 client := Client{
paddy@66 446 ID: uuid.NewID(),
paddy@66 447 Secret: "super secret!",
paddy@66 448 OwnerID: uuid.NewID(),
paddy@66 449 Name: "My test client",
paddy@66 450 Logo: "https://secondbit.org/logo.png",
paddy@66 451 Website: "https://secondbit.org",
paddy@66 452 Type: "public",
paddy@66 453 }
paddy@66 454 uri, err := url.Parse("https://test.secondbit.org/redirect")
paddy@66 455 if err != nil {
paddy@66 456 t.Fatal("Can't parse URL:", err)
paddy@66 457 }
paddy@66 458 endpoint := Endpoint{
paddy@66 459 ID: uuid.NewID(),
paddy@66 460 ClientID: client.ID,
paddy@66 461 URI: *uri,
paddy@66 462 Added: time.Now(),
paddy@66 463 }
paddy@66 464 err = testContext.SaveClient(client)
paddy@66 465 if err != nil {
paddy@66 466 t.Fatal("Can't store client:", err)
paddy@66 467 }
paddy@66 468 err = testContext.AddEndpoint(client.ID, endpoint)
paddy@66 469 if err != nil {
paddy@66 470 t.Fatal("Can't store endpoint:", err)
paddy@66 471 }
paddy@77 472 session := Session{
paddy@77 473 ID: "testsession",
paddy@77 474 Active: true,
paddy@77 475 }
paddy@77 476 err = testContext.CreateSession(session)
paddy@77 477 if err != nil {
paddy@77 478 t.Fatal("Can't store session:", err)
paddy@77 479 }
paddy@66 480 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@66 481 if err != nil {
paddy@66 482 t.Fatal("Can't build request:", err)
paddy@66 483 }
paddy@77 484 cookie := &http.Cookie{
paddy@77 485 Name: authCookieName,
paddy@77 486 Value: session.ID,
paddy@77 487 }
paddy@77 488 req.AddCookie(cookie)
paddy@66 489 params := url.Values{}
paddy@66 490 params.Set("response_type", "code")
paddy@66 491 params.Set("client_id", client.ID.String())
paddy@66 492 params.Set("redirect_uri", endpoint.URI.String())
paddy@66 493 params.Set("scope", "testscope")
paddy@66 494 params.Set("state", "my super secure state string")
paddy@66 495 data := url.Values{}
paddy@66 496 data.Set("grant", "denied")
paddy@66 497 req.URL.RawQuery = params.Encode()
paddy@66 498 req.Body = ioutil.NopCloser(bytes.NewBufferString(data.Encode()))
paddy@66 499 req.Method = "POST"
paddy@66 500 w := httptest.NewRecorder()
paddy@66 501 GetGrantHandler(w, req, testContext)
paddy@66 502 if w.Code != http.StatusFound {
paddy@66 503 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
paddy@66 504 }
paddy@66 505 redirectedTo := w.Header().Get("Location")
paddy@66 506 red, err := url.Parse(redirectedTo)
paddy@66 507 if err != nil {
paddy@66 508 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
paddy@66 509 }
paddy@66 510 if red.Query().Get("error") != "access_denied" {
paddy@66 511 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "access_denied", red.Query().Get("error"))
paddy@66 512 }
paddy@66 513 stripParam("error", red)
paddy@66 514 if red.Query().Get("state") != params.Get("state") {
paddy@66 515 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
paddy@66 516 }
paddy@66 517 stripParam("state", red)
paddy@66 518 if red.String() != endpoint.URI.String() {
paddy@66 519 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
paddy@66 520 }
paddy@66 521 }
paddy@77 522
paddy@77 523 func TestGetBasicAuth(t *testing.T) {
paddy@77 524 tests := map[string]struct {
paddy@77 525 un string
paddy@77 526 pass string
paddy@77 527 err error
paddy@77 528 }{
paddy@77 529 "Basic dGVzdHVzZXI6cGFzc3dvcmQx": {"testuser", "password1", nil},
paddy@77 530 "": {"", "", ErrNoAuth},
paddy@77 531 "dGVzdHVzZXI6cGFzc3dvcmQx": {"", "", ErrInvalidAuthFormat},
paddy@77 532 "Basic _*&^##$@#$@&!!@": {"", "", ErrInvalidAuthFormat},
paddy@77 533 "Basic abcdefgh": {"", "", ErrInvalidAuthFormat},
paddy@77 534 "Basic dXNlcjo=": {"user", "", nil},
paddy@77 535 }
paddy@77 536
paddy@77 537 for header, test := range tests {
paddy@77 538 req, err := http.NewRequest("GET", "https://auth.secondbit.org", nil)
paddy@77 539 if err != nil {
paddy@77 540 t.Error("Unexpected error creating base request:", err)
paddy@77 541 }
paddy@77 542 req.Header.Set("Authorization", header)
paddy@77 543 un, pass, err := getBasicAuth(req)
paddy@77 544 if un != test.un {
paddy@77 545 t.Errorf(`Expected header "%s" to return username "%s", got "%s"`, header, test.un, un)
paddy@77 546 }
paddy@77 547 if pass != test.pass {
paddy@77 548 t.Errorf(`Expected header "%s" to return password "%s", got "%s"`, header, test.pass, pass)
paddy@77 549 }
paddy@77 550 if err != test.err {
paddy@77 551 t.Errorf(`Expected header "%s" to return error "%s", got "%s"`, header, test.err, err)
paddy@77 552 }
paddy@77 553 }
paddy@77 554 }
paddy@78 555
paddy@78 556 func TestCheckCookie(t *testing.T) {
paddy@78 557 t.Parallel()
paddy@78 558 req, err := http.NewRequest("GET", "https://auth.secondbit.org", nil)
paddy@78 559 if err != nil {
paddy@78 560 t.Error("Unexpected error creating base request:", err)
paddy@78 561 }
paddy@78 562 store := NewMemstore()
paddy@78 563 testContext := Context{
paddy@78 564 sessions: store,
paddy@78 565 }
paddy@78 566 session, err := checkCookie(req, testContext)
paddy@78 567 if err != ErrNoSession {
paddy@78 568 t.Errorf("Expected ErrNoSession, got %s", err)
paddy@78 569 }
paddy@78 570 session = Session{
paddy@78 571 ID: "testsession",
paddy@78 572 Active: true,
paddy@78 573 }
paddy@78 574 err = testContext.CreateSession(session)
paddy@78 575 if err != nil {
paddy@78 576 t.Error("Unexpected error persisting session:", err)
paddy@78 577 }
paddy@78 578 invalidSession := Session{
paddy@78 579 ID: "testsession2",
paddy@78 580 Active: false,
paddy@78 581 }
paddy@78 582 err = testContext.CreateSession(invalidSession)
paddy@78 583 if err != nil {
paddy@78 584 t.Error("Unexpected error persisting session:", err)
paddy@78 585 }
paddy@78 586 result, err := checkCookie(req, testContext)
paddy@78 587 if err != ErrNoSession {
paddy@78 588 t.Errorf("Expected ErrNoSession, got %s", err)
paddy@78 589 }
paddy@78 590 req.AddCookie(&http.Cookie{
paddy@78 591 Name: "wrongcookie",
paddy@78 592 Value: "wrong value",
paddy@78 593 })
paddy@78 594 result, err = checkCookie(req, testContext)
paddy@78 595 if err != ErrNoSession {
paddy@78 596 t.Error("Expected ErrNoSession, got", err)
paddy@78 597 }
paddy@78 598 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
paddy@78 599 if err != nil {
paddy@78 600 t.Error("Unexpected error creating base request:", err)
paddy@78 601 }
paddy@78 602 req.AddCookie(&http.Cookie{
paddy@78 603 Name: "Stillwrongcookie",
paddy@78 604 Value: session.ID,
paddy@78 605 })
paddy@78 606 result, err = checkCookie(req, testContext)
paddy@78 607 if err != ErrNoSession {
paddy@78 608 t.Error("Expected ErrNoSession, got", err)
paddy@78 609 }
paddy@78 610 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
paddy@78 611 if err != nil {
paddy@78 612 t.Error("Unexpected error creating base request:", err)
paddy@78 613 }
paddy@78 614 req.AddCookie(&http.Cookie{
paddy@78 615 Name: authCookieName,
paddy@78 616 Value: "wrong value",
paddy@78 617 })
paddy@78 618 result, err = checkCookie(req, testContext)
paddy@78 619 if err != ErrInvalidSession {
paddy@78 620 t.Error("Expected ErrInvalidSession, got", err)
paddy@78 621 }
paddy@78 622 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
paddy@78 623 if err != nil {
paddy@78 624 t.Error("Unexpected error creating base request:", err)
paddy@78 625 }
paddy@78 626 req.AddCookie(&http.Cookie{
paddy@78 627 Name: authCookieName,
paddy@78 628 Value: invalidSession.ID,
paddy@78 629 })
paddy@78 630 result, err = checkCookie(req, testContext)
paddy@78 631 if err != ErrInvalidSession {
paddy@78 632 t.Error("Expected ErrInvalidSession, got", err)
paddy@78 633 }
paddy@78 634 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil)
paddy@78 635 if err != nil {
paddy@78 636 t.Error("Unexpected error creating base request:", err)
paddy@78 637 }
paddy@78 638 req.AddCookie(&http.Cookie{
paddy@78 639 Name: authCookieName,
paddy@78 640 Value: session.ID,
paddy@78 641 })
paddy@78 642 result, err = checkCookie(req, testContext)
paddy@78 643 if err != nil {
paddy@78 644 t.Error("Unexpected error:", err)
paddy@78 645 }
paddy@78 646 success, field, expectation, outcome := compareSessions(session, result)
paddy@78 647 if !success {
paddy@78 648 t.Errorf(`Expected field %s to be %v, but got %v`, field, expectation, outcome)
paddy@78 649 }
paddy@78 650 }
paddy@78 651
paddy@78 652 func TestBuildLoginRedirect(t *testing.T) {
paddy@78 653 t.Parallel()
paddy@78 654 req, err := http.NewRequest("GET", "https://client.secondbit.org/my/awesome/path?has=query&params=to&screw=this&all=up", nil)
paddy@78 655 if err != nil {
paddy@78 656 t.Error("Unexpected error creating base request:", err)
paddy@78 657 }
paddy@78 658 result := buildLoginRedirect(req, Context{})
paddy@78 659 if result != "" {
paddy@78 660 t.Error("Expected empty string as the result, got", result)
paddy@78 661 }
paddy@78 662 uri, err := url.Parse("https://auth.secondbit.org/login?query=string&other=param")
paddy@78 663 if err != nil {
paddy@78 664 t.Error("Unexpected error parsing URL:", err)
paddy@78 665 }
paddy@78 666 c := Context{loginURI: uri}
paddy@78 667 result = buildLoginRedirect(req, c)
paddy@78 668 expectation := "https://auth.secondbit.org/login?from=https%3A%2F%2Fclient.secondbit.org%2Fmy%2Fawesome%2Fpath%3Fhas%3Dquery%26params%3Dto%26screw%3Dthis%26all%3Dup&other=param&query=string"
paddy@78 669 if result != expectation {
paddy@78 670 t.Errorf(`Expected result string to be "%s", was "%s"`, expectation, result)
paddy@78 671 }
paddy@78 672 }