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