github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/distribution/registry_unit_test.go (about) 1 package distribution // import "github.com/docker/docker/distribution" 2 3 import ( 4 "context" 5 "net/http" 6 "net/http/httptest" 7 "net/url" 8 "strings" 9 "testing" 10 11 "github.com/containerd/log" 12 "github.com/distribution/reference" 13 "github.com/docker/docker/api/types/registry" 14 registrypkg "github.com/docker/docker/registry" 15 ) 16 17 const secretRegistryToken = "mysecrettoken" 18 19 type tokenPassThruHandler struct { 20 reached bool 21 gotToken bool 22 shouldSend401 func(url string) bool 23 } 24 25 func (h *tokenPassThruHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 26 h.reached = true 27 if strings.Contains(r.Header.Get("Authorization"), secretRegistryToken) { 28 log.G(context.TODO()).Debug("Detected registry token in auth header") 29 h.gotToken = true 30 } 31 if h.shouldSend401 == nil || h.shouldSend401(r.RequestURI) { 32 w.Header().Set("WWW-Authenticate", `Bearer realm="foorealm"`) 33 w.WriteHeader(401) 34 } 35 } 36 37 func testTokenPassThru(t *testing.T, ts *httptest.Server) { 38 uri, err := url.Parse(ts.URL) 39 if err != nil { 40 t.Fatalf("could not parse url from test server: %v", err) 41 } 42 43 endpoint := registrypkg.APIEndpoint{ 44 Mirror: false, 45 URL: uri, 46 Official: false, 47 TrimHostname: false, 48 TLSConfig: nil, 49 } 50 n, _ := reference.ParseNormalizedNamed("testremotename") 51 repoInfo := ®istrypkg.RepositoryInfo{ 52 Name: n, 53 Index: ®istry.IndexInfo{ 54 Name: "testrepo", 55 Mirrors: nil, 56 Secure: false, 57 Official: false, 58 }, 59 Official: false, 60 } 61 imagePullConfig := &ImagePullConfig{ 62 Config: Config{ 63 MetaHeaders: http.Header{}, 64 AuthConfig: ®istry.AuthConfig{ 65 RegistryToken: secretRegistryToken, 66 }, 67 }, 68 } 69 p := newPuller(endpoint, repoInfo, imagePullConfig, nil) 70 ctx := context.Background() 71 p.repo, err = newRepository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") 72 if err != nil { 73 t.Fatal(err) 74 } 75 76 log.G(ctx).Debug("About to pull") 77 // We expect it to fail, since we haven't mock'd the full registry exchange in our handler above 78 tag, _ := reference.WithTag(n, "tag_goes_here") 79 _ = p.pullRepository(ctx, tag) 80 } 81 82 func TestTokenPassThru(t *testing.T) { 83 handler := &tokenPassThruHandler{shouldSend401: func(url string) bool { return url == "/v2/" }} 84 ts := httptest.NewServer(handler) 85 defer ts.Close() 86 87 testTokenPassThru(t, ts) 88 89 if !handler.reached { 90 t.Fatal("Handler not reached") 91 } 92 if !handler.gotToken { 93 t.Fatal("Failed to receive registry token") 94 } 95 } 96 97 func TestTokenPassThruDifferentHost(t *testing.T) { 98 handler := new(tokenPassThruHandler) 99 ts := httptest.NewServer(handler) 100 defer ts.Close() 101 102 tsredirect := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 103 if r.RequestURI == "/v2/" { 104 w.Header().Set("WWW-Authenticate", `Bearer realm="foorealm"`) 105 w.WriteHeader(401) 106 return 107 } 108 http.Redirect(w, r, ts.URL+r.URL.Path, http.StatusMovedPermanently) 109 })) 110 defer tsredirect.Close() 111 112 testTokenPassThru(t, tsredirect) 113 114 if !handler.reached { 115 t.Fatal("Handler not reached") 116 } 117 if handler.gotToken { 118 t.Fatal("Redirect should not forward Authorization header to another host") 119 } 120 }