github.com/opcr-io/oras-go/v2@v2.0.0-20231122155130-eb4260d8a0ae/registry/remote/auth/client_test.go (about)

     1  /*
     2  Copyright The ORAS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package auth
    17  
    18  import (
    19  	"context"
    20  	"encoding/base64"
    21  	"errors"
    22  	"fmt"
    23  	"net/http"
    24  	"net/http/httptest"
    25  	"net/url"
    26  	"reflect"
    27  	"strings"
    28  	"sync/atomic"
    29  	"testing"
    30  
    31  	"github.com/opcr-io/oras-go/v2/registry/remote/errcode"
    32  )
    33  
    34  func TestClient_SetUserAgent(t *testing.T) {
    35  	wantUserAgent := "test agent"
    36  	var requestCount, wantRequestCount int64
    37  	var successCount, wantSuccessCount int64
    38  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    39  		atomic.AddInt64(&requestCount, 1)
    40  		if r.Method != http.MethodGet || r.URL.Path != "/" {
    41  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
    42  			w.WriteHeader(http.StatusNotFound)
    43  			return
    44  		}
    45  		if userAgent := r.UserAgent(); userAgent != wantUserAgent {
    46  			t.Errorf("unexpected User-Agent: %v, want %v", userAgent, wantUserAgent)
    47  			return
    48  		}
    49  		atomic.AddInt64(&successCount, 1)
    50  	}))
    51  	defer ts.Close()
    52  
    53  	var client Client
    54  	client.SetUserAgent(wantUserAgent)
    55  
    56  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
    57  	if err != nil {
    58  		t.Fatalf("failed to create test request: %v", err)
    59  	}
    60  	resp, err := client.Do(req)
    61  	if err != nil {
    62  		t.Fatalf("Client.Do() error = %v", err)
    63  	}
    64  	if resp.StatusCode != http.StatusOK {
    65  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
    66  	}
    67  	if wantRequestCount++; requestCount != wantRequestCount {
    68  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
    69  	}
    70  	if wantSuccessCount++; successCount != wantSuccessCount {
    71  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
    72  	}
    73  }
    74  
    75  func TestClient_Do_Basic_Auth(t *testing.T) {
    76  	username := "test_user"
    77  	password := "test_password"
    78  	var requestCount, wantRequestCount int64
    79  	var successCount, wantSuccessCount int64
    80  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    81  		atomic.AddInt64(&requestCount, 1)
    82  		if r.Method != http.MethodGet || r.URL.Path != "/" {
    83  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
    84  			w.WriteHeader(http.StatusNotFound)
    85  			return
    86  		}
    87  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
    88  		if auth := r.Header.Get("Authorization"); auth != header {
    89  			w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`)
    90  			w.WriteHeader(http.StatusUnauthorized)
    91  			return
    92  		}
    93  		atomic.AddInt64(&successCount, 1)
    94  	}))
    95  	defer ts.Close()
    96  	uri, err := url.Parse(ts.URL)
    97  	if err != nil {
    98  		t.Fatalf("invalid test http server: %v", err)
    99  	}
   100  
   101  	client := &Client{
   102  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   103  			if reg != uri.Host {
   104  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   105  				t.Error(err)
   106  				return EmptyCredential, err
   107  			}
   108  			return Credential{
   109  				Username: username,
   110  				Password: password,
   111  			}, nil
   112  		},
   113  	}
   114  
   115  	// first request
   116  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   117  	if err != nil {
   118  		t.Fatalf("failed to create test request: %v", err)
   119  	}
   120  	resp, err := client.Do(req)
   121  	if err != nil {
   122  		t.Fatalf("Client.Do() error = %v", err)
   123  	}
   124  	if resp.StatusCode != http.StatusOK {
   125  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   126  	}
   127  	if wantRequestCount += 2; requestCount != wantRequestCount {
   128  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   129  	}
   130  	if wantSuccessCount++; successCount != wantSuccessCount {
   131  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   132  	}
   133  
   134  	// credential change
   135  	username = "test_user2"
   136  	password = "test_password2"
   137  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
   138  	if err != nil {
   139  		t.Fatalf("failed to create test request: %v", err)
   140  	}
   141  	resp, err = client.Do(req)
   142  	if err != nil {
   143  		t.Fatalf("Client.Do() error = %v", err)
   144  	}
   145  	if resp.StatusCode != http.StatusOK {
   146  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   147  	}
   148  	if wantRequestCount += 2; requestCount != wantRequestCount {
   149  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   150  	}
   151  	if wantSuccessCount++; successCount != wantSuccessCount {
   152  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   153  	}
   154  }
   155  
   156  func TestClient_Do_Basic_Auth_Cached(t *testing.T) {
   157  	username := "test_user"
   158  	password := "test_password"
   159  	var requestCount, wantRequestCount int64
   160  	var successCount, wantSuccessCount int64
   161  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   162  		atomic.AddInt64(&requestCount, 1)
   163  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   164  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   165  			w.WriteHeader(http.StatusNotFound)
   166  			return
   167  		}
   168  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
   169  		if auth := r.Header.Get("Authorization"); auth != header {
   170  			w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`)
   171  			w.WriteHeader(http.StatusUnauthorized)
   172  			return
   173  		}
   174  		atomic.AddInt64(&successCount, 1)
   175  	}))
   176  	defer ts.Close()
   177  	uri, err := url.Parse(ts.URL)
   178  	if err != nil {
   179  		t.Fatalf("invalid test http server: %v", err)
   180  	}
   181  
   182  	client := &Client{
   183  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   184  			if reg != uri.Host {
   185  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   186  				t.Error(err)
   187  				return EmptyCredential, err
   188  			}
   189  			return Credential{
   190  				Username: username,
   191  				Password: password,
   192  			}, nil
   193  		},
   194  		Cache: NewCache(),
   195  	}
   196  
   197  	// first request
   198  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   199  	if err != nil {
   200  		t.Fatalf("failed to create test request: %v", err)
   201  	}
   202  	resp, err := client.Do(req)
   203  	if err != nil {
   204  		t.Fatalf("Client.Do() error = %v", err)
   205  	}
   206  	if resp.StatusCode != http.StatusOK {
   207  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   208  	}
   209  	if wantRequestCount += 2; requestCount != wantRequestCount {
   210  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   211  	}
   212  	if wantSuccessCount++; successCount != wantSuccessCount {
   213  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   214  	}
   215  
   216  	// repeated request
   217  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
   218  	if err != nil {
   219  		t.Fatalf("failed to create test request: %v", err)
   220  	}
   221  	resp, err = client.Do(req)
   222  	if err != nil {
   223  		t.Fatalf("Client.Do() error = %v", err)
   224  	}
   225  	if resp.StatusCode != http.StatusOK {
   226  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   227  	}
   228  	if wantRequestCount++; requestCount != wantRequestCount {
   229  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   230  	}
   231  	if wantSuccessCount++; successCount != wantSuccessCount {
   232  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   233  	}
   234  
   235  	// credential change
   236  	username = "test_user2"
   237  	password = "test_password2"
   238  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
   239  	if err != nil {
   240  		t.Fatalf("failed to create test request: %v", err)
   241  	}
   242  	resp, err = client.Do(req)
   243  	if err != nil {
   244  		t.Fatalf("Client.Do() error = %v", err)
   245  	}
   246  	if resp.StatusCode != http.StatusOK {
   247  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   248  	}
   249  	if wantRequestCount += 2; requestCount != wantRequestCount {
   250  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   251  	}
   252  	if wantSuccessCount++; successCount != wantSuccessCount {
   253  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   254  	}
   255  }
   256  
   257  func TestClient_Do_Bearer_AccessToken(t *testing.T) {
   258  	accessToken := "test/access/token"
   259  	var requestCount, wantRequestCount int64
   260  	var successCount, wantSuccessCount int64
   261  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   262  		t.Error("unexecuted attempt of authorization service")
   263  		w.WriteHeader(http.StatusUnauthorized)
   264  	}))
   265  	defer as.Close()
   266  	var service string
   267  	scope := "repository:test:pull,push"
   268  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   269  		atomic.AddInt64(&requestCount, 1)
   270  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   271  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   272  			w.WriteHeader(http.StatusNotFound)
   273  			return
   274  		}
   275  		header := "Bearer " + accessToken
   276  		if auth := r.Header.Get("Authorization"); auth != header {
   277  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
   278  			w.Header().Set("Www-Authenticate", challenge)
   279  			w.WriteHeader(http.StatusUnauthorized)
   280  			return
   281  		}
   282  		atomic.AddInt64(&successCount, 1)
   283  	}))
   284  	defer ts.Close()
   285  	uri, err := url.Parse(ts.URL)
   286  	if err != nil {
   287  		t.Fatalf("invalid test http server: %v", err)
   288  	}
   289  	service = uri.Host
   290  
   291  	client := &Client{
   292  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   293  			if reg != uri.Host {
   294  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   295  				t.Error(err)
   296  				return EmptyCredential, err
   297  			}
   298  			return Credential{
   299  				AccessToken: accessToken,
   300  			}, nil
   301  		},
   302  	}
   303  
   304  	// first request
   305  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   306  	if err != nil {
   307  		t.Fatalf("failed to create test request: %v", err)
   308  	}
   309  	resp, err := client.Do(req)
   310  	if err != nil {
   311  		t.Fatalf("Client.Do() error = %v", err)
   312  	}
   313  	if resp.StatusCode != http.StatusOK {
   314  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   315  	}
   316  	if wantRequestCount += 2; requestCount != wantRequestCount {
   317  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   318  	}
   319  	if wantSuccessCount++; successCount != wantSuccessCount {
   320  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   321  	}
   322  
   323  	// credential change
   324  	accessToken = "test/access/token/2"
   325  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
   326  	if err != nil {
   327  		t.Fatalf("failed to create test request: %v", err)
   328  	}
   329  	resp, err = client.Do(req)
   330  	if err != nil {
   331  		t.Fatalf("Client.Do() error = %v", err)
   332  	}
   333  	if resp.StatusCode != http.StatusOK {
   334  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   335  	}
   336  	if wantRequestCount += 2; requestCount != wantRequestCount {
   337  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   338  	}
   339  	if wantSuccessCount++; successCount != wantSuccessCount {
   340  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   341  	}
   342  }
   343  
   344  func TestClient_Do_Bearer_AccessToken_Cached(t *testing.T) {
   345  	accessToken := "test/access/token"
   346  	var requestCount, wantRequestCount int64
   347  	var successCount, wantSuccessCount int64
   348  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   349  		t.Error("unexecuted attempt of authorization service")
   350  		w.WriteHeader(http.StatusUnauthorized)
   351  	}))
   352  	defer as.Close()
   353  	var service string
   354  	scope := "repository:test:pull,push"
   355  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   356  		atomic.AddInt64(&requestCount, 1)
   357  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   358  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   359  			w.WriteHeader(http.StatusNotFound)
   360  			return
   361  		}
   362  		header := "Bearer " + accessToken
   363  		if auth := r.Header.Get("Authorization"); auth != header {
   364  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
   365  			w.Header().Set("Www-Authenticate", challenge)
   366  			w.WriteHeader(http.StatusUnauthorized)
   367  			return
   368  		}
   369  		atomic.AddInt64(&successCount, 1)
   370  	}))
   371  	defer ts.Close()
   372  	uri, err := url.Parse(ts.URL)
   373  	if err != nil {
   374  		t.Fatalf("invalid test http server: %v", err)
   375  	}
   376  	service = uri.Host
   377  
   378  	client := &Client{
   379  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   380  			if reg != uri.Host {
   381  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   382  				t.Error(err)
   383  				return EmptyCredential, err
   384  			}
   385  			return Credential{
   386  				AccessToken: accessToken,
   387  			}, nil
   388  		},
   389  		Cache: NewCache(),
   390  	}
   391  
   392  	// first request
   393  	ctx := WithScopes(context.Background(), scope)
   394  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   395  	if err != nil {
   396  		t.Fatalf("failed to create test request: %v", err)
   397  	}
   398  	resp, err := client.Do(req)
   399  	if err != nil {
   400  		t.Fatalf("Client.Do() error = %v", err)
   401  	}
   402  	if resp.StatusCode != http.StatusOK {
   403  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   404  	}
   405  	if wantRequestCount += 2; requestCount != wantRequestCount {
   406  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   407  	}
   408  	if wantSuccessCount++; successCount != wantSuccessCount {
   409  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   410  	}
   411  
   412  	// repeated request
   413  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   414  	if err != nil {
   415  		t.Fatalf("failed to create test request: %v", err)
   416  	}
   417  	resp, err = client.Do(req)
   418  	if err != nil {
   419  		t.Fatalf("Client.Do() error = %v", err)
   420  	}
   421  	if resp.StatusCode != http.StatusOK {
   422  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   423  	}
   424  	if wantRequestCount++; requestCount != wantRequestCount {
   425  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   426  	}
   427  	if wantSuccessCount++; successCount != wantSuccessCount {
   428  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   429  	}
   430  
   431  	// credential change
   432  	accessToken = "test/access/token/2"
   433  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   434  	if err != nil {
   435  		t.Fatalf("failed to create test request: %v", err)
   436  	}
   437  	resp, err = client.Do(req)
   438  	if err != nil {
   439  		t.Fatalf("Client.Do() error = %v", err)
   440  	}
   441  	if resp.StatusCode != http.StatusOK {
   442  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   443  	}
   444  	if wantRequestCount += 2; requestCount != wantRequestCount {
   445  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   446  	}
   447  	if wantSuccessCount++; successCount != wantSuccessCount {
   448  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   449  	}
   450  }
   451  
   452  func TestClient_Do_Bearer_Auth(t *testing.T) {
   453  	username := "test_user"
   454  	password := "test_password"
   455  	accessToken := "test/access/token"
   456  	var requestCount, wantRequestCount int64
   457  	var successCount, wantSuccessCount int64
   458  	var authCount, wantAuthCount int64
   459  	var service string
   460  	scopes := []string{
   461  		"repository:dst:pull,push",
   462  		"repository:src:pull",
   463  	}
   464  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   465  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   466  			t.Error("unexecuted attempt of authorization service")
   467  			w.WriteHeader(http.StatusUnauthorized)
   468  			return
   469  		}
   470  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
   471  		if auth := r.Header.Get("Authorization"); auth != header {
   472  			t.Errorf("unexpected auth: got %s, want %s", auth, header)
   473  			w.WriteHeader(http.StatusUnauthorized)
   474  			return
   475  		}
   476  		if got := r.URL.Query().Get("service"); got != service {
   477  			t.Errorf("unexpected service: got %s, want %s", got, service)
   478  			w.WriteHeader(http.StatusUnauthorized)
   479  			return
   480  		}
   481  		if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes) {
   482  			t.Errorf("unexpected scope: got %s, want %s", got, scopes)
   483  			w.WriteHeader(http.StatusUnauthorized)
   484  			return
   485  		}
   486  
   487  		atomic.AddInt64(&authCount, 1)
   488  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
   489  			t.Errorf("failed to write %q: %v", r.URL, err)
   490  		}
   491  	}))
   492  	defer as.Close()
   493  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   494  		atomic.AddInt64(&requestCount, 1)
   495  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   496  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   497  			w.WriteHeader(http.StatusNotFound)
   498  			return
   499  		}
   500  		header := "Bearer " + accessToken
   501  		if auth := r.Header.Get("Authorization"); auth != header {
   502  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
   503  			w.Header().Set("Www-Authenticate", challenge)
   504  			w.WriteHeader(http.StatusUnauthorized)
   505  			return
   506  		}
   507  		atomic.AddInt64(&successCount, 1)
   508  	}))
   509  	defer ts.Close()
   510  	uri, err := url.Parse(ts.URL)
   511  	if err != nil {
   512  		t.Fatalf("invalid test http server: %v", err)
   513  	}
   514  	service = uri.Host
   515  
   516  	client := &Client{
   517  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   518  			if reg != uri.Host {
   519  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   520  				t.Error(err)
   521  				return EmptyCredential, err
   522  			}
   523  			return Credential{
   524  				Username: username,
   525  				Password: password,
   526  			}, nil
   527  		},
   528  	}
   529  
   530  	// first request
   531  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   532  	if err != nil {
   533  		t.Fatalf("failed to create test request: %v", err)
   534  	}
   535  	resp, err := client.Do(req)
   536  	if err != nil {
   537  		t.Fatalf("Client.Do() error = %v", err)
   538  	}
   539  	if resp.StatusCode != http.StatusOK {
   540  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   541  	}
   542  	if wantRequestCount += 2; requestCount != wantRequestCount {
   543  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   544  	}
   545  	if wantSuccessCount++; successCount != wantSuccessCount {
   546  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   547  	}
   548  	if wantAuthCount++; authCount != wantAuthCount {
   549  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   550  	}
   551  
   552  	// credential change
   553  	username = "test_user2"
   554  	password = "test_password2"
   555  	accessToken = "test/access/token/2"
   556  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
   557  	if err != nil {
   558  		t.Fatalf("failed to create test request: %v", err)
   559  	}
   560  	resp, err = client.Do(req)
   561  	if err != nil {
   562  		t.Fatalf("Client.Do() error = %v", err)
   563  	}
   564  	if resp.StatusCode != http.StatusOK {
   565  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   566  	}
   567  	if wantRequestCount += 2; requestCount != wantRequestCount {
   568  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   569  	}
   570  	if wantSuccessCount++; successCount != wantSuccessCount {
   571  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   572  	}
   573  	if wantAuthCount++; authCount != wantAuthCount {
   574  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   575  	}
   576  }
   577  
   578  func TestClient_Do_Bearer_Auth_Cached(t *testing.T) {
   579  	username := "test_user"
   580  	password := "test_password"
   581  	accessToken := "test/access/token"
   582  	var requestCount, wantRequestCount int64
   583  	var successCount, wantSuccessCount int64
   584  	var authCount, wantAuthCount int64
   585  	var service string
   586  	scopes := []string{
   587  		"repository:dst:pull,push",
   588  		"repository:src:pull",
   589  	}
   590  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   591  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   592  			t.Error("unexecuted attempt of authorization service")
   593  			w.WriteHeader(http.StatusUnauthorized)
   594  			return
   595  		}
   596  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
   597  		if auth := r.Header.Get("Authorization"); auth != header {
   598  			t.Errorf("unexpected auth: got %s, want %s", auth, header)
   599  			w.WriteHeader(http.StatusUnauthorized)
   600  			return
   601  		}
   602  		if got := r.URL.Query().Get("service"); got != service {
   603  			t.Errorf("unexpected service: got %s, want %s", got, service)
   604  			w.WriteHeader(http.StatusUnauthorized)
   605  			return
   606  		}
   607  		if got := r.URL.Query()["scope"]; !reflect.DeepEqual(got, scopes) {
   608  			t.Errorf("unexpected scope: got %s, want %s", got, scopes)
   609  			w.WriteHeader(http.StatusUnauthorized)
   610  			return
   611  		}
   612  
   613  		atomic.AddInt64(&authCount, 1)
   614  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
   615  			t.Errorf("failed to write %q: %v", r.URL, err)
   616  		}
   617  	}))
   618  	defer as.Close()
   619  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   620  		atomic.AddInt64(&requestCount, 1)
   621  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   622  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   623  			w.WriteHeader(http.StatusNotFound)
   624  			return
   625  		}
   626  		header := "Bearer " + accessToken
   627  		if auth := r.Header.Get("Authorization"); auth != header {
   628  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
   629  			w.Header().Set("Www-Authenticate", challenge)
   630  			w.WriteHeader(http.StatusUnauthorized)
   631  			return
   632  		}
   633  		atomic.AddInt64(&successCount, 1)
   634  	}))
   635  	defer ts.Close()
   636  	uri, err := url.Parse(ts.URL)
   637  	if err != nil {
   638  		t.Fatalf("invalid test http server: %v", err)
   639  	}
   640  	service = uri.Host
   641  
   642  	client := &Client{
   643  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   644  			if reg != uri.Host {
   645  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   646  				t.Error(err)
   647  				return EmptyCredential, err
   648  			}
   649  			return Credential{
   650  				Username: username,
   651  				Password: password,
   652  			}, nil
   653  		},
   654  		Cache: NewCache(),
   655  	}
   656  
   657  	// first request
   658  	ctx := WithScopes(context.Background(), scopes...)
   659  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   660  	if err != nil {
   661  		t.Fatalf("failed to create test request: %v", err)
   662  	}
   663  	resp, err := client.Do(req)
   664  	if err != nil {
   665  		t.Fatalf("Client.Do() error = %v", err)
   666  	}
   667  	if resp.StatusCode != http.StatusOK {
   668  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   669  	}
   670  	if wantRequestCount += 2; requestCount != wantRequestCount {
   671  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   672  	}
   673  	if wantSuccessCount++; successCount != wantSuccessCount {
   674  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   675  	}
   676  	if wantAuthCount++; authCount != wantAuthCount {
   677  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   678  	}
   679  
   680  	// repeated request
   681  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   682  	if err != nil {
   683  		t.Fatalf("failed to create test request: %v", err)
   684  	}
   685  	resp, err = client.Do(req)
   686  	if err != nil {
   687  		t.Fatalf("Client.Do() error = %v", err)
   688  	}
   689  	if resp.StatusCode != http.StatusOK {
   690  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   691  	}
   692  	if wantRequestCount++; requestCount != wantRequestCount {
   693  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   694  	}
   695  	if wantSuccessCount++; successCount != wantSuccessCount {
   696  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   697  	}
   698  	if authCount != wantAuthCount {
   699  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   700  	}
   701  
   702  	// credential change
   703  	username = "test_user2"
   704  	password = "test_password2"
   705  	accessToken = "test/access/token/2"
   706  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   707  	if err != nil {
   708  		t.Fatalf("failed to create test request: %v", err)
   709  	}
   710  	resp, err = client.Do(req)
   711  	if err != nil {
   712  		t.Fatalf("Client.Do() error = %v", err)
   713  	}
   714  	if resp.StatusCode != http.StatusOK {
   715  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   716  	}
   717  	if wantRequestCount += 2; requestCount != wantRequestCount {
   718  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   719  	}
   720  	if wantSuccessCount++; successCount != wantSuccessCount {
   721  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   722  	}
   723  	if wantAuthCount++; authCount != wantAuthCount {
   724  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   725  	}
   726  }
   727  
   728  func TestClient_Do_Bearer_OAuth2_Password(t *testing.T) {
   729  	username := "test_user"
   730  	password := "test_password"
   731  	accessToken := "test/access/token"
   732  	var requestCount, wantRequestCount int64
   733  	var successCount, wantSuccessCount int64
   734  	var authCount, wantAuthCount int64
   735  	var service string
   736  	scopes := []string{
   737  		"repository:dst:pull,push",
   738  		"repository:src:pull",
   739  	}
   740  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   741  		if r.Method != http.MethodPost || r.URL.Path != "/" {
   742  			t.Error("unexecuted attempt of authorization service")
   743  			w.WriteHeader(http.StatusUnauthorized)
   744  			return
   745  		}
   746  		if err := r.ParseForm(); err != nil {
   747  			t.Errorf("failed to parse form: %v", err)
   748  			w.WriteHeader(http.StatusUnauthorized)
   749  			return
   750  		}
   751  		if got := r.PostForm.Get("grant_type"); got != "password" {
   752  			t.Errorf("unexpected grant type: %v, want %v", got, "password")
   753  			w.WriteHeader(http.StatusUnauthorized)
   754  			return
   755  		}
   756  		if got := r.PostForm.Get("service"); got != service {
   757  			t.Errorf("unexpected service: %v, want %v", got, service)
   758  			w.WriteHeader(http.StatusUnauthorized)
   759  			return
   760  		}
   761  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
   762  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
   763  			w.WriteHeader(http.StatusUnauthorized)
   764  			return
   765  		}
   766  		scope := strings.Join(scopes, " ")
   767  		if got := r.PostForm.Get("scope"); got != scope {
   768  			t.Errorf("unexpected scope: %v, want %v", got, scope)
   769  			w.WriteHeader(http.StatusUnauthorized)
   770  			return
   771  		}
   772  		if got := r.PostForm.Get("username"); got != username {
   773  			t.Errorf("unexpected username: %v, want %v", got, username)
   774  			w.WriteHeader(http.StatusUnauthorized)
   775  			return
   776  		}
   777  		if got := r.PostForm.Get("password"); got != password {
   778  			t.Errorf("unexpected password: %v, want %v", got, password)
   779  			w.WriteHeader(http.StatusUnauthorized)
   780  			return
   781  		}
   782  
   783  		atomic.AddInt64(&authCount, 1)
   784  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
   785  			t.Errorf("failed to write %q: %v", r.URL, err)
   786  		}
   787  	}))
   788  	defer as.Close()
   789  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   790  		atomic.AddInt64(&requestCount, 1)
   791  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   792  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   793  			w.WriteHeader(http.StatusNotFound)
   794  			return
   795  		}
   796  		header := "Bearer " + accessToken
   797  		if auth := r.Header.Get("Authorization"); auth != header {
   798  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
   799  			w.Header().Set("Www-Authenticate", challenge)
   800  			w.WriteHeader(http.StatusUnauthorized)
   801  			return
   802  		}
   803  		atomic.AddInt64(&successCount, 1)
   804  	}))
   805  	defer ts.Close()
   806  	uri, err := url.Parse(ts.URL)
   807  	if err != nil {
   808  		t.Fatalf("invalid test http server: %v", err)
   809  	}
   810  	service = uri.Host
   811  
   812  	client := &Client{
   813  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   814  			if reg != uri.Host {
   815  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   816  				t.Error(err)
   817  				return EmptyCredential, err
   818  			}
   819  			return Credential{
   820  				Username: username,
   821  				Password: password,
   822  			}, nil
   823  		},
   824  		ForceAttemptOAuth2: true,
   825  	}
   826  
   827  	// first request
   828  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
   829  	if err != nil {
   830  		t.Fatalf("failed to create test request: %v", err)
   831  	}
   832  	resp, err := client.Do(req)
   833  	if err != nil {
   834  		t.Fatalf("Client.Do() error = %v", err)
   835  	}
   836  	if resp.StatusCode != http.StatusOK {
   837  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   838  	}
   839  	if wantRequestCount += 2; requestCount != wantRequestCount {
   840  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   841  	}
   842  	if wantSuccessCount++; successCount != wantSuccessCount {
   843  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   844  	}
   845  	if wantAuthCount++; authCount != wantAuthCount {
   846  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   847  	}
   848  
   849  	// credential change
   850  	username = "test_user2"
   851  	password = "test_password2"
   852  	accessToken = "test/access/token/2"
   853  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
   854  	if err != nil {
   855  		t.Fatalf("failed to create test request: %v", err)
   856  	}
   857  	resp, err = client.Do(req)
   858  	if err != nil {
   859  		t.Fatalf("Client.Do() error = %v", err)
   860  	}
   861  	if resp.StatusCode != http.StatusOK {
   862  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   863  	}
   864  	if wantRequestCount += 2; requestCount != wantRequestCount {
   865  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   866  	}
   867  	if wantSuccessCount++; successCount != wantSuccessCount {
   868  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   869  	}
   870  	if wantAuthCount++; authCount != wantAuthCount {
   871  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   872  	}
   873  }
   874  
   875  func TestClient_Do_Bearer_OAuth2_Password_Cached(t *testing.T) {
   876  	username := "test_user"
   877  	password := "test_password"
   878  	accessToken := "test/access/token"
   879  	var requestCount, wantRequestCount int64
   880  	var successCount, wantSuccessCount int64
   881  	var authCount, wantAuthCount int64
   882  	var service string
   883  	scopes := []string{
   884  		"repository:dst:pull,push",
   885  		"repository:src:pull",
   886  	}
   887  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   888  		if r.Method != http.MethodPost || r.URL.Path != "/" {
   889  			t.Error("unexecuted attempt of authorization service")
   890  			w.WriteHeader(http.StatusUnauthorized)
   891  			return
   892  		}
   893  		if err := r.ParseForm(); err != nil {
   894  			t.Errorf("failed to parse form: %v", err)
   895  			w.WriteHeader(http.StatusUnauthorized)
   896  			return
   897  		}
   898  		if got := r.PostForm.Get("grant_type"); got != "password" {
   899  			t.Errorf("unexpected grant type: %v, want %v", got, "password")
   900  			w.WriteHeader(http.StatusUnauthorized)
   901  			return
   902  		}
   903  		if got := r.PostForm.Get("service"); got != service {
   904  			t.Errorf("unexpected service: %v, want %v", got, service)
   905  			w.WriteHeader(http.StatusUnauthorized)
   906  			return
   907  		}
   908  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
   909  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
   910  			w.WriteHeader(http.StatusUnauthorized)
   911  			return
   912  		}
   913  		scope := strings.Join(scopes, " ")
   914  		if got := r.PostForm.Get("scope"); got != scope {
   915  			t.Errorf("unexpected scope: %v, want %v", got, scope)
   916  			w.WriteHeader(http.StatusUnauthorized)
   917  			return
   918  		}
   919  		if got := r.PostForm.Get("username"); got != username {
   920  			t.Errorf("unexpected username: %v, want %v", got, username)
   921  			w.WriteHeader(http.StatusUnauthorized)
   922  			return
   923  		}
   924  		if got := r.PostForm.Get("password"); got != password {
   925  			t.Errorf("unexpected password: %v, want %v", got, password)
   926  			w.WriteHeader(http.StatusUnauthorized)
   927  			return
   928  		}
   929  
   930  		atomic.AddInt64(&authCount, 1)
   931  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
   932  			t.Errorf("failed to write %q: %v", r.URL, err)
   933  		}
   934  	}))
   935  	defer as.Close()
   936  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   937  		atomic.AddInt64(&requestCount, 1)
   938  		if r.Method != http.MethodGet || r.URL.Path != "/" {
   939  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
   940  			w.WriteHeader(http.StatusNotFound)
   941  			return
   942  		}
   943  		header := "Bearer " + accessToken
   944  		if auth := r.Header.Get("Authorization"); auth != header {
   945  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
   946  			w.Header().Set("Www-Authenticate", challenge)
   947  			w.WriteHeader(http.StatusUnauthorized)
   948  			return
   949  		}
   950  		atomic.AddInt64(&successCount, 1)
   951  	}))
   952  	defer ts.Close()
   953  	uri, err := url.Parse(ts.URL)
   954  	if err != nil {
   955  		t.Fatalf("invalid test http server: %v", err)
   956  	}
   957  	service = uri.Host
   958  
   959  	client := &Client{
   960  		Credential: func(ctx context.Context, reg string) (Credential, error) {
   961  			if reg != uri.Host {
   962  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
   963  				t.Error(err)
   964  				return EmptyCredential, err
   965  			}
   966  			return Credential{
   967  				Username: username,
   968  				Password: password,
   969  			}, nil
   970  		},
   971  		ForceAttemptOAuth2: true,
   972  		Cache:              NewCache(),
   973  	}
   974  
   975  	// first request
   976  	ctx := WithScopes(context.Background(), scopes...)
   977  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
   978  	if err != nil {
   979  		t.Fatalf("failed to create test request: %v", err)
   980  	}
   981  	resp, err := client.Do(req)
   982  	if err != nil {
   983  		t.Fatalf("Client.Do() error = %v", err)
   984  	}
   985  	if resp.StatusCode != http.StatusOK {
   986  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
   987  	}
   988  	if wantRequestCount += 2; requestCount != wantRequestCount {
   989  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
   990  	}
   991  	if wantSuccessCount++; successCount != wantSuccessCount {
   992  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
   993  	}
   994  	if wantAuthCount++; authCount != wantAuthCount {
   995  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
   996  	}
   997  
   998  	// repeated request
   999  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1000  	if err != nil {
  1001  		t.Fatalf("failed to create test request: %v", err)
  1002  	}
  1003  	resp, err = client.Do(req)
  1004  	if err != nil {
  1005  		t.Fatalf("Client.Do() error = %v", err)
  1006  	}
  1007  	if resp.StatusCode != http.StatusOK {
  1008  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1009  	}
  1010  	if wantRequestCount++; requestCount != wantRequestCount {
  1011  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1012  	}
  1013  	if wantSuccessCount++; successCount != wantSuccessCount {
  1014  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1015  	}
  1016  	if authCount != wantAuthCount {
  1017  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1018  	}
  1019  
  1020  	// credential change
  1021  	username = "test_user2"
  1022  	password = "test_password2"
  1023  	accessToken = "test/access/token/2"
  1024  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1025  	if err != nil {
  1026  		t.Fatalf("failed to create test request: %v", err)
  1027  	}
  1028  	resp, err = client.Do(req)
  1029  	if err != nil {
  1030  		t.Fatalf("Client.Do() error = %v", err)
  1031  	}
  1032  	if resp.StatusCode != http.StatusOK {
  1033  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1034  	}
  1035  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1036  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1037  	}
  1038  	if wantSuccessCount++; successCount != wantSuccessCount {
  1039  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1040  	}
  1041  	if wantAuthCount++; authCount != wantAuthCount {
  1042  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1043  	}
  1044  }
  1045  
  1046  func TestClient_Do_Bearer_OAuth2_RefreshToken(t *testing.T) {
  1047  	refreshToken := "test/refresh/token"
  1048  	accessToken := "test/access/token"
  1049  	var requestCount, wantRequestCount int64
  1050  	var successCount, wantSuccessCount int64
  1051  	var authCount, wantAuthCount int64
  1052  	var service string
  1053  	scopes := []string{
  1054  		"repository:dst:pull,push",
  1055  		"repository:src:pull",
  1056  	}
  1057  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1058  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  1059  			t.Error("unexecuted attempt of authorization service")
  1060  			w.WriteHeader(http.StatusUnauthorized)
  1061  			return
  1062  		}
  1063  		if err := r.ParseForm(); err != nil {
  1064  			t.Errorf("failed to parse form: %v", err)
  1065  			w.WriteHeader(http.StatusUnauthorized)
  1066  			return
  1067  		}
  1068  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  1069  			t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
  1070  			w.WriteHeader(http.StatusUnauthorized)
  1071  			return
  1072  		}
  1073  		if got := r.PostForm.Get("service"); got != service {
  1074  			t.Errorf("unexpected service: %v, want %v", got, service)
  1075  			w.WriteHeader(http.StatusUnauthorized)
  1076  			return
  1077  		}
  1078  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  1079  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  1080  			w.WriteHeader(http.StatusUnauthorized)
  1081  			return
  1082  		}
  1083  		scope := strings.Join(scopes, " ")
  1084  		if got := r.PostForm.Get("scope"); got != scope {
  1085  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  1086  			w.WriteHeader(http.StatusUnauthorized)
  1087  			return
  1088  		}
  1089  		if got := r.PostForm.Get("refresh_token"); got != refreshToken {
  1090  			t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken)
  1091  			w.WriteHeader(http.StatusUnauthorized)
  1092  			return
  1093  		}
  1094  
  1095  		atomic.AddInt64(&authCount, 1)
  1096  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  1097  			t.Errorf("failed to write %q: %v", r.URL, err)
  1098  		}
  1099  	}))
  1100  	defer as.Close()
  1101  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1102  		atomic.AddInt64(&requestCount, 1)
  1103  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1104  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1105  			w.WriteHeader(http.StatusNotFound)
  1106  			return
  1107  		}
  1108  		header := "Bearer " + accessToken
  1109  		if auth := r.Header.Get("Authorization"); auth != header {
  1110  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
  1111  			w.Header().Set("Www-Authenticate", challenge)
  1112  			w.WriteHeader(http.StatusUnauthorized)
  1113  			return
  1114  		}
  1115  		atomic.AddInt64(&successCount, 1)
  1116  	}))
  1117  	defer ts.Close()
  1118  	uri, err := url.Parse(ts.URL)
  1119  	if err != nil {
  1120  		t.Fatalf("invalid test http server: %v", err)
  1121  	}
  1122  	service = uri.Host
  1123  
  1124  	client := &Client{
  1125  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  1126  			if reg != uri.Host {
  1127  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  1128  				t.Error(err)
  1129  				return EmptyCredential, err
  1130  			}
  1131  			return Credential{
  1132  				RefreshToken: refreshToken,
  1133  			}, nil
  1134  		},
  1135  	}
  1136  
  1137  	// first request
  1138  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
  1139  	if err != nil {
  1140  		t.Fatalf("failed to create test request: %v", err)
  1141  	}
  1142  	resp, err := client.Do(req)
  1143  	if err != nil {
  1144  		t.Fatalf("Client.Do() error = %v", err)
  1145  	}
  1146  	if resp.StatusCode != http.StatusOK {
  1147  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1148  	}
  1149  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1150  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1151  	}
  1152  	if wantSuccessCount++; successCount != wantSuccessCount {
  1153  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1154  	}
  1155  	if wantAuthCount++; authCount != wantAuthCount {
  1156  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1157  	}
  1158  
  1159  	// credential change
  1160  	refreshToken = "test/refresh/token/2"
  1161  	accessToken = "test/access/token/2"
  1162  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
  1163  	if err != nil {
  1164  		t.Fatalf("failed to create test request: %v", err)
  1165  	}
  1166  	resp, err = client.Do(req)
  1167  	if err != nil {
  1168  		t.Fatalf("Client.Do() error = %v", err)
  1169  	}
  1170  	if resp.StatusCode != http.StatusOK {
  1171  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1172  	}
  1173  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1174  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1175  	}
  1176  	if wantSuccessCount++; successCount != wantSuccessCount {
  1177  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1178  	}
  1179  	if wantAuthCount++; authCount != wantAuthCount {
  1180  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1181  	}
  1182  }
  1183  
  1184  func TestClient_Do_Bearer_OAuth2_RefreshToken_Cached(t *testing.T) {
  1185  	refreshToken := "test/refresh/token"
  1186  	accessToken := "test/access/token"
  1187  	var requestCount, wantRequestCount int64
  1188  	var successCount, wantSuccessCount int64
  1189  	var authCount, wantAuthCount int64
  1190  	var service string
  1191  	scopes := []string{
  1192  		"repository:dst:pull,push",
  1193  		"repository:src:pull",
  1194  	}
  1195  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1196  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  1197  			t.Error("unexecuted attempt of authorization service")
  1198  			w.WriteHeader(http.StatusUnauthorized)
  1199  			return
  1200  		}
  1201  		if err := r.ParseForm(); err != nil {
  1202  			t.Errorf("failed to parse form: %v", err)
  1203  			w.WriteHeader(http.StatusUnauthorized)
  1204  			return
  1205  		}
  1206  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  1207  			t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
  1208  			w.WriteHeader(http.StatusUnauthorized)
  1209  			return
  1210  		}
  1211  		if got := r.PostForm.Get("service"); got != service {
  1212  			t.Errorf("unexpected service: %v, want %v", got, service)
  1213  			w.WriteHeader(http.StatusUnauthorized)
  1214  			return
  1215  		}
  1216  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  1217  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  1218  			w.WriteHeader(http.StatusUnauthorized)
  1219  			return
  1220  		}
  1221  		scope := strings.Join(scopes, " ")
  1222  		if got := r.PostForm.Get("scope"); got != scope {
  1223  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  1224  			w.WriteHeader(http.StatusUnauthorized)
  1225  			return
  1226  		}
  1227  		if got := r.PostForm.Get("refresh_token"); got != refreshToken {
  1228  			t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken)
  1229  			w.WriteHeader(http.StatusUnauthorized)
  1230  			return
  1231  		}
  1232  
  1233  		atomic.AddInt64(&authCount, 1)
  1234  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  1235  			t.Errorf("failed to write %q: %v", r.URL, err)
  1236  		}
  1237  	}))
  1238  	defer as.Close()
  1239  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1240  		atomic.AddInt64(&requestCount, 1)
  1241  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1242  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1243  			w.WriteHeader(http.StatusNotFound)
  1244  			return
  1245  		}
  1246  		header := "Bearer " + accessToken
  1247  		if auth := r.Header.Get("Authorization"); auth != header {
  1248  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
  1249  			w.Header().Set("Www-Authenticate", challenge)
  1250  			w.WriteHeader(http.StatusUnauthorized)
  1251  			return
  1252  		}
  1253  		atomic.AddInt64(&successCount, 1)
  1254  	}))
  1255  	defer ts.Close()
  1256  	uri, err := url.Parse(ts.URL)
  1257  	if err != nil {
  1258  		t.Fatalf("invalid test http server: %v", err)
  1259  	}
  1260  	service = uri.Host
  1261  
  1262  	client := &Client{
  1263  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  1264  			if reg != uri.Host {
  1265  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  1266  				t.Error(err)
  1267  				return EmptyCredential, err
  1268  			}
  1269  			return Credential{
  1270  				RefreshToken: refreshToken,
  1271  			}, nil
  1272  		},
  1273  		Cache: NewCache(),
  1274  	}
  1275  
  1276  	// first request
  1277  	ctx := WithScopes(context.Background(), scopes...)
  1278  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1279  	if err != nil {
  1280  		t.Fatalf("failed to create test request: %v", err)
  1281  	}
  1282  	resp, err := client.Do(req)
  1283  	if err != nil {
  1284  		t.Fatalf("Client.Do() error = %v", err)
  1285  	}
  1286  	if resp.StatusCode != http.StatusOK {
  1287  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1288  	}
  1289  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1290  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1291  	}
  1292  	if wantSuccessCount++; successCount != wantSuccessCount {
  1293  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1294  	}
  1295  	if wantAuthCount++; authCount != wantAuthCount {
  1296  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1297  	}
  1298  
  1299  	// repeated request
  1300  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1301  	if err != nil {
  1302  		t.Fatalf("failed to create test request: %v", err)
  1303  	}
  1304  	resp, err = client.Do(req)
  1305  	if err != nil {
  1306  		t.Fatalf("Client.Do() error = %v", err)
  1307  	}
  1308  	if resp.StatusCode != http.StatusOK {
  1309  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1310  	}
  1311  	if wantRequestCount++; requestCount != wantRequestCount {
  1312  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1313  	}
  1314  	if wantSuccessCount++; successCount != wantSuccessCount {
  1315  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1316  	}
  1317  	if authCount != wantAuthCount {
  1318  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1319  	}
  1320  
  1321  	// credential change
  1322  	refreshToken = "test/refresh/token/2"
  1323  	accessToken = "test/access/token/2"
  1324  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1325  	if err != nil {
  1326  		t.Fatalf("failed to create test request: %v", err)
  1327  	}
  1328  	resp, err = client.Do(req)
  1329  	if err != nil {
  1330  		t.Fatalf("Client.Do() error = %v", err)
  1331  	}
  1332  	if resp.StatusCode != http.StatusOK {
  1333  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1334  	}
  1335  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1336  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1337  	}
  1338  	if wantSuccessCount++; successCount != wantSuccessCount {
  1339  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1340  	}
  1341  	if wantAuthCount++; authCount != wantAuthCount {
  1342  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1343  	}
  1344  }
  1345  
  1346  func TestClient_Do_Token_Expire(t *testing.T) {
  1347  	refreshToken := "test/refresh/token"
  1348  	accessToken := "test/access/token"
  1349  	var requestCount, wantRequestCount int64
  1350  	var successCount, wantSuccessCount int64
  1351  	var authCount, wantAuthCount int64
  1352  	var service string
  1353  	scopes := []string{
  1354  		"repository:dst:pull,push",
  1355  		"repository:src:pull",
  1356  	}
  1357  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1358  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  1359  			t.Error("unexecuted attempt of authorization service")
  1360  			w.WriteHeader(http.StatusUnauthorized)
  1361  			return
  1362  		}
  1363  		if err := r.ParseForm(); err != nil {
  1364  			t.Errorf("failed to parse form: %v", err)
  1365  			w.WriteHeader(http.StatusUnauthorized)
  1366  			return
  1367  		}
  1368  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  1369  			t.Errorf("unexpected grant type: %v, want %v", got, "refresh_token")
  1370  			w.WriteHeader(http.StatusUnauthorized)
  1371  			return
  1372  		}
  1373  		if got := r.PostForm.Get("service"); got != service {
  1374  			t.Errorf("unexpected service: %v, want %v", got, service)
  1375  			w.WriteHeader(http.StatusUnauthorized)
  1376  			return
  1377  		}
  1378  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  1379  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  1380  			w.WriteHeader(http.StatusUnauthorized)
  1381  			return
  1382  		}
  1383  		scope := strings.Join(scopes, " ")
  1384  		if got := r.PostForm.Get("scope"); got != scope {
  1385  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  1386  			w.WriteHeader(http.StatusUnauthorized)
  1387  			return
  1388  		}
  1389  		if got := r.PostForm.Get("refresh_token"); got != refreshToken {
  1390  			t.Errorf("unexpected refresh token: %v, want %v", got, refreshToken)
  1391  			w.WriteHeader(http.StatusUnauthorized)
  1392  			return
  1393  		}
  1394  
  1395  		atomic.AddInt64(&authCount, 1)
  1396  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  1397  			t.Errorf("failed to write %q: %v", r.URL, err)
  1398  		}
  1399  	}))
  1400  	defer as.Close()
  1401  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1402  		atomic.AddInt64(&requestCount, 1)
  1403  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1404  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1405  			w.WriteHeader(http.StatusNotFound)
  1406  			return
  1407  		}
  1408  		header := "Bearer " + accessToken
  1409  		if auth := r.Header.Get("Authorization"); auth != header {
  1410  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
  1411  			w.Header().Set("Www-Authenticate", challenge)
  1412  			w.WriteHeader(http.StatusUnauthorized)
  1413  			return
  1414  		}
  1415  		atomic.AddInt64(&successCount, 1)
  1416  	}))
  1417  	defer ts.Close()
  1418  	uri, err := url.Parse(ts.URL)
  1419  	if err != nil {
  1420  		t.Fatalf("invalid test http server: %v", err)
  1421  	}
  1422  	service = uri.Host
  1423  
  1424  	client := &Client{
  1425  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  1426  			if reg != uri.Host {
  1427  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  1428  				t.Error(err)
  1429  				return EmptyCredential, err
  1430  			}
  1431  			return Credential{
  1432  				RefreshToken: refreshToken,
  1433  			}, nil
  1434  		},
  1435  		Cache: NewCache(),
  1436  	}
  1437  
  1438  	// first request
  1439  	ctx := WithScopes(context.Background(), scopes...)
  1440  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1441  	if err != nil {
  1442  		t.Fatalf("failed to create test request: %v", err)
  1443  	}
  1444  	resp, err := client.Do(req)
  1445  	if err != nil {
  1446  		t.Fatalf("Client.Do() error = %v", err)
  1447  	}
  1448  	if resp.StatusCode != http.StatusOK {
  1449  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1450  	}
  1451  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1452  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1453  	}
  1454  	if wantSuccessCount++; successCount != wantSuccessCount {
  1455  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1456  	}
  1457  	if wantAuthCount++; authCount != wantAuthCount {
  1458  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1459  	}
  1460  
  1461  	// invalidate the access token and request again
  1462  	accessToken = "test/access/token/2"
  1463  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1464  	if err != nil {
  1465  		t.Fatalf("failed to create test request: %v", err)
  1466  	}
  1467  	resp, err = client.Do(req)
  1468  	if err != nil {
  1469  		t.Fatalf("Client.Do() error = %v", err)
  1470  	}
  1471  	if resp.StatusCode != http.StatusOK {
  1472  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1473  	}
  1474  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1475  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1476  	}
  1477  	if wantSuccessCount++; successCount != wantSuccessCount {
  1478  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1479  	}
  1480  	if wantAuthCount++; authCount != wantAuthCount {
  1481  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1482  	}
  1483  }
  1484  
  1485  func TestClient_Do_Scope_Hint_Mismatch(t *testing.T) {
  1486  	username := "test_user"
  1487  	password := "test_password"
  1488  	accessToken := "test/access/token"
  1489  	var requestCount, wantRequestCount int64
  1490  	var successCount, wantSuccessCount int64
  1491  	var authCount, wantAuthCount int64
  1492  	var service string
  1493  	scopes := []string{
  1494  		"repository:dst:pull,push",
  1495  		"repository:src:pull",
  1496  	}
  1497  	scope := "repository:test:delete"
  1498  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1499  		if r.Method != http.MethodPost || r.URL.Path != "/" {
  1500  			t.Error("unexecuted attempt of authorization service")
  1501  			w.WriteHeader(http.StatusUnauthorized)
  1502  			return
  1503  		}
  1504  		if err := r.ParseForm(); err != nil {
  1505  			t.Errorf("failed to parse form: %v", err)
  1506  			w.WriteHeader(http.StatusUnauthorized)
  1507  			return
  1508  		}
  1509  		if got := r.PostForm.Get("grant_type"); got != "password" {
  1510  			t.Errorf("unexpected grant type: %v, want %v", got, "password")
  1511  			w.WriteHeader(http.StatusUnauthorized)
  1512  			return
  1513  		}
  1514  		if got := r.PostForm.Get("service"); got != service {
  1515  			t.Errorf("unexpected service: %v, want %v", got, service)
  1516  			w.WriteHeader(http.StatusUnauthorized)
  1517  			return
  1518  		}
  1519  		if got := r.PostForm.Get("client_id"); got != defaultClientID {
  1520  			t.Errorf("unexpected client id: %v, want %v", got, defaultClientID)
  1521  			w.WriteHeader(http.StatusUnauthorized)
  1522  			return
  1523  		}
  1524  		scopes := CleanScopes(append([]string{scope}, scopes...))
  1525  		scope := strings.Join(scopes, " ")
  1526  		if got := r.PostForm.Get("scope"); got != scope {
  1527  			t.Errorf("unexpected scope: %v, want %v", got, scope)
  1528  			w.WriteHeader(http.StatusUnauthorized)
  1529  			return
  1530  		}
  1531  		if got := r.PostForm.Get("username"); got != username {
  1532  			t.Errorf("unexpected username: %v, want %v", got, username)
  1533  			w.WriteHeader(http.StatusUnauthorized)
  1534  			return
  1535  		}
  1536  		if got := r.PostForm.Get("password"); got != password {
  1537  			t.Errorf("unexpected password: %v, want %v", got, password)
  1538  			w.WriteHeader(http.StatusUnauthorized)
  1539  			return
  1540  		}
  1541  
  1542  		atomic.AddInt64(&authCount, 1)
  1543  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  1544  			t.Errorf("failed to write %q: %v", r.URL, err)
  1545  		}
  1546  	}))
  1547  	defer as.Close()
  1548  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1549  		atomic.AddInt64(&requestCount, 1)
  1550  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1551  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1552  			w.WriteHeader(http.StatusNotFound)
  1553  			return
  1554  		}
  1555  		header := "Bearer " + accessToken
  1556  		if auth := r.Header.Get("Authorization"); auth != header {
  1557  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
  1558  			w.Header().Set("Www-Authenticate", challenge)
  1559  			w.WriteHeader(http.StatusUnauthorized)
  1560  			return
  1561  		}
  1562  		atomic.AddInt64(&successCount, 1)
  1563  	}))
  1564  	defer ts.Close()
  1565  	uri, err := url.Parse(ts.URL)
  1566  	if err != nil {
  1567  		t.Fatalf("invalid test http server: %v", err)
  1568  	}
  1569  	service = uri.Host
  1570  
  1571  	client := &Client{
  1572  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  1573  			if reg != uri.Host {
  1574  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  1575  				t.Error(err)
  1576  				return EmptyCredential, err
  1577  			}
  1578  			return Credential{
  1579  				Username: username,
  1580  				Password: password,
  1581  			}, nil
  1582  		},
  1583  		ForceAttemptOAuth2: true,
  1584  		Cache:              NewCache(),
  1585  	}
  1586  
  1587  	// first request
  1588  	ctx := WithScopes(context.Background(), scopes...)
  1589  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1590  	if err != nil {
  1591  		t.Fatalf("failed to create test request: %v", err)
  1592  	}
  1593  	resp, err := client.Do(req)
  1594  	if err != nil {
  1595  		t.Fatalf("Client.Do() error = %v", err)
  1596  	}
  1597  	if resp.StatusCode != http.StatusOK {
  1598  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1599  	}
  1600  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1601  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1602  	}
  1603  	if wantSuccessCount++; successCount != wantSuccessCount {
  1604  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1605  	}
  1606  	if wantAuthCount++; authCount != wantAuthCount {
  1607  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1608  	}
  1609  
  1610  	// repeated request
  1611  	// although the actual scope does not match the hinted scopes, the client
  1612  	// with cache cannot avoid a request to obtain a challenge but can prevent
  1613  	// a repeated call to the authorization server.
  1614  	req, err = http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
  1615  	if err != nil {
  1616  		t.Fatalf("failed to create test request: %v", err)
  1617  	}
  1618  	resp, err = client.Do(req)
  1619  	if err != nil {
  1620  		t.Fatalf("Client.Do() error = %v", err)
  1621  	}
  1622  	if resp.StatusCode != http.StatusOK {
  1623  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1624  	}
  1625  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1626  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1627  	}
  1628  	if wantSuccessCount++; successCount != wantSuccessCount {
  1629  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1630  	}
  1631  	if authCount != wantAuthCount {
  1632  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1633  	}
  1634  }
  1635  
  1636  func TestClient_Do_Invalid_Credential_Basic(t *testing.T) {
  1637  	username := "test_user"
  1638  	password := "test_password"
  1639  	var requestCount, wantRequestCount int64
  1640  	var successCount, wantSuccessCount int64
  1641  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1642  		atomic.AddInt64(&requestCount, 1)
  1643  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1644  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1645  			w.WriteHeader(http.StatusNotFound)
  1646  			return
  1647  		}
  1648  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
  1649  		if auth := r.Header.Get("Authorization"); auth != header {
  1650  			w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`)
  1651  			w.WriteHeader(http.StatusUnauthorized)
  1652  			return
  1653  		}
  1654  		atomic.AddInt64(&successCount, 1)
  1655  		t.Error("authentication should fail but succeeded")
  1656  	}))
  1657  	defer ts.Close()
  1658  	uri, err := url.Parse(ts.URL)
  1659  	if err != nil {
  1660  		t.Fatalf("invalid test http server: %v", err)
  1661  	}
  1662  
  1663  	client := &Client{
  1664  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  1665  			if reg != uri.Host {
  1666  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  1667  				t.Error(err)
  1668  				return EmptyCredential, err
  1669  			}
  1670  			return Credential{
  1671  				Username: username,
  1672  				Password: "bad credential",
  1673  			}, nil
  1674  		},
  1675  	}
  1676  
  1677  	// request should fail
  1678  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
  1679  	if err != nil {
  1680  		t.Fatalf("failed to create test request: %v", err)
  1681  	}
  1682  	resp, err := client.Do(req)
  1683  	if err != nil {
  1684  		t.Fatalf("Client.Do() error = %v", err)
  1685  	}
  1686  	if resp.StatusCode != http.StatusUnauthorized {
  1687  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusUnauthorized)
  1688  	}
  1689  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1690  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1691  	}
  1692  	if successCount != wantSuccessCount {
  1693  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1694  	}
  1695  }
  1696  
  1697  func TestClient_Do_Invalid_Credential_Bearer(t *testing.T) {
  1698  	username := "test_user"
  1699  	password := "test_password"
  1700  	accessToken := "test/access/token"
  1701  	var requestCount, wantRequestCount int64
  1702  	var successCount, wantSuccessCount int64
  1703  	var authCount, wantAuthCount int64
  1704  	var service string
  1705  	scopes := []string{
  1706  		"repository:dst:pull,push",
  1707  		"repository:src:pull",
  1708  	}
  1709  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1710  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1711  			t.Error("unexecuted attempt of authorization service")
  1712  			w.WriteHeader(http.StatusUnauthorized)
  1713  			return
  1714  		}
  1715  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
  1716  		if auth := r.Header.Get("Authorization"); auth != header {
  1717  			atomic.AddInt64(&authCount, 1)
  1718  			w.WriteHeader(http.StatusUnauthorized)
  1719  			return
  1720  		}
  1721  		t.Error("authentication should fail but succeeded")
  1722  	}))
  1723  	defer as.Close()
  1724  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1725  		atomic.AddInt64(&requestCount, 1)
  1726  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1727  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1728  			w.WriteHeader(http.StatusNotFound)
  1729  			return
  1730  		}
  1731  		header := "Bearer " + accessToken
  1732  		if auth := r.Header.Get("Authorization"); auth != header {
  1733  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, strings.Join(scopes, " "))
  1734  			w.Header().Set("Www-Authenticate", challenge)
  1735  			w.WriteHeader(http.StatusUnauthorized)
  1736  			return
  1737  		}
  1738  		atomic.AddInt64(&successCount, 1)
  1739  		t.Error("authentication should fail but succeeded")
  1740  	}))
  1741  	defer ts.Close()
  1742  	uri, err := url.Parse(ts.URL)
  1743  	if err != nil {
  1744  		t.Fatalf("invalid test http server: %v", err)
  1745  	}
  1746  	service = uri.Host
  1747  
  1748  	client := &Client{
  1749  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  1750  			if reg != uri.Host {
  1751  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  1752  				t.Error(err)
  1753  				return EmptyCredential, err
  1754  			}
  1755  			return Credential{
  1756  				Username: username,
  1757  				Password: "bad credential",
  1758  			}, nil
  1759  		},
  1760  	}
  1761  
  1762  	// request should fail
  1763  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
  1764  	if err != nil {
  1765  		t.Fatalf("failed to create test request: %v", err)
  1766  	}
  1767  	_, err = client.Do(req)
  1768  	if err == nil {
  1769  		t.Fatalf("Client.Do() error = %v, wantErr %v", err, true)
  1770  	}
  1771  	if wantRequestCount++; requestCount != wantRequestCount {
  1772  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1773  	}
  1774  	if successCount != wantSuccessCount {
  1775  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1776  	}
  1777  	if wantAuthCount++; authCount != wantAuthCount {
  1778  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1779  	}
  1780  }
  1781  
  1782  func TestClient_Do_Anonymous_Pull(t *testing.T) {
  1783  	accessToken := "test/access/token"
  1784  	var requestCount, wantRequestCount int64
  1785  	var successCount, wantSuccessCount int64
  1786  	var authCount, wantAuthCount int64
  1787  	var service string
  1788  	scope := "repository:test:pull"
  1789  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1790  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1791  			t.Error("unexecuted attempt of authorization service")
  1792  			w.WriteHeader(http.StatusUnauthorized)
  1793  			return
  1794  		}
  1795  		if auth := r.Header.Get("Authorization"); auth != "" {
  1796  			t.Errorf("unexpected auth: got %s, want %s", auth, "")
  1797  			w.WriteHeader(http.StatusUnauthorized)
  1798  			return
  1799  		}
  1800  		if got := r.URL.Query().Get("service"); got != service {
  1801  			t.Errorf("unexpected service: got %s, want %s", got, service)
  1802  			w.WriteHeader(http.StatusUnauthorized)
  1803  			return
  1804  		}
  1805  		if got := r.URL.Query().Get("scope"); got != scope {
  1806  			t.Errorf("unexpected scope: got %s, want %s", got, scope)
  1807  			w.WriteHeader(http.StatusUnauthorized)
  1808  			return
  1809  		}
  1810  
  1811  		atomic.AddInt64(&authCount, 1)
  1812  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  1813  			t.Errorf("failed to write %q: %v", r.URL, err)
  1814  		}
  1815  	}))
  1816  	defer as.Close()
  1817  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1818  		atomic.AddInt64(&requestCount, 1)
  1819  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1820  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1821  			w.WriteHeader(http.StatusNotFound)
  1822  			return
  1823  		}
  1824  		header := "Bearer " + accessToken
  1825  		if auth := r.Header.Get("Authorization"); auth != header {
  1826  			challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
  1827  			w.Header().Set("Www-Authenticate", challenge)
  1828  			w.WriteHeader(http.StatusUnauthorized)
  1829  			return
  1830  		}
  1831  		atomic.AddInt64(&successCount, 1)
  1832  	}))
  1833  	defer ts.Close()
  1834  	uri, err := url.Parse(ts.URL)
  1835  	if err != nil {
  1836  		t.Fatalf("invalid test http server: %v", err)
  1837  	}
  1838  	service = uri.Host
  1839  
  1840  	// request with the default client
  1841  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
  1842  	if err != nil {
  1843  		t.Fatalf("failed to create test request: %v", err)
  1844  	}
  1845  	resp, err := DefaultClient.Do(req)
  1846  	if err != nil {
  1847  		t.Fatalf("Client.Do() error = %v", err)
  1848  	}
  1849  	if resp.StatusCode != http.StatusOK {
  1850  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1851  	}
  1852  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1853  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1854  	}
  1855  	if wantSuccessCount++; successCount != wantSuccessCount {
  1856  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1857  	}
  1858  	if wantAuthCount++; authCount != wantAuthCount {
  1859  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1860  	}
  1861  }
  1862  
  1863  func TestClient_Do_Scheme_Change(t *testing.T) {
  1864  	username := "test_user"
  1865  	password := "test_password"
  1866  	accessToken := "test/access/token"
  1867  	var requestCount, wantRequestCount int64
  1868  	var successCount, wantSuccessCount int64
  1869  	var authCount, wantAuthCount int64
  1870  	var service string
  1871  	scope := "repository:test:pull"
  1872  	challengeBearerAuth := true
  1873  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1874  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1875  			t.Error("unexecuted attempt of authorization service")
  1876  			w.WriteHeader(http.StatusUnauthorized)
  1877  			return
  1878  		}
  1879  		header := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
  1880  		if auth := r.Header.Get("Authorization"); auth != header {
  1881  			t.Errorf("unexpected auth: got %s, want %s", auth, header)
  1882  			w.WriteHeader(http.StatusUnauthorized)
  1883  			return
  1884  		}
  1885  		if got := r.URL.Query().Get("service"); got != service {
  1886  			t.Errorf("unexpected service: got %s, want %s", got, service)
  1887  			w.WriteHeader(http.StatusUnauthorized)
  1888  			return
  1889  		}
  1890  		if got := r.URL.Query().Get("scope"); got != scope {
  1891  			t.Errorf("unexpected scope: got %s, want %s", got, scope)
  1892  			w.WriteHeader(http.StatusUnauthorized)
  1893  			return
  1894  		}
  1895  
  1896  		atomic.AddInt64(&authCount, 1)
  1897  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, accessToken); err != nil {
  1898  			t.Errorf("failed to write %q: %v", r.URL, err)
  1899  		}
  1900  	}))
  1901  	defer as.Close()
  1902  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1903  		atomic.AddInt64(&requestCount, 1)
  1904  		if r.Method != http.MethodGet || r.URL.Path != "/" {
  1905  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
  1906  			w.WriteHeader(http.StatusNotFound)
  1907  			return
  1908  		}
  1909  		bearerHeader := "Bearer " + accessToken
  1910  		basicHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
  1911  		header := r.Header.Get("Authorization")
  1912  		if (challengeBearerAuth && header != bearerHeader) || (!challengeBearerAuth && header != basicHeader) {
  1913  			var challenge string
  1914  			if challengeBearerAuth {
  1915  				challenge = fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, service, scope)
  1916  			} else {
  1917  				challenge = `Basic realm="Test Server"`
  1918  			}
  1919  			w.Header().Set("Www-Authenticate", challenge)
  1920  			w.WriteHeader(http.StatusUnauthorized)
  1921  			return
  1922  		}
  1923  		atomic.AddInt64(&successCount, 1)
  1924  	}))
  1925  	defer ts.Close()
  1926  	uri, err := url.Parse(ts.URL)
  1927  	if err != nil {
  1928  		t.Fatalf("invalid test http server: %v", err)
  1929  	}
  1930  	service = uri.Host
  1931  
  1932  	client := &Client{
  1933  		Credential: func(ctx context.Context, reg string) (Credential, error) {
  1934  			if reg != uri.Host {
  1935  				err := fmt.Errorf("registry mismatch: got %v, want %v", reg, uri.Host)
  1936  				t.Error(err)
  1937  				return EmptyCredential, err
  1938  			}
  1939  			return Credential{
  1940  				Username: username,
  1941  				Password: password,
  1942  			}, nil
  1943  		},
  1944  		Cache: NewCache(),
  1945  	}
  1946  
  1947  	// request with bearer auth
  1948  	req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
  1949  	if err != nil {
  1950  		t.Fatalf("failed to create test request: %v", err)
  1951  	}
  1952  	resp, err := client.Do(req)
  1953  	if err != nil {
  1954  		t.Fatalf("Client.Do() error = %v", err)
  1955  	}
  1956  	if resp.StatusCode != http.StatusOK {
  1957  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1958  	}
  1959  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1960  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1961  	}
  1962  	if wantSuccessCount++; successCount != wantSuccessCount {
  1963  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1964  	}
  1965  	if wantAuthCount++; authCount != wantAuthCount {
  1966  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1967  	}
  1968  
  1969  	// change to basic auth
  1970  	challengeBearerAuth = false
  1971  	req, err = http.NewRequest(http.MethodGet, ts.URL, nil)
  1972  	if err != nil {
  1973  		t.Fatalf("failed to create test request: %v", err)
  1974  	}
  1975  	resp, err = client.Do(req)
  1976  	if err != nil {
  1977  		t.Fatalf("Client.Do() error = %v", err)
  1978  	}
  1979  	if resp.StatusCode != http.StatusOK {
  1980  		t.Errorf("Client.Do() = %v, want %v", resp.StatusCode, http.StatusOK)
  1981  	}
  1982  	if wantRequestCount += 2; requestCount != wantRequestCount {
  1983  		t.Errorf("unexpected number of requests: %d, want %d", requestCount, wantRequestCount)
  1984  	}
  1985  	if wantSuccessCount++; successCount != wantSuccessCount {
  1986  		t.Errorf("unexpected number of successful requests: %d, want %d", successCount, wantSuccessCount)
  1987  	}
  1988  	if authCount != wantAuthCount {
  1989  		t.Errorf("unexpected number of auth requests: %d, want %d", authCount, wantAuthCount)
  1990  	}
  1991  }
  1992  
  1993  func TestClient_StaticCredential_basicAuth(t *testing.T) {
  1994  	testUsername := "username"
  1995  	testPassword := "password"
  1996  
  1997  	// create a test server
  1998  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1999  		path := r.URL.Path
  2000  		if r.Method != http.MethodGet {
  2001  			w.WriteHeader(http.StatusNotFound)
  2002  			t.Fatal("unexpected access")
  2003  		}
  2004  		switch path {
  2005  		case "/basicAuth":
  2006  			wantedAuthHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(testUsername+":"+testPassword))
  2007  			authHeader := r.Header.Get("Authorization")
  2008  			if authHeader != wantedAuthHeader {
  2009  				w.Header().Set("Www-Authenticate", `Basic realm="Test Server"`)
  2010  				w.WriteHeader(http.StatusUnauthorized)
  2011  			}
  2012  		default:
  2013  			w.WriteHeader(http.StatusNotAcceptable)
  2014  		}
  2015  	}))
  2016  	defer ts.Close()
  2017  	host := ts.URL
  2018  	uri, _ := url.Parse(host)
  2019  	hostAddress := uri.Host
  2020  	basicAuthURL := fmt.Sprintf("%s/basicAuth", host)
  2021  
  2022  	// create a test client with the correct credentials
  2023  	clientValid := &Client{
  2024  		Credential: StaticCredential(hostAddress, Credential{
  2025  			Username: testUsername,
  2026  			Password: testPassword,
  2027  		}),
  2028  	}
  2029  	req, err := http.NewRequest(http.MethodGet, basicAuthURL, nil)
  2030  	if err != nil {
  2031  		t.Fatalf("could not create request, err = %v", err)
  2032  	}
  2033  	respValid, err := clientValid.Do(req)
  2034  	if err != nil {
  2035  		t.Fatalf("could not send request, err = %v", err)
  2036  	}
  2037  	if respValid.StatusCode != 200 {
  2038  		t.Errorf("incorrect status code: %d, expected 200", respValid.StatusCode)
  2039  	}
  2040  
  2041  	// create a test client with incorrect credentials
  2042  	clientInvalid := &Client{
  2043  		Credential: StaticCredential(hostAddress, Credential{
  2044  			Username: "foo",
  2045  			Password: "bar",
  2046  		}),
  2047  	}
  2048  	respInvalid, err := clientInvalid.Do(req)
  2049  	if err != nil {
  2050  		t.Fatalf("could not send request, err = %v", err)
  2051  	}
  2052  	if respInvalid.StatusCode != 401 {
  2053  		t.Errorf("incorrect status code: %d, expected 401", respInvalid.StatusCode)
  2054  	}
  2055  }
  2056  
  2057  func TestClient_StaticCredential_withAccessToken(t *testing.T) {
  2058  	var host string
  2059  	testAccessToken := "test/access/token"
  2060  	scope := "repository:test:pull,push"
  2061  
  2062  	// create an authorization server
  2063  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2064  		w.WriteHeader(http.StatusUnauthorized)
  2065  		t.Error("unexecuted attempt of authorization service")
  2066  	}))
  2067  	defer as.Close()
  2068  
  2069  	// create a test server
  2070  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2071  		path := r.URL.Path
  2072  		if r.Method != http.MethodGet {
  2073  			w.WriteHeader(http.StatusNotFound)
  2074  			t.Fatal("unexpected access")
  2075  		}
  2076  		switch path {
  2077  		case "/accessToken":
  2078  			wantedAuthHeader := "Bearer " + testAccessToken
  2079  			if auth := r.Header.Get("Authorization"); auth != wantedAuthHeader {
  2080  				challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, host, scope)
  2081  				w.Header().Set("Www-Authenticate", challenge)
  2082  				w.WriteHeader(http.StatusUnauthorized)
  2083  			}
  2084  		default:
  2085  			w.WriteHeader(http.StatusNotAcceptable)
  2086  		}
  2087  	}))
  2088  	defer ts.Close()
  2089  	host = ts.URL
  2090  	uri, _ := url.Parse(host)
  2091  	hostAddress := uri.Host
  2092  	accessTokenURL := fmt.Sprintf("%s/accessToken", host)
  2093  
  2094  	// create a test client with the correct credentials
  2095  	clientValid := &Client{
  2096  		Credential: StaticCredential(hostAddress, Credential{
  2097  			AccessToken: testAccessToken,
  2098  		}),
  2099  	}
  2100  	req, err := http.NewRequest(http.MethodGet, accessTokenURL, nil)
  2101  	if err != nil {
  2102  		t.Fatalf("could not create request, err = %v", err)
  2103  	}
  2104  	respValid, err := clientValid.Do(req)
  2105  	if err != nil {
  2106  		t.Fatalf("could not send request, err = %v", err)
  2107  	}
  2108  	if respValid.StatusCode != 200 {
  2109  		t.Errorf("incorrect status code: %d, expected 200", respValid.StatusCode)
  2110  	}
  2111  
  2112  	// create a test client with incorrect credentials
  2113  	clientInvalid := &Client{
  2114  		Credential: StaticCredential(hostAddress, Credential{
  2115  			AccessToken: "foo",
  2116  		}),
  2117  	}
  2118  	respInvalid, err := clientInvalid.Do(req)
  2119  	if err != nil {
  2120  		t.Fatalf("could not send request, err = %v", err)
  2121  	}
  2122  	if respInvalid.StatusCode != 401 {
  2123  		t.Errorf("incorrect status code: %d, expected 401", respInvalid.StatusCode)
  2124  	}
  2125  }
  2126  
  2127  func TestClient_StaticCredential_withRefreshToken(t *testing.T) {
  2128  	var host string
  2129  	testAccessToken := "test/access/token"
  2130  	testRefreshToken := "test/refresh/token"
  2131  	scope := "repository:test:pull,push"
  2132  
  2133  	// create an authorization server
  2134  	as := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2135  		if r.Method != http.MethodGet && r.Method != http.MethodPost {
  2136  			w.WriteHeader(http.StatusUnauthorized)
  2137  			t.Error("unexecuted attempt of authorization service")
  2138  		}
  2139  		if err := r.ParseForm(); err != nil {
  2140  			w.WriteHeader(http.StatusUnauthorized)
  2141  			t.Error("failed to parse form")
  2142  		}
  2143  		if got := r.PostForm.Get("service"); got != host {
  2144  			w.WriteHeader(http.StatusUnauthorized)
  2145  		}
  2146  		// handles refresh token requests
  2147  		if got := r.PostForm.Get("grant_type"); got != "refresh_token" {
  2148  			w.WriteHeader(http.StatusUnauthorized)
  2149  		}
  2150  		if got := r.PostForm.Get("scope"); got != scope {
  2151  			w.WriteHeader(http.StatusUnauthorized)
  2152  		}
  2153  		if got := r.PostForm.Get("refresh_token"); got != testRefreshToken {
  2154  			w.WriteHeader(http.StatusUnauthorized)
  2155  		}
  2156  		// writes back access token
  2157  		if _, err := fmt.Fprintf(w, `{"access_token":%q}`, testAccessToken); err != nil {
  2158  			t.Fatalf("could not write back access token, error = %v", err)
  2159  		}
  2160  	}))
  2161  	defer as.Close()
  2162  
  2163  	// create a test server
  2164  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  2165  		path := r.URL.Path
  2166  		if r.Method != http.MethodGet {
  2167  			w.WriteHeader(http.StatusNotFound)
  2168  			panic("unexpected access")
  2169  		}
  2170  		switch path {
  2171  		case "/refreshToken":
  2172  			wantedAuthHeader := "Bearer " + testAccessToken
  2173  			if auth := r.Header.Get("Authorization"); auth != wantedAuthHeader {
  2174  				challenge := fmt.Sprintf("Bearer realm=%q,service=%q,scope=%q", as.URL, host, scope)
  2175  				w.Header().Set("Www-Authenticate", challenge)
  2176  				w.WriteHeader(http.StatusUnauthorized)
  2177  			}
  2178  		default:
  2179  			w.WriteHeader(http.StatusNotAcceptable)
  2180  		}
  2181  	}))
  2182  	defer ts.Close()
  2183  	host = ts.URL
  2184  	uri, _ := url.Parse(host)
  2185  	hostAddress := uri.Host
  2186  	refreshTokenURL := fmt.Sprintf("%s/refreshToken", host)
  2187  
  2188  	// create a test client with the correct credentials
  2189  	clientValid := &Client{
  2190  		Credential: StaticCredential(hostAddress, Credential{
  2191  			RefreshToken: testRefreshToken,
  2192  		}),
  2193  	}
  2194  	req, err := http.NewRequest(http.MethodGet, refreshTokenURL, nil)
  2195  	if err != nil {
  2196  		t.Fatalf("could not create request, err = %v", err)
  2197  	}
  2198  	respValid, err := clientValid.Do(req)
  2199  	if err != nil {
  2200  		t.Fatalf("could not send request, err = %v", err)
  2201  	}
  2202  	if respValid.StatusCode != 200 {
  2203  		t.Errorf("incorrect status code: %d, expected 200", respValid.StatusCode)
  2204  	}
  2205  
  2206  	// create a test client with incorrect credentials
  2207  	clientInvalid := &Client{
  2208  		Credential: StaticCredential(hostAddress, Credential{
  2209  			RefreshToken: "bar",
  2210  		}),
  2211  	}
  2212  	_, err = clientInvalid.Do(req)
  2213  
  2214  	var expectedError *errcode.ErrorResponse
  2215  	if !errors.As(err, &expectedError) || expectedError.StatusCode != http.StatusUnauthorized {
  2216  		t.Errorf("incorrect error: %v, expected %v", err, expectedError)
  2217  	}
  2218  }
  2219  
  2220  func TestClient_StaticCredential_registryMismatch(t *testing.T) {
  2221  	testUsername := "username"
  2222  	testPassword := "password"
  2223  	targetAddress := "target/address"
  2224  
  2225  	client := &Client{
  2226  		Credential: StaticCredential(targetAddress, Credential{
  2227  			Username: testUsername,
  2228  			Password: testPassword,
  2229  		}),
  2230  	}
  2231  
  2232  	cred, err := client.Credential(context.Background(), "registry/mismatched")
  2233  	if cred != EmptyCredential {
  2234  		t.Errorf("Credential() = %v, want = %v", cred, EmptyCredential)
  2235  	}
  2236  	if err != nil {
  2237  		t.Errorf("got error = %v, expected error = %v", err, nil)
  2238  	}
  2239  }