github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/cliconfig/credentials/native_store_test.go (about)

     1  package credentials
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/docker/engine-api/types"
    12  )
    13  
    14  const (
    15  	validServerAddress   = "https://index.docker.io/v1"
    16  	validServerAddress2  = "https://example.com:5002"
    17  	invalidServerAddress = "https://foobar.example.com"
    18  	missingCredsAddress  = "https://missing.docker.io/v1"
    19  )
    20  
    21  var errCommandExited = fmt.Errorf("exited 1")
    22  
    23  // mockCommand simulates interactions between the docker client and a remote
    24  // credentials helper.
    25  // Unit tests inject this mocked command into the remote to control execution.
    26  type mockCommand struct {
    27  	arg   string
    28  	input io.Reader
    29  }
    30  
    31  // Output returns responses from the remote credentials helper.
    32  // It mocks those responses based in the input in the mock.
    33  func (m *mockCommand) Output() ([]byte, error) {
    34  	in, err := ioutil.ReadAll(m.input)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	inS := string(in)
    39  
    40  	switch m.arg {
    41  	case "erase":
    42  		switch inS {
    43  		case validServerAddress:
    44  			return nil, nil
    45  		default:
    46  			return []byte("error erasing credentials"), errCommandExited
    47  		}
    48  	case "get":
    49  		switch inS {
    50  		case validServerAddress:
    51  			return []byte(`{"Username": "foo", "Secret": "bar"}`), nil
    52  		case validServerAddress2:
    53  			return []byte(`{"Username": "<token>", "Secret": "abcd1234"}`), nil
    54  		case missingCredsAddress:
    55  			return []byte(errCredentialsNotFound.Error()), errCommandExited
    56  		case invalidServerAddress:
    57  			return []byte("error getting credentials"), errCommandExited
    58  		}
    59  	case "store":
    60  		var c credentialsRequest
    61  		err := json.NewDecoder(strings.NewReader(inS)).Decode(&c)
    62  		if err != nil {
    63  			return []byte("error storing credentials"), errCommandExited
    64  		}
    65  		switch c.ServerURL {
    66  		case validServerAddress:
    67  			return nil, nil
    68  		default:
    69  			return []byte("error storing credentials"), errCommandExited
    70  		}
    71  	}
    72  
    73  	return []byte(fmt.Sprintf("unknown argument %q with %q", m.arg, inS)), errCommandExited
    74  }
    75  
    76  // Input sets the input to send to a remote credentials helper.
    77  func (m *mockCommand) Input(in io.Reader) {
    78  	m.input = in
    79  }
    80  
    81  func mockCommandFn(args ...string) command {
    82  	return &mockCommand{
    83  		arg: args[0],
    84  	}
    85  }
    86  
    87  func TestNativeStoreAddCredentials(t *testing.T) {
    88  	f := newConfigFile(make(map[string]types.AuthConfig))
    89  	f.CredentialsStore = "mock"
    90  
    91  	s := &nativeStore{
    92  		commandFn: mockCommandFn,
    93  		fileStore: NewFileStore(f),
    94  	}
    95  	err := s.Store(types.AuthConfig{
    96  		Username:      "foo",
    97  		Password:      "bar",
    98  		Email:         "foo@example.com",
    99  		ServerAddress: validServerAddress,
   100  	})
   101  
   102  	if err != nil {
   103  		t.Fatal(err)
   104  	}
   105  
   106  	if len(f.AuthConfigs) != 1 {
   107  		t.Fatalf("expected 1 auth config, got %d", len(f.AuthConfigs))
   108  	}
   109  
   110  	a, ok := f.AuthConfigs[validServerAddress]
   111  	if !ok {
   112  		t.Fatalf("expected auth for %s, got %v", validServerAddress, f.AuthConfigs)
   113  	}
   114  	if a.Auth != "" {
   115  		t.Fatalf("expected auth to be empty, got %s", a.Auth)
   116  	}
   117  	if a.Username != "" {
   118  		t.Fatalf("expected username to be empty, got %s", a.Username)
   119  	}
   120  	if a.Password != "" {
   121  		t.Fatalf("expected password to be empty, got %s", a.Password)
   122  	}
   123  	if a.IdentityToken != "" {
   124  		t.Fatalf("expected identity token to be empty, got %s", a.IdentityToken)
   125  	}
   126  	if a.Email != "foo@example.com" {
   127  		t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
   128  	}
   129  }
   130  
   131  func TestNativeStoreAddInvalidCredentials(t *testing.T) {
   132  	f := newConfigFile(make(map[string]types.AuthConfig))
   133  	f.CredentialsStore = "mock"
   134  
   135  	s := &nativeStore{
   136  		commandFn: mockCommandFn,
   137  		fileStore: NewFileStore(f),
   138  	}
   139  	err := s.Store(types.AuthConfig{
   140  		Username:      "foo",
   141  		Password:      "bar",
   142  		Email:         "foo@example.com",
   143  		ServerAddress: invalidServerAddress,
   144  	})
   145  
   146  	if err == nil {
   147  		t.Fatal("expected error, got nil")
   148  	}
   149  
   150  	if err.Error() != "error storing credentials" {
   151  		t.Fatalf("expected `error storing credentials`, got %v", err)
   152  	}
   153  
   154  	if len(f.AuthConfigs) != 0 {
   155  		t.Fatalf("expected 0 auth config, got %d", len(f.AuthConfigs))
   156  	}
   157  }
   158  
   159  func TestNativeStoreGet(t *testing.T) {
   160  	f := newConfigFile(map[string]types.AuthConfig{
   161  		validServerAddress: {
   162  			Email: "foo@example.com",
   163  		},
   164  	})
   165  	f.CredentialsStore = "mock"
   166  
   167  	s := &nativeStore{
   168  		commandFn: mockCommandFn,
   169  		fileStore: NewFileStore(f),
   170  	}
   171  	a, err := s.Get(validServerAddress)
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  
   176  	if a.Username != "foo" {
   177  		t.Fatalf("expected username `foo`, got %s", a.Username)
   178  	}
   179  	if a.Password != "bar" {
   180  		t.Fatalf("expected password `bar`, got %s", a.Password)
   181  	}
   182  	if a.IdentityToken != "" {
   183  		t.Fatalf("expected identity token to be empty, got %s", a.IdentityToken)
   184  	}
   185  	if a.Email != "foo@example.com" {
   186  		t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
   187  	}
   188  }
   189  
   190  func TestNativeStoreGetIdentityToken(t *testing.T) {
   191  	f := newConfigFile(map[string]types.AuthConfig{
   192  		validServerAddress2: {
   193  			Email: "foo@example2.com",
   194  		},
   195  	})
   196  	f.CredentialsStore = "mock"
   197  
   198  	s := &nativeStore{
   199  		commandFn: mockCommandFn,
   200  		fileStore: NewFileStore(f),
   201  	}
   202  	a, err := s.Get(validServerAddress2)
   203  	if err != nil {
   204  		t.Fatal(err)
   205  	}
   206  
   207  	if a.Username != "" {
   208  		t.Fatalf("expected username to be empty, got %s", a.Username)
   209  	}
   210  	if a.Password != "" {
   211  		t.Fatalf("expected password to be empty, got %s", a.Password)
   212  	}
   213  	if a.IdentityToken != "abcd1234" {
   214  		t.Fatalf("expected identity token `abcd1234`, got %s", a.IdentityToken)
   215  	}
   216  	if a.Email != "foo@example2.com" {
   217  		t.Fatalf("expected email `foo@example2.com`, got %s", a.Email)
   218  	}
   219  }
   220  
   221  func TestNativeStoreGetAll(t *testing.T) {
   222  	f := newConfigFile(map[string]types.AuthConfig{
   223  		validServerAddress: {
   224  			Email: "foo@example.com",
   225  		},
   226  		validServerAddress2: {
   227  			Email: "foo@example2.com",
   228  		},
   229  	})
   230  	f.CredentialsStore = "mock"
   231  
   232  	s := &nativeStore{
   233  		commandFn: mockCommandFn,
   234  		fileStore: NewFileStore(f),
   235  	}
   236  	as, err := s.GetAll()
   237  	if err != nil {
   238  		t.Fatal(err)
   239  	}
   240  
   241  	if len(as) != 2 {
   242  		t.Fatalf("wanted 2, got %d", len(as))
   243  	}
   244  
   245  	if as[validServerAddress].Username != "foo" {
   246  		t.Fatalf("expected username `foo` for %s, got %s", validServerAddress, as[validServerAddress].Username)
   247  	}
   248  	if as[validServerAddress].Password != "bar" {
   249  		t.Fatalf("expected password `bar` for %s, got %s", validServerAddress, as[validServerAddress].Password)
   250  	}
   251  	if as[validServerAddress].IdentityToken != "" {
   252  		t.Fatalf("expected identity to be empty for %s, got %s", validServerAddress, as[validServerAddress].IdentityToken)
   253  	}
   254  	if as[validServerAddress].Email != "foo@example.com" {
   255  		t.Fatalf("expected email `foo@example.com` for %s, got %s", validServerAddress, as[validServerAddress].Email)
   256  	}
   257  	if as[validServerAddress2].Username != "" {
   258  		t.Fatalf("expected username to be empty for %s, got %s", validServerAddress2, as[validServerAddress2].Username)
   259  	}
   260  	if as[validServerAddress2].Password != "" {
   261  		t.Fatalf("expected password to be empty for %s, got %s", validServerAddress2, as[validServerAddress2].Password)
   262  	}
   263  	if as[validServerAddress2].IdentityToken != "abcd1234" {
   264  		t.Fatalf("expected identity token `abcd1324` for %s, got %s", validServerAddress2, as[validServerAddress2].IdentityToken)
   265  	}
   266  	if as[validServerAddress2].Email != "foo@example2.com" {
   267  		t.Fatalf("expected email `foo@example2.com` for %s, got %s", validServerAddress2, as[validServerAddress2].Email)
   268  	}
   269  }
   270  
   271  func TestNativeStoreGetMissingCredentials(t *testing.T) {
   272  	f := newConfigFile(map[string]types.AuthConfig{
   273  		validServerAddress: {
   274  			Email: "foo@example.com",
   275  		},
   276  	})
   277  	f.CredentialsStore = "mock"
   278  
   279  	s := &nativeStore{
   280  		commandFn: mockCommandFn,
   281  		fileStore: NewFileStore(f),
   282  	}
   283  	_, err := s.Get(missingCredsAddress)
   284  	if err != nil {
   285  		// missing credentials do not produce an error
   286  		t.Fatal(err)
   287  	}
   288  }
   289  
   290  func TestNativeStoreGetInvalidAddress(t *testing.T) {
   291  	f := newConfigFile(map[string]types.AuthConfig{
   292  		validServerAddress: {
   293  			Email: "foo@example.com",
   294  		},
   295  	})
   296  	f.CredentialsStore = "mock"
   297  
   298  	s := &nativeStore{
   299  		commandFn: mockCommandFn,
   300  		fileStore: NewFileStore(f),
   301  	}
   302  	_, err := s.Get(invalidServerAddress)
   303  	if err == nil {
   304  		t.Fatal("expected error, got nil")
   305  	}
   306  
   307  	if err.Error() != "error getting credentials" {
   308  		t.Fatalf("expected `error getting credentials`, got %v", err)
   309  	}
   310  }
   311  
   312  func TestNativeStoreErase(t *testing.T) {
   313  	f := newConfigFile(map[string]types.AuthConfig{
   314  		validServerAddress: {
   315  			Email: "foo@example.com",
   316  		},
   317  	})
   318  	f.CredentialsStore = "mock"
   319  
   320  	s := &nativeStore{
   321  		commandFn: mockCommandFn,
   322  		fileStore: NewFileStore(f),
   323  	}
   324  	err := s.Erase(validServerAddress)
   325  	if err != nil {
   326  		t.Fatal(err)
   327  	}
   328  
   329  	if len(f.AuthConfigs) != 0 {
   330  		t.Fatalf("expected 0 auth configs, got %d", len(f.AuthConfigs))
   331  	}
   332  }
   333  
   334  func TestNativeStoreEraseInvalidAddress(t *testing.T) {
   335  	f := newConfigFile(map[string]types.AuthConfig{
   336  		validServerAddress: {
   337  			Email: "foo@example.com",
   338  		},
   339  	})
   340  	f.CredentialsStore = "mock"
   341  
   342  	s := &nativeStore{
   343  		commandFn: mockCommandFn,
   344  		fileStore: NewFileStore(f),
   345  	}
   346  	err := s.Erase(invalidServerAddress)
   347  	if err == nil {
   348  		t.Fatal("expected error, got nil")
   349  	}
   350  
   351  	if err.Error() != "error erasing credentials" {
   352  		t.Fatalf("expected `error erasing credentials`, got %v", err)
   353  	}
   354  }