|
9 | 9 | package rest |
10 | 10 |
|
11 | 11 | import ( |
| 12 | + "fmt" |
12 | 13 | "net/http" |
13 | 14 | "strconv" |
14 | 15 | "strings" |
@@ -286,6 +287,24 @@ func TestCORSUserNoAccess(t *testing.T) { |
286 | 287 | } |
287 | 288 | } |
288 | 289 |
|
| 290 | +// TestCORSResponseHeadersEmptyConfig ensures that an empty CORS config results in no CORS headers being set on the response. |
| 291 | +func TestCORSResponseHeadersEmptyConfig(t *testing.T) { |
| 292 | + rt := NewRestTester(t, nil) |
| 293 | + // RestTester initializes using defaultTestingCORSOrigin - override to empty for this test |
| 294 | + rt.ServerContext().Config.API.CORS = &auth.CORSConfig{} |
| 295 | + defer rt.Close() |
| 296 | + |
| 297 | + reqHeaders := map[string]string{ |
| 298 | + "Origin": "http://example.com", |
| 299 | + } |
| 300 | + response := rt.SendRequestWithHeaders(http.MethodGet, "/{{.db}}/", "", reqHeaders) |
| 301 | + RequireStatus(t, response, http.StatusUnauthorized) |
| 302 | + require.Contains(t, response.Body.String(), ErrLoginRequired.Message) |
| 303 | + assert.NotContains(t, response.Header(), "Access-Control-Allow-Origin") |
| 304 | + assert.NotContains(t, response.Header(), "Access-Control-Allow-Credentials") |
| 305 | + assert.NotContains(t, response.Header(), "Access-Control-Allow-Headers") |
| 306 | +} |
| 307 | + |
289 | 308 | func TestCORSOriginPerDatabase(t *testing.T) { |
290 | 309 | // Override the default (example.com) CORS configuration in the DbConfig for /db: |
291 | 310 | const perDBMaxAge = 1234 |
@@ -384,6 +403,74 @@ func TestCORSOriginPerDatabase(t *testing.T) { |
384 | 403 | } |
385 | 404 | } |
386 | 405 |
|
| 406 | +func TestCORSLoginOriginPerDatabase(t *testing.T) { |
| 407 | + // Override the default (example.com) CORS configuration in the DbConfig for /db: |
| 408 | + rt := NewRestTesterPersistentConfigNoDB(t) |
| 409 | + defer rt.Close() |
| 410 | + dbConfig := rt.NewDbConfig() |
| 411 | + dbConfig.CORS = &auth.CORSConfig{ |
| 412 | + Origin: []string{"http://couchbase.com", "http://staging.couchbase.com"}, |
| 413 | + LoginOrigin: []string{"http://couchbase.com"}, |
| 414 | + Headers: []string{}, |
| 415 | + } |
| 416 | + RequireStatus(t, rt.CreateDatabase("dbloginorigin", dbConfig), http.StatusCreated) |
| 417 | + |
| 418 | + const username = "alice" |
| 419 | + rt.CreateUser(username, nil) |
| 420 | + |
| 421 | + testCases := []struct { |
| 422 | + name string |
| 423 | + origin string |
| 424 | + responseCode int |
| 425 | + responseErrorBody string |
| 426 | + }{ |
| 427 | + { |
| 428 | + name: "CORS login origin allowed couchbase", |
| 429 | + origin: "http://couchbase.com", |
| 430 | + responseCode: http.StatusOK, |
| 431 | + }, |
| 432 | + { |
| 433 | + name: "CORS login origin not allowed staging", |
| 434 | + origin: "http://staging.couchbase.com", |
| 435 | + responseCode: http.StatusBadRequest, |
| 436 | + responseErrorBody: "No CORS", |
| 437 | + }, |
| 438 | + } |
| 439 | + for _, test := range testCases { |
| 440 | + rt.Run(test.name, func(t *testing.T) { |
| 441 | + reqHeaders := map[string]string{ |
| 442 | + "Origin": test.origin, |
| 443 | + "Authorization": GetBasicAuthHeader(t, username, RestTesterDefaultUserPassword), |
| 444 | + } |
| 445 | + resp := rt.SendRequestWithHeaders(http.MethodPost, "/{{.db}}/_session", "", reqHeaders) |
| 446 | + RequireStatus(t, resp, test.responseCode) |
| 447 | + if test.responseErrorBody != "" { |
| 448 | + require.Contains(t, resp.Body.String(), test.responseErrorBody) |
| 449 | + // the access control headers are returned based on Origin and not LoginOrigin which could be considered a bug |
| 450 | + require.Equal(t, test.origin, resp.Header().Get(accessControlAllowOrigin)) |
| 451 | + } else { |
| 452 | + require.Equal(t, test.origin, resp.Header().Get(accessControlAllowOrigin)) |
| 453 | + } |
| 454 | + if test.responseCode == http.StatusOK { |
| 455 | + cookie, err := http.ParseSetCookie(resp.Header().Get("Set-Cookie")) |
| 456 | + require.NoError(t, err) |
| 457 | + require.NotEmpty(t, cookie.Path) |
| 458 | + reqHeaders["Cookie"] = fmt.Sprintf("%s=%s", cookie.Name, cookie.Value) |
| 459 | + } |
| 460 | + resp = rt.SendRequestWithHeaders(http.MethodDelete, "/{{.db}}/_session", "", reqHeaders) |
| 461 | + RequireStatus(t, resp, test.responseCode) |
| 462 | + if test.responseErrorBody != "" { |
| 463 | + require.Contains(t, resp.Body.String(), test.responseErrorBody) |
| 464 | + // the access control headers are returned based on Origin and not LoginOrigin which could be considered a bug |
| 465 | + require.Equal(t, test.origin, resp.Header().Get(accessControlAllowOrigin)) |
| 466 | + } else { |
| 467 | + require.Equal(t, test.origin, resp.Header().Get(accessControlAllowOrigin)) |
| 468 | + } |
| 469 | + |
| 470 | + }) |
| 471 | + } |
| 472 | +} |
| 473 | + |
387 | 474 | func TestCORSValidation(t *testing.T) { |
388 | 475 | rt := NewRestTester(t, &RestTesterConfig{ |
389 | 476 | PersistentConfig: true, |
|
0 commit comments