github.com/nikkelma/oras-project_oras-go@v1.1.1-0.20220201001104-a75f6a419090/pkg/auth/docker/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 docker
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"net/http"
    23  	"os"
    24  	"path/filepath"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/distribution/distribution/v3/configuration"
    29  	"github.com/distribution/distribution/v3/registry"
    30  	_ "github.com/distribution/distribution/v3/registry/auth/htpasswd"
    31  	_ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
    32  	"github.com/phayes/freeport"
    33  	"github.com/stretchr/testify/suite"
    34  	"golang.org/x/crypto/bcrypt"
    35  
    36  	iface "oras.land/oras-go/pkg/auth"
    37  )
    38  
    39  var (
    40  	testConfig   = "test.config"
    41  	testHtpasswd = "test.htpasswd"
    42  	testUsername = "alice"
    43  	testPassword = "wonderland"
    44  )
    45  
    46  type DockerClientTestSuite struct {
    47  	suite.Suite
    48  	DockerRegistryHost string
    49  	Client             *Client
    50  	TempTestDir        string
    51  }
    52  
    53  func newContext() context.Context {
    54  	return context.Background()
    55  }
    56  
    57  func (suite *DockerClientTestSuite) SetupSuite() {
    58  	tempDir, err := ioutil.TempDir("", "oras_auth_docker_test")
    59  	suite.Nil(err, "no error creating temp directory for test")
    60  	suite.TempTestDir = tempDir
    61  
    62  	// Create client
    63  	client, err := NewClient(filepath.Join(suite.TempTestDir, testConfig))
    64  	suite.Nil(err, "no error creating client")
    65  	var ok bool
    66  	suite.Client, ok = client.(*Client)
    67  	suite.True(ok, "NewClient returns a *docker.Client inside")
    68  
    69  	// Create htpasswd file with bcrypt
    70  	secret, err := bcrypt.GenerateFromPassword([]byte(testPassword), bcrypt.DefaultCost)
    71  	suite.Nil(err, "no error generating bcrypt password for test htpasswd file")
    72  	authRecord := fmt.Sprintf("%s:%s\n", testUsername, string(secret))
    73  	htpasswdPath := filepath.Join(suite.TempTestDir, testHtpasswd)
    74  	err = ioutil.WriteFile(htpasswdPath, []byte(authRecord), 0644)
    75  	suite.Nil(err, "no error creating test htpasswd file")
    76  
    77  	// Registry config
    78  	config := &configuration.Configuration{}
    79  	port, err := freeport.GetFreePort()
    80  	suite.Nil(err, "no error finding free port for test registry")
    81  	suite.DockerRegistryHost = fmt.Sprintf("localhost:%d", port)
    82  	config.HTTP.Addr = fmt.Sprintf(":%d", port)
    83  	config.HTTP.DrainTimeout = time.Duration(10) * time.Second
    84  	config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}}
    85  	config.Auth = configuration.Auth{
    86  		"htpasswd": configuration.Parameters{
    87  			"realm": "localhost",
    88  			"path":  htpasswdPath,
    89  		},
    90  	}
    91  	dockerRegistry, err := registry.NewRegistry(context.Background(), config)
    92  	suite.Nil(err, "no error finding free port for test registry")
    93  
    94  	// Start Docker registry
    95  	go dockerRegistry.ListenAndServe()
    96  	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    97  	defer cancel()
    98  	for {
    99  		select {
   100  		case <-ctx.Done():
   101  			suite.FailNow("docker registry timed out")
   102  		default:
   103  		}
   104  		req, err := http.NewRequestWithContext(
   105  			ctx,
   106  			http.MethodGet,
   107  			fmt.Sprintf("http://%s/v2/", suite.DockerRegistryHost),
   108  			nil,
   109  		)
   110  		suite.Nil(err, "no error in generate a /v2/ request")
   111  		resp, err := http.DefaultClient.Do(req)
   112  		if err == nil {
   113  			resp.Body.Close()
   114  			break
   115  		}
   116  		time.Sleep(time.Second)
   117  	}
   118  }
   119  
   120  func (suite *DockerClientTestSuite) TearDownSuite() {
   121  	os.RemoveAll(suite.TempTestDir)
   122  }
   123  
   124  func (suite *DockerClientTestSuite) Test_0_Login() {
   125  	var err error
   126  
   127  	err = suite.Client.Login(newContext(), suite.DockerRegistryHost, "oscar", "opponent", false)
   128  	suite.NotNil(err, "error logging into registry with invalid credentials")
   129  
   130  	err = suite.Client.Login(newContext(), suite.DockerRegistryHost, testUsername, testPassword, false)
   131  	suite.Nil(err, "no error logging into registry with valid credentials")
   132  }
   133  
   134  func (suite *DockerClientTestSuite) Test_1_LoginWithOpts() {
   135  	var err error
   136  
   137  	opts := []iface.LoginOption{
   138  		iface.WithLoginContext(newContext()),
   139  		iface.WithLoginHostname(suite.DockerRegistryHost),
   140  		iface.WithLoginUsername("oscar"),
   141  		iface.WithLoginSecret("opponent"),
   142  	}
   143  	err = suite.Client.LoginWithOpts(opts...)
   144  	suite.NotNil(err, "error logging into registry with invalid credentials (LoginWithOpts)")
   145  
   146  	opts = []iface.LoginOption{
   147  		iface.WithLoginContext(newContext()),
   148  		iface.WithLoginHostname(suite.DockerRegistryHost),
   149  		iface.WithLoginUsername(testUsername),
   150  		iface.WithLoginSecret(testPassword),
   151  	}
   152  	err = suite.Client.LoginWithOpts(opts...)
   153  	suite.Nil(err, "no error logging into registry with valid credentials (LoginWithOpts)")
   154  }
   155  
   156  func (suite *DockerClientTestSuite) Test_2_Logout() {
   157  	var err error
   158  
   159  	err = suite.Client.Logout(newContext(), "non-existing-host:42")
   160  	suite.NotNil(err, "error logging out of registry that has no entry")
   161  
   162  	err = suite.Client.Logout(newContext(), suite.DockerRegistryHost)
   163  	suite.Nil(err, "no error logging out of registry")
   164  }
   165  
   166  func TestDockerClientTestSuite(t *testing.T) {
   167  	suite.Run(t, new(DockerClientTestSuite))
   168  }