123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- package credentials
- import (
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "strings"
- "testing"
- "github.com/docker/docker-credential-helpers/client"
- "github.com/docker/docker-credential-helpers/credentials"
- "github.com/docker/engine-api/types"
- )
- const (
- validServerAddress = "https://index.docker.io/v1"
- validServerAddress2 = "https://example.com:5002"
- invalidServerAddress = "https://foobar.example.com"
- missingCredsAddress = "https://missing.docker.io/v1"
- )
- var errCommandExited = fmt.Errorf("exited 1")
- // mockCommand simulates interactions between the docker client and a remote
- // credentials helper.
- // Unit tests inject this mocked command into the remote to control execution.
- type mockCommand struct {
- arg string
- input io.Reader
- }
- // Output returns responses from the remote credentials helper.
- // It mocks those responses based in the input in the mock.
- func (m *mockCommand) Output() ([]byte, error) {
- in, err := ioutil.ReadAll(m.input)
- if err != nil {
- return nil, err
- }
- inS := string(in)
- switch m.arg {
- case "erase":
- switch inS {
- case validServerAddress:
- return nil, nil
- default:
- return []byte("program failed"), errCommandExited
- }
- case "get":
- switch inS {
- case validServerAddress:
- return []byte(`{"Username": "foo", "Secret": "bar"}`), nil
- case validServerAddress2:
- return []byte(`{"Username": "<token>", "Secret": "abcd1234"}`), nil
- case missingCredsAddress:
- return []byte(credentials.NewErrCredentialsNotFound().Error()), errCommandExited
- case invalidServerAddress:
- return []byte("program failed"), errCommandExited
- }
- case "store":
- var c credentials.Credentials
- err := json.NewDecoder(strings.NewReader(inS)).Decode(&c)
- if err != nil {
- return []byte("program failed"), errCommandExited
- }
- switch c.ServerURL {
- case validServerAddress:
- return nil, nil
- default:
- return []byte("program failed"), errCommandExited
- }
- }
- return []byte(fmt.Sprintf("unknown argument %q with %q", m.arg, inS)), errCommandExited
- }
- // Input sets the input to send to a remote credentials helper.
- func (m *mockCommand) Input(in io.Reader) {
- m.input = in
- }
- func mockCommandFn(args ...string) client.Program {
- return &mockCommand{
- arg: args[0],
- }
- }
- func TestNativeStoreAddCredentials(t *testing.T) {
- f := newConfigFile(make(map[string]types.AuthConfig))
- f.CredentialsStore = "mock"
- s := &nativeStore{
- programFunc: mockCommandFn,
- fileStore: NewFileStore(f),
- }
- err := s.Store(types.AuthConfig{
- Username: "foo",
- Password: "bar",
- Email: "foo@example.com",
- ServerAddress: validServerAddress,
- })
- if err != nil {
- t.Fatal(err)
- }
- if len(f.AuthConfigs) != 1 {
- t.Fatalf("expected 1 auth config, got %d", len(f.AuthConfigs))
- }
- a, ok := f.AuthConfigs[validServerAddress]
- if !ok {
- t.Fatalf("expected auth for %s, got %v", validServerAddress, f.AuthConfigs)
- }
- if a.Auth != "" {
- t.Fatalf("expected auth to be empty, got %s", a.Auth)
- }
- if a.Username != "" {
- t.Fatalf("expected username to be empty, got %s", a.Username)
- }
- if a.Password != "" {
- t.Fatalf("expected password to be empty, got %s", a.Password)
- }
- if a.IdentityToken != "" {
- t.Fatalf("expected identity token to be empty, got %s", a.IdentityToken)
- }
- if a.Email != "foo@example.com" {
- t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
- }
- }
- func TestNativeStoreAddInvalidCredentials(t *testing.T) {
- f := newConfigFile(make(map[string]types.AuthConfig))
- f.CredentialsStore = "mock"
- s := &nativeStore{
- programFunc: mockCommandFn,
- fileStore: NewFileStore(f),
- }
- err := s.Store(types.AuthConfig{
- Username: "foo",
- Password: "bar",
- Email: "foo@example.com",
- ServerAddress: invalidServerAddress,
- })
- if err == nil {
- t.Fatal("expected error, got nil")
- }
- if !strings.Contains(err.Error(), "program failed") {
- t.Fatalf("expected `program failed`, got %v", err)
- }
- if len(f.AuthConfigs) != 0 {
- t.Fatalf("expected 0 auth config, got %d", len(f.AuthConfigs))
- }
- }
- func TestNativeStoreGet(t *testing.T) {
- f := newConfigFile(map[string]types.AuthConfig{
- validServerAddress: {
- Email: "foo@example.com",
- },
- })
- f.CredentialsStore = "mock"
- s := &nativeStore{
- programFunc: mockCommandFn,
- fileStore: NewFileStore(f),
- }
- a, err := s.Get(validServerAddress)
- if err != nil {
- t.Fatal(err)
- }
- if a.Username != "foo" {
- t.Fatalf("expected username `foo`, got %s", a.Username)
- }
- if a.Password != "bar" {
- t.Fatalf("expected password `bar`, got %s", a.Password)
- }
- if a.IdentityToken != "" {
- t.Fatalf("expected identity token to be empty, got %s", a.IdentityToken)
- }
- if a.Email != "foo@example.com" {
- t.Fatalf("expected email `foo@example.com`, got %s", a.Email)
- }
- }
- func TestNativeStoreGetIdentityToken(t *testing.T) {
- f := newConfigFile(map[string]types.AuthConfig{
- validServerAddress2: {
- Email: "foo@example2.com",
- },
- })
- f.CredentialsStore = "mock"
- s := &nativeStore{
- programFunc: mockCommandFn,
- fileStore: NewFileStore(f),
- }
- a, err := s.Get(validServerAddress2)
- if err != nil {
- t.Fatal(err)
- }
- if a.Username != "" {
- t.Fatalf("expected username to be empty, got %s", a.Username)
- }
- if a.Password != "" {
- t.Fatalf("expected password to be empty, got %s", a.Password)
- }
- if a.IdentityToken != "abcd1234" {
- t.Fatalf("expected identity token `abcd1234`, got %s", a.IdentityToken)
- }
- if a.Email != "foo@example2.com" {
- t.Fatalf("expected email `foo@example2.com`, got %s", a.Email)
- }
- }
- func TestNativeStoreGetAll(t *testing.T) {
- f := newConfigFile(map[string]types.AuthConfig{
- validServerAddress: {
- Email: "foo@example.com",
- },
- validServerAddress2: {
- Email: "foo@example2.com",
- },
- })
- f.CredentialsStore = "mock"
- s := &nativeStore{
- programFunc: mockCommandFn,
- fileStore: NewFileStore(f),
- }
- as, err := s.GetAll()
- if err != nil {
- t.Fatal(err)
- }
- if len(as) != 2 {
- t.Fatalf("wanted 2, got %d", len(as))
- }
- if as[validServerAddress].Username != "foo" {
- t.Fatalf("expected username `foo` for %s, got %s", validServerAddress, as[validServerAddress].Username)
- }
- if as[validServerAddress].Password != "bar" {
- t.Fatalf("expected password `bar` for %s, got %s", validServerAddress, as[validServerAddress].Password)
- }
- if as[validServerAddress].IdentityToken != "" {
- t.Fatalf("expected identity to be empty for %s, got %s", validServerAddress, as[validServerAddress].IdentityToken)
- }
- if as[validServerAddress].Email != "foo@example.com" {
- t.Fatalf("expected email `foo@example.com` for %s, got %s", validServerAddress, as[validServerAddress].Email)
- }
- if as[validServerAddress2].Username != "" {
- t.Fatalf("expected username to be empty for %s, got %s", validServerAddress2, as[validServerAddress2].Username)
- }
- if as[validServerAddress2].Password != "" {
- t.Fatalf("expected password to be empty for %s, got %s", validServerAddress2, as[validServerAddress2].Password)
- }
- if as[validServerAddress2].IdentityToken != "abcd1234" {
- t.Fatalf("expected identity token `abcd1324` for %s, got %s", validServerAddress2, as[validServerAddress2].IdentityToken)
- }
- if as[validServerAddress2].Email != "foo@example2.com" {
- t.Fatalf("expected email `foo@example2.com` for %s, got %s", validServerAddress2, as[validServerAddress2].Email)
- }
- }
- func TestNativeStoreGetMissingCredentials(t *testing.T) {
- f := newConfigFile(map[string]types.AuthConfig{
- validServerAddress: {
- Email: "foo@example.com",
- },
- })
- f.CredentialsStore = "mock"
- s := &nativeStore{
- programFunc: mockCommandFn,
- fileStore: NewFileStore(f),
- }
- _, err := s.Get(missingCredsAddress)
- if err != nil {
- // missing credentials do not produce an error
- t.Fatal(err)
- }
- }
- func TestNativeStoreGetInvalidAddress(t *testing.T) {
- f := newConfigFile(map[string]types.AuthConfig{
- validServerAddress: {
- Email: "foo@example.com",
- },
- })
- f.CredentialsStore = "mock"
- s := &nativeStore{
- programFunc: mockCommandFn,
- fileStore: NewFileStore(f),
- }
- _, err := s.Get(invalidServerAddress)
- if err == nil {
- t.Fatal("expected error, got nil")
- }
- if !strings.Contains(err.Error(), "program failed") {
- t.Fatalf("expected `program failed`, got %v", err)
- }
- }
- func TestNativeStoreErase(t *testing.T) {
- f := newConfigFile(map[string]types.AuthConfig{
- validServerAddress: {
- Email: "foo@example.com",
- },
- })
- f.CredentialsStore = "mock"
- s := &nativeStore{
- programFunc: mockCommandFn,
- fileStore: NewFileStore(f),
- }
- err := s.Erase(validServerAddress)
- if err != nil {
- t.Fatal(err)
- }
- if len(f.AuthConfigs) != 0 {
- t.Fatalf("expected 0 auth configs, got %d", len(f.AuthConfigs))
- }
- }
- func TestNativeStoreEraseInvalidAddress(t *testing.T) {
- f := newConfigFile(map[string]types.AuthConfig{
- validServerAddress: {
- Email: "foo@example.com",
- },
- })
- f.CredentialsStore = "mock"
- s := &nativeStore{
- programFunc: mockCommandFn,
- fileStore: NewFileStore(f),
- }
- err := s.Erase(invalidServerAddress)
- if err == nil {
- t.Fatal("expected error, got nil")
- }
- if !strings.Contains(err.Error(), "program failed") {
- t.Fatalf("expected `program failed`, got %v", err)
- }
- }
|