func TestEndpointAuthorizeBasic(t *testing.T) { m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, }) username := "******" password := "******" authenicate := fmt.Sprintf("Basic realm=localhost") validCheck := func(a string) bool { return a == fmt.Sprintf("Basic %s", basicAuth(username, password)) } e, c := testServerWithAuth(m, authenicate, validCheck) defer c() creds := &testCredentialStore{ username: username, password: password, } challengeManager := NewSimpleChallengeManager() _, err := ping(challengeManager, e+"/v2/", "") if err != nil { t.Fatal(err) } transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager, NewBasicHandler(creds))) client := &http.Client{Transport: transport1} req, _ := http.NewRequest("GET", e+"/v2/hello", nil) resp, err := client.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusAccepted { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted) } }
func TestEndpointAuthorizeToken(t *testing.T) { service := "localhost.localdomain" repo1 := "some/registry" repo2 := "other/registry" scope1 := fmt.Sprintf("repository:%s:pull,push", repo1) scope2 := fmt.Sprintf("repository:%s:pull,push", repo2) tokenScope1 := TokenScope{ Resource: "repository", Scope: repo1, Actions: []string{"pull", "push"}, } tokenScope2 := TokenScope{ Resource: "repository", Scope: repo2, Actions: []string{"pull", "push"}, } tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: fmt.Sprintf("/token?scope=%s&service=%s", url.QueryEscape(scope1), service), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(`{"token":"statictoken"}`), }, }, { Request: testutil.Request{ Method: "GET", Route: fmt.Sprintf("/token?scope=%s&service=%s", url.QueryEscape(scope2), service), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(`{"token":"badtoken"}`), }, }, }) te, tc := testServer(tokenMap) defer tc() m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, }) authenicate := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service) validCheck := func(a string) bool { return a == "Bearer statictoken" } e, c := testServerWithAuth(m, authenicate, validCheck) defer c() transport1 := NewTransport(nil, NewAuthorizer(nil, NewTokenHandler(nil, nil, tokenScope1))) client := &http.Client{Transport: transport1} req, _ := http.NewRequest("GET", e+"/v2/hello", nil) resp, err := client.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusAccepted { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted) } badCheck := func(a string) bool { return a == "Bearer statictoken" } e2, c2 := testServerWithAuth(m, authenicate, badCheck) defer c2() transport2 := NewTransport(nil, NewAuthorizer(nil, NewTokenHandler(nil, nil, tokenScope2))) client2 := &http.Client{Transport: transport2} req, _ = http.NewRequest("GET", e2+"/v2/hello", nil) resp, err = client2.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusUnauthorized { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusUnauthorized) } }
func TestEndpointAuthorizeTokenBasic(t *testing.T) { service := "localhost.localdomain" repo := "some/fun/registry" scope := fmt.Sprintf("repository:%s:pull,push", repo) username := "******" password := "******" tokenScope := TokenScope{ Resource: "repository", Scope: repo, Actions: []string{"pull", "push"}, } tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: fmt.Sprintf("/token?account=%s&scope=%s&service=%s", username, url.QueryEscape(scope), service), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(`{"token":"statictoken"}`), }, }, }) authenicate1 := fmt.Sprintf("Basic realm=localhost") basicCheck := func(a string) bool { return a == fmt.Sprintf("Basic %s", basicAuth(username, password)) } te, tc := testServerWithAuth(tokenMap, authenicate1, basicCheck) defer tc() m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, }) authenicate2 := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service) bearerCheck := func(a string) bool { return a == "Bearer statictoken" } e, c := testServerWithAuth(m, authenicate2, bearerCheck) defer c() creds := &testCredentialStore{ username: username, password: password, } transport1 := NewTransport(nil, NewAuthorizer(nil, NewTokenHandler(nil, creds, tokenScope), NewBasicHandler(creds))) client := &http.Client{Transport: transport1} req, _ := http.NewRequest("GET", e+"/v2/hello", nil) resp, err := client.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusAccepted { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted) } }
func TestEndpointAuthorizeTokenBasicWithExpiresInAndIssuedAt(t *testing.T) { service := "localhost.localdomain" repo := "some/fun/registry" scope := fmt.Sprintf("repository:%s:pull,push", repo) username := "******" password := "******" // This test sets things up such that the token was issued one increment // earlier than its sibling in TestEndpointAuthorizeTokenBasicWithExpiresIn. // This will mean that the token expires after 3 increments instead of 4. clock := &fakeClock{current: time.Now()} timeIncrement := 1000 * time.Second firstIssuedAt := clock.Now() clock.current = clock.current.Add(timeIncrement) secondIssuedAt := clock.current.Add(2 * timeIncrement) tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: fmt.Sprintf("/token?account=%s&scope=%s&service=%s", username, url.QueryEscape(scope), service), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(`{"token":"statictoken", "issued_at": "` + firstIssuedAt.Format(time.RFC3339Nano) + `", "expires_in": 3001}`), }, }, { Request: testutil.Request{ Method: "GET", Route: fmt.Sprintf("/token?account=%s&scope=%s&service=%s", username, url.QueryEscape(scope), service), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(`{"access_token":"statictoken", "issued_at": "` + secondIssuedAt.Format(time.RFC3339Nano) + `", "expires_in": 3001}`), }, }, }) authenicate1 := fmt.Sprintf("Basic realm=localhost") tokenExchanges := 0 basicCheck := func(a string) bool { tokenExchanges = tokenExchanges + 1 return a == fmt.Sprintf("Basic %s", basicAuth(username, password)) } te, tc := testServerWithAuth(tokenMap, authenicate1, basicCheck) defer tc() m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, }) authenicate2 := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service) bearerCheck := func(a string) bool { return a == "Bearer statictoken" } e, c := testServerWithAuth(m, authenicate2, bearerCheck) defer c() creds := &testCredentialStore{ username: username, password: password, } challengeManager := NewSimpleChallengeManager() _, err := ping(challengeManager, e+"/v2/", "") if err != nil { t.Fatal(err) } options := TokenHandlerOptions{ Transport: nil, Credentials: creds, Scopes: []Scope{ RepositoryScope{ Repository: repo, Actions: []string{"pull", "push"}, }, }, } tHandler := NewTokenHandlerWithOptions(options) tHandler.(*tokenHandler).clock = clock transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager, tHandler, NewBasicHandler(creds))) client := &http.Client{Transport: transport1} // First call should result in a token exchange // Subsequent calls should recycle the token from the first request, until the expiration has lapsed. // We shaved one increment off of the equivalent logic in TestEndpointAuthorizeTokenBasicWithExpiresIn // so this loop should have one fewer iteration. for i := 0; i < 3; i++ { req, _ := http.NewRequest("GET", e+"/v2/hello", nil) resp, err := client.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusAccepted { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted) } if tokenExchanges != 1 { t.Fatalf("Unexpected number of token exchanges, want: 1, got %d (iteration: %d)", tokenExchanges, i) } clock.current = clock.current.Add(timeIncrement) } // After we've exceeded the expiration, we should see a second token exchange. req, _ := http.NewRequest("GET", e+"/v2/hello", nil) resp, err := client.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusAccepted { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted) } if tokenExchanges != 2 { t.Fatalf("Unexpected number of token exchanges, want: 2, got %d", tokenExchanges) } }
func TestEndpointAuthorizeV2RefreshToken(t *testing.T) { service := "localhost.localdomain" scope1 := "registry:catalog:search" refreshToken1 := "0123456790abcdef" tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "POST", Route: "/token", Body: []byte(fmt.Sprintf("client_id=registry-client&grant_type=refresh_token&refresh_token=%s&scope=%s&service=%s", refreshToken1, url.QueryEscape(scope1), service)), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(fmt.Sprintf(`{"access_token":"statictoken","refresh_token":"%s"}`, refreshToken1)), }, }, }) te, tc := testServer(tokenMap) defer tc() m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: "/v1/search", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, }) authenicate := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service) validCheck := func(a string) bool { return a == "Bearer statictoken" } e, c := testServerWithAuth(m, authenicate, validCheck) defer c() challengeManager1 := NewSimpleChallengeManager() versions, err := ping(challengeManager1, e+"/v2/", "x-api-version") if err != nil { t.Fatal(err) } if len(versions) != 1 { t.Fatalf("Unexpected version count: %d, expected 1", len(versions)) } if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check { t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check) } tho := TokenHandlerOptions{ Credentials: &testCredentialStore{ refreshTokens: map[string]string{ service: refreshToken1, }, }, Scopes: []Scope{ RegistryScope{ Name: "catalog", Actions: []string{"search"}, }, }, } transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager1, NewTokenHandlerWithOptions(tho))) client := &http.Client{Transport: transport1} req, _ := http.NewRequest("GET", e+"/v1/search", nil) resp, err := client.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusAccepted { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted) } }
func TestEndpointAuthorizeRefreshToken(t *testing.T) { service := "localhost.localdomain" repo1 := "some/registry" repo2 := "other/registry" scope1 := fmt.Sprintf("repository:%s:pull,push", repo1) scope2 := fmt.Sprintf("repository:%s:pull,push", repo2) refreshToken1 := "0123456790abcdef" refreshToken2 := "0123456790fedcba" tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "POST", Route: "/token", Body: []byte(fmt.Sprintf("client_id=registry-client&grant_type=refresh_token&refresh_token=%s&scope=%s&service=%s", refreshToken1, url.QueryEscape(scope1), service)), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(fmt.Sprintf(`{"access_token":"statictoken","refresh_token":"%s"}`, refreshToken1)), }, }, { // In the future this test may fail and require using basic auth to get a different refresh token Request: testutil.Request{ Method: "POST", Route: "/token", Body: []byte(fmt.Sprintf("client_id=registry-client&grant_type=refresh_token&refresh_token=%s&scope=%s&service=%s", refreshToken1, url.QueryEscape(scope2), service)), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(fmt.Sprintf(`{"access_token":"statictoken","refresh_token":"%s"}`, refreshToken2)), }, }, { Request: testutil.Request{ Method: "POST", Route: "/token", Body: []byte(fmt.Sprintf("client_id=registry-client&grant_type=refresh_token&refresh_token=%s&scope=%s&service=%s", refreshToken2, url.QueryEscape(scope2), service)), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(`{"access_token":"badtoken","refresh_token":"%s"}`), }, }, }) te, tc := testServer(tokenMap) defer tc() m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, }) authenicate := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service) validCheck := func(a string) bool { return a == "Bearer statictoken" } e, c := testServerWithAuth(m, authenicate, validCheck) defer c() challengeManager1 := NewSimpleChallengeManager() versions, err := ping(challengeManager1, e+"/v2/", "x-api-version") if err != nil { t.Fatal(err) } if len(versions) != 1 { t.Fatalf("Unexpected version count: %d, expected 1", len(versions)) } if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check { t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check) } creds := &testCredentialStore{ refreshTokens: map[string]string{ service: refreshToken1, }, } transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager1, NewTokenHandler(nil, creds, repo1, "pull", "push"))) client := &http.Client{Transport: transport1} req, _ := http.NewRequest("GET", e+"/v2/hello", nil) resp, err := client.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusAccepted { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted) } // Try with refresh token setting e2, c2 := testServerWithAuth(m, authenicate, validCheck) defer c2() challengeManager2 := NewSimpleChallengeManager() versions, err = ping(challengeManager2, e2+"/v2/", "x-api-version") if err != nil { t.Fatal(err) } if len(versions) != 1 { t.Fatalf("Unexpected version count: %d, expected 1", len(versions)) } if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check { t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check) } transport2 := transport.NewTransport(nil, NewAuthorizer(challengeManager2, NewTokenHandler(nil, creds, repo2, "pull", "push"))) client2 := &http.Client{Transport: transport2} req, _ = http.NewRequest("GET", e2+"/v2/hello", nil) resp, err = client2.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusAccepted { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusUnauthorized) } if creds.refreshTokens[service] != refreshToken2 { t.Fatalf("Refresh token not set after change") } // Try with bad token e3, c3 := testServerWithAuth(m, authenicate, validCheck) defer c3() challengeManager3 := NewSimpleChallengeManager() versions, err = ping(challengeManager3, e3+"/v2/", "x-api-version") if err != nil { t.Fatal(err) } if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check { t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check) } transport3 := transport.NewTransport(nil, NewAuthorizer(challengeManager3, NewTokenHandler(nil, creds, repo2, "pull", "push"))) client3 := &http.Client{Transport: transport3} req, _ = http.NewRequest("GET", e3+"/v2/hello", nil) resp, err = client3.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusUnauthorized { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusUnauthorized) } }
func TestEndpointAuthorizeToken(t *testing.T) { service := "localhost.localdomain" repo1 := "some/registry" repo2 := "other/registry" scope1 := fmt.Sprintf("repository:%s:pull,push", repo1) scope2 := fmt.Sprintf("repository:%s:pull,push", repo2) tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: fmt.Sprintf("/token?scope=%s&service=%s", url.QueryEscape(scope1), service), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(`{"token":"statictoken"}`), }, }, { Request: testutil.Request{ Method: "GET", Route: fmt.Sprintf("/token?scope=%s&service=%s", url.QueryEscape(scope2), service), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(`{"token":"badtoken"}`), }, }, }) te, tc := testServer(tokenMap) defer tc() m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, }) authenicate := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service) validCheck := func(a string) bool { return a == "Bearer statictoken" } e, c := testServerWithAuth(m, authenicate, validCheck) defer c() challengeManager1 := NewSimpleChallengeManager() versions, err := ping(challengeManager1, e+"/v2/", "x-api-version") if err != nil { t.Fatal(err) } if len(versions) != 1 { t.Fatalf("Unexpected version count: %d, expected 1", len(versions)) } if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check { t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check) } transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager1, NewTokenHandler(nil, nil, repo1, "pull", "push"))) client := &http.Client{Transport: transport1} req, _ := http.NewRequest("GET", e+"/v2/hello", nil) resp, err := client.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusAccepted { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted) } e2, c2 := testServerWithAuth(m, authenicate, validCheck) defer c2() challengeManager2 := NewSimpleChallengeManager() versions, err = ping(challengeManager2, e2+"/v2/", "x-multi-api-version") if err != nil { t.Fatal(err) } if len(versions) != 3 { t.Fatalf("Unexpected version count: %d, expected 3", len(versions)) } if check := (APIVersion{Type: "registry", Version: "2.0"}); versions[0] != check { t.Fatalf("Unexpected api version: %#v, expected %#v", versions[0], check) } if check := (APIVersion{Type: "registry", Version: "2.1"}); versions[1] != check { t.Fatalf("Unexpected api version: %#v, expected %#v", versions[1], check) } if check := (APIVersion{Type: "trust", Version: "1.0"}); versions[2] != check { t.Fatalf("Unexpected api version: %#v, expected %#v", versions[2], check) } transport2 := transport.NewTransport(nil, NewAuthorizer(challengeManager2, NewTokenHandler(nil, nil, repo2, "pull", "push"))) client2 := &http.Client{Transport: transport2} req, _ = http.NewRequest("GET", e2+"/v2/hello", nil) resp, err = client2.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusUnauthorized { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusUnauthorized) } }
func TestUploadReadFrom(t *testing.T) { _, b := newRandomBlob(64) repo := "test/upload/readfrom" locationPath := fmt.Sprintf("/v2/%s/uploads/testid", repo) m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: "/v2/", }, Response: testutil.Response{ StatusCode: http.StatusOK, Headers: http.Header(map[string][]string{ "Docker-Distribution-API-Version": {"registry/2.0"}, }), }, }, // Test Valid case { Request: testutil.Request{ Method: "PATCH", Route: locationPath, Body: b, }, Response: testutil.Response{ StatusCode: http.StatusAccepted, Headers: http.Header(map[string][]string{ "Docker-Upload-UUID": {"46603072-7a1b-4b41-98f9-fd8a7da89f9b"}, "Location": {locationPath}, "Range": {"0-63"}, }), }, }, // Test invalid range { Request: testutil.Request{ Method: "PATCH", Route: locationPath, Body: b, }, Response: testutil.Response{ StatusCode: http.StatusAccepted, Headers: http.Header(map[string][]string{ "Docker-Upload-UUID": {"46603072-7a1b-4b41-98f9-fd8a7da89f9b"}, "Location": {locationPath}, "Range": {""}, }), }, }, // Test 404 { Request: testutil.Request{ Method: "PATCH", Route: locationPath, Body: b, }, Response: testutil.Response{ StatusCode: http.StatusNotFound, }, }, // Test 400 valid json { Request: testutil.Request{ Method: "PATCH", Route: locationPath, Body: b, }, Response: testutil.Response{ StatusCode: http.StatusBadRequest, Body: []byte(` { "errors": [ { "code": "BLOB_UPLOAD_INVALID", "message": "blob upload invalid", "detail": "more detail" } ] } `), }, }, // Test 400 invalid json { Request: testutil.Request{ Method: "PATCH", Route: locationPath, Body: b, }, Response: testutil.Response{ StatusCode: http.StatusBadRequest, Body: []byte("something bad happened"), }, }, // Test 500 { Request: testutil.Request{ Method: "PATCH", Route: locationPath, Body: b, }, Response: testutil.Response{ StatusCode: http.StatusInternalServerError, }, }, }) e, c := testServer(m) defer c() blobUpload := &httpBlobUpload{ client: &http.Client{}, } // Valid case blobUpload.location = e + locationPath n, err := blobUpload.ReadFrom(bytes.NewReader(b)) if err != nil { t.Fatalf("Error calling ReadFrom: %s", err) } if n != 64 { t.Fatalf("Wrong length returned from ReadFrom: %d, expected 64", n) } // Bad range blobUpload.location = e + locationPath _, err = blobUpload.ReadFrom(bytes.NewReader(b)) if err == nil { t.Fatalf("Expected error when bad range received") } // 404 blobUpload.location = e + locationPath _, err = blobUpload.ReadFrom(bytes.NewReader(b)) if err == nil { t.Fatalf("Expected error when not found") } if err != distribution.ErrBlobUploadUnknown { t.Fatalf("Wrong error thrown: %s, expected %s", err, distribution.ErrBlobUploadUnknown) } // 400 valid json blobUpload.location = e + locationPath _, err = blobUpload.ReadFrom(bytes.NewReader(b)) if err == nil { t.Fatalf("Expected error when not found") } if uploadErr, ok := err.(errcode.Errors); !ok { t.Fatalf("Wrong error type %T: %s", err, err) } else if len(uploadErr) != 1 { t.Fatalf("Unexpected number of errors: %d, expected 1", len(uploadErr)) } else { v2Err, ok := uploadErr[0].(errcode.Error) if !ok { t.Fatalf("Not an 'Error' type: %#v", uploadErr[0]) } if v2Err.Code != v2.ErrorCodeBlobUploadInvalid { t.Fatalf("Unexpected error code: %s, expected %d", v2Err.Code.String(), v2.ErrorCodeBlobUploadInvalid) } if expected := "blob upload invalid"; v2Err.Message != expected { t.Fatalf("Unexpected error message: %q, expected %q", v2Err.Message, expected) } if expected := "more detail"; v2Err.Detail.(string) != expected { t.Fatalf("Unexpected error message: %q, expected %q", v2Err.Detail.(string), expected) } } // 400 invalid json blobUpload.location = e + locationPath _, err = blobUpload.ReadFrom(bytes.NewReader(b)) if err == nil { t.Fatalf("Expected error when not found") } if uploadErr, ok := err.(*UnexpectedHTTPResponseError); !ok { t.Fatalf("Wrong error type %T: %s", err, err) } else { respStr := string(uploadErr.Response) if expected := "something bad happened"; respStr != expected { t.Fatalf("Unexpected response string: %s, expected: %s", respStr, expected) } } // 500 blobUpload.location = e + locationPath _, err = blobUpload.ReadFrom(bytes.NewReader(b)) if err == nil { t.Fatalf("Expected error when not found") } if uploadErr, ok := err.(*UnexpectedHTTPStatusError); !ok { t.Fatalf("Wrong error type %T: %s", err, err) } else if expected := "500 " + http.StatusText(http.StatusInternalServerError); uploadErr.Status != expected { t.Fatalf("Unexpected response status: %s, expected %s", uploadErr.Status, expected) } }
func TestEndpointAuthorizeTokenBasicWithExpiresIn(t *testing.T) { service := "localhost.localdomain" repo := "some/fun/registry" scope := fmt.Sprintf("repository:%s:pull,push", repo) username := "******" password := "******" tokenMap := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: fmt.Sprintf("/token?account=%s&scope=%s&service=%s", username, url.QueryEscape(scope), service), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(`{"token":"statictoken", "expires_in": 3001}`), }, }, { Request: testutil.Request{ Method: "GET", Route: fmt.Sprintf("/token?account=%s&scope=%s&service=%s", username, url.QueryEscape(scope), service), }, Response: testutil.Response{ StatusCode: http.StatusOK, Body: []byte(`{"access_token":"statictoken", "expires_in": 3001}`), }, }, }) authenicate1 := fmt.Sprintf("Basic realm=localhost") tokenExchanges := 0 basicCheck := func(a string) bool { tokenExchanges = tokenExchanges + 1 return a == fmt.Sprintf("Basic %s", basicAuth(username, password)) } te, tc := testServerWithAuth(tokenMap, authenicate1, basicCheck) defer tc() m := testutil.RequestResponseMap([]testutil.RequestResponseMapping{ { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, { Request: testutil.Request{ Method: "GET", Route: "/v2/hello", }, Response: testutil.Response{ StatusCode: http.StatusAccepted, }, }, }) authenicate2 := fmt.Sprintf("Bearer realm=%q,service=%q", te+"/token", service) bearerCheck := func(a string) bool { return a == "Bearer statictoken" } e, c := testServerWithAuth(m, authenicate2, bearerCheck) defer c() creds := &testCredentialStore{ username: username, password: password, } challengeManager := NewSimpleChallengeManager() _, err := ping(challengeManager, e+"/v2/", "") if err != nil { t.Fatal(err) } clock := &fakeClock{current: time.Now()} transport1 := transport.NewTransport(nil, NewAuthorizer(challengeManager, newTokenHandler(nil, creds, clock, repo, "pull", "push"), NewBasicHandler(creds))) client := &http.Client{Transport: transport1} // First call should result in a token exchange // Subsequent calls should recycle the token from the first request, until the expiration has lapsed. timeIncrement := 1000 * time.Second for i := 0; i < 4; i++ { req, _ := http.NewRequest("GET", e+"/v2/hello", nil) resp, err := client.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusAccepted { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted) } if tokenExchanges != 1 { t.Fatalf("Unexpected number of token exchanges, want: 1, got %d (iteration: %d)", tokenExchanges, i) } clock.current = clock.current.Add(timeIncrement) } // After we've exceeded the expiration, we should see a second token exchange. req, _ := http.NewRequest("GET", e+"/v2/hello", nil) resp, err := client.Do(req) if err != nil { t.Fatalf("Error sending get request: %s", err) } if resp.StatusCode != http.StatusAccepted { t.Fatalf("Unexpected status code: %d, expected %d", resp.StatusCode, http.StatusAccepted) } if tokenExchanges != 2 { t.Fatalf("Unexpected number of token exchanges, want: 2, got %d", tokenExchanges) } }