...

Source file src/github.com/docker/docker-credential-helpers/osxkeychain/osxkeychain_test.go

Documentation: github.com/docker/docker-credential-helpers/osxkeychain

     1  //go:build darwin && cgo
     2  
     3  package osxkeychain
     4  
     5  import (
     6  	"fmt"
     7  	"testing"
     8  
     9  	"github.com/docker/docker-credential-helpers/credentials"
    10  )
    11  
    12  func TestOSXKeychainHelper(t *testing.T) {
    13  	creds := &credentials.Credentials{
    14  		ServerURL: "https://foobar.example.com:2376/v1",
    15  		Username:  "foobar",
    16  		Secret:    "foobarbaz",
    17  	}
    18  	creds1 := &credentials.Credentials{
    19  		ServerURL: "https://foobar.example.com:2376/v2",
    20  		Username:  "foobarbaz",
    21  		Secret:    "foobar",
    22  	}
    23  	helper := Osxkeychain{}
    24  	if err := helper.Add(creds); err != nil {
    25  		t.Fatal(err)
    26  	}
    27  
    28  	username, secret, err := helper.Get(creds.ServerURL)
    29  	if err != nil {
    30  		t.Fatal(err)
    31  	}
    32  
    33  	if username != "foobar" {
    34  		t.Fatalf("expected %s, got %s\n", "foobar", username)
    35  	}
    36  
    37  	if secret != "foobarbaz" {
    38  		t.Fatalf("expected %s, got %s\n", "foobarbaz", secret)
    39  	}
    40  
    41  	auths, err := helper.List()
    42  	if err != nil || len(auths) == 0 {
    43  		t.Fatal(err)
    44  	}
    45  
    46  	helper.Add(creds1)
    47  	defer helper.Delete(creds1.ServerURL)
    48  	newauths, err := helper.List()
    49  	if len(newauths)-len(auths) != 1 {
    50  		if err == nil {
    51  			t.Fatalf("Error: len(newauths): %d, len(auths): %d", len(newauths), len(auths))
    52  		}
    53  		t.Fatalf("Error: len(newauths): %d, len(auths): %d\n Error= %v", len(newauths), len(auths), err)
    54  	}
    55  
    56  	if err := helper.Delete(creds.ServerURL); err != nil {
    57  		t.Fatal(err)
    58  	}
    59  }
    60  
    61  // TestOSXKeychainHelperRetrieveAliases verifies that secrets can be accessed
    62  // through variations on the URL
    63  func TestOSXKeychainHelperRetrieveAliases(t *testing.T) {
    64  	tests := []struct {
    65  		doc      string
    66  		storeURL string
    67  		readURL  string
    68  	}{
    69  		{
    70  			doc:      "stored with port, retrieved without",
    71  			storeURL: "https://foobar.example.com:2376",
    72  			readURL:  "https://foobar.example.com",
    73  		},
    74  		{
    75  			doc:      "stored as https, retrieved without scheme",
    76  			storeURL: "https://foobar.example.com:2376",
    77  			readURL:  "foobar.example.com",
    78  		},
    79  		{
    80  			doc:      "stored with path, retrieved without",
    81  			storeURL: "https://foobar.example.com:1234/one/two",
    82  			readURL:  "https://foobar.example.com:1234",
    83  		},
    84  	}
    85  
    86  	helper := Osxkeychain{}
    87  	t.Cleanup(func() {
    88  		for _, tc := range tests {
    89  			if err := helper.Delete(tc.storeURL); err != nil && !credentials.IsErrCredentialsNotFound(err) {
    90  				t.Errorf("cleanup: failed to delete '%s': %v", tc.storeURL, err)
    91  			}
    92  		}
    93  	})
    94  
    95  	// Clean store before testing.
    96  	for _, tc := range tests {
    97  		if err := helper.Delete(tc.storeURL); err != nil && !credentials.IsErrCredentialsNotFound(err) {
    98  			t.Errorf("prepare: failed to delete '%s': %v", tc.storeURL, err)
    99  		}
   100  	}
   101  
   102  	for _, tc := range tests {
   103  		tc := tc
   104  		t.Run(tc.doc, func(t *testing.T) {
   105  			c := &credentials.Credentials{ServerURL: tc.storeURL, Username: "hello", Secret: "world"}
   106  			if err := helper.Add(c); err != nil {
   107  				t.Fatalf("Error: failed to store secret for URL %q: %s", tc.storeURL, err)
   108  			}
   109  			if _, _, err := helper.Get(tc.readURL); err != nil {
   110  				t.Errorf("Error: failed to read secret for URL %q using %q", tc.storeURL, tc.readURL)
   111  			}
   112  			if err := helper.Delete(tc.storeURL); err != nil {
   113  				t.Error(err)
   114  			}
   115  		})
   116  	}
   117  }
   118  
   119  // TestOSXKeychainHelperRetrieveStrict verifies that only matching secrets are
   120  // returned.
   121  func TestOSXKeychainHelperRetrieveStrict(t *testing.T) {
   122  	tests := []struct {
   123  		doc      string
   124  		storeURL string
   125  		readURL  string
   126  	}{
   127  		{
   128  			doc:      "stored as https, retrieved using http",
   129  			storeURL: "https://foobar.example.com:2376",
   130  			readURL:  "http://foobar.example.com:2376",
   131  		},
   132  		{
   133  			doc:      "stored as http, retrieved using https",
   134  			storeURL: "http://foobar.example.com:2376",
   135  			readURL:  "https://foobar.example.com:2376",
   136  		},
   137  		{
   138  			// stored as http, retrieved without a scheme specified (hence, using the default https://)
   139  			doc:      "stored as http, retrieved without scheme",
   140  			storeURL: "http://foobar.example.com",
   141  			readURL:  "foobar.example.com:5678",
   142  		},
   143  		{
   144  			doc:      "non-matching ports",
   145  			storeURL: "https://foobar.example.com:1234",
   146  			readURL:  "https://foobar.example.com:5678",
   147  		},
   148  		// TODO: is this desired behavior? The other way round does work
   149  		// {
   150  		// 	doc:      "non-matching ports (stored without port)",
   151  		// 	storeURL: "https://foobar.example.com",
   152  		// 	readURL:  "https://foobar.example.com:5678",
   153  		// },
   154  		{
   155  			doc:      "non-matching paths",
   156  			storeURL: "https://foobar.example.com:1234/one/two",
   157  			readURL:  "https://foobar.example.com:1234/five/six",
   158  		},
   159  	}
   160  
   161  	helper := Osxkeychain{}
   162  	t.Cleanup(func() {
   163  		for _, tc := range tests {
   164  			if err := helper.Delete(tc.storeURL); err != nil && !credentials.IsErrCredentialsNotFound(err) {
   165  				t.Errorf("cleanup: failed to delete '%s': %v", tc.storeURL, err)
   166  			}
   167  		}
   168  	})
   169  
   170  	// Clean store before testing.
   171  	for _, tc := range tests {
   172  		if err := helper.Delete(tc.storeURL); err != nil && !credentials.IsErrCredentialsNotFound(err) {
   173  			t.Errorf("prepare: failed to delete '%s': %v", tc.storeURL, err)
   174  		}
   175  	}
   176  
   177  	for _, tc := range tests {
   178  		tc := tc
   179  		t.Run(tc.doc, func(t *testing.T) {
   180  			c := &credentials.Credentials{ServerURL: tc.storeURL, Username: "hello", Secret: "world"}
   181  			if err := helper.Add(c); err != nil {
   182  				t.Fatalf("Error: failed to store secret for URL %q: %s", tc.storeURL, err)
   183  			}
   184  			if _, _, err := helper.Get(tc.readURL); err == nil {
   185  				t.Errorf("Error: managed to read secret for URL %q using %q, but should not be able to", tc.storeURL, tc.readURL)
   186  			}
   187  			if err := helper.Delete(tc.storeURL); err != nil {
   188  				t.Error(err)
   189  			}
   190  		})
   191  	}
   192  }
   193  
   194  // TestOSXKeychainHelperStoreRetrieve verifies that secrets stored in the
   195  // the keychain can be read back using the URL that was used to store them.
   196  func TestOSXKeychainHelperStoreRetrieve(t *testing.T) {
   197  	tests := []struct {
   198  		url string
   199  	}{
   200  		{url: "foobar.example.com"},
   201  		{url: "foobar.example.com:2376"},
   202  		{url: "//foobar.example.com:2376"},
   203  		{url: "https://foobar.example.com:2376"},
   204  		{url: "http://foobar.example.com:2376"},
   205  		{url: "https://foobar.example.com:2376/some/path"},
   206  		{url: "https://foobar.example.com:2376/some/other/path"},
   207  		{url: "https://foobar.example.com:2376/some/other/path?foo=bar"},
   208  	}
   209  
   210  	helper := Osxkeychain{}
   211  	t.Cleanup(func() {
   212  		for _, tc := range tests {
   213  			if err := helper.Delete(tc.url); err != nil && !credentials.IsErrCredentialsNotFound(err) {
   214  				t.Errorf("cleanup: failed to delete '%s': %v", tc.url, err)
   215  			}
   216  		}
   217  	})
   218  
   219  	// Clean store before testing.
   220  	for _, tc := range tests {
   221  		if err := helper.Delete(tc.url); err != nil && !credentials.IsErrCredentialsNotFound(err) {
   222  			t.Errorf("prepare: failed to delete '%s': %v", tc.url, err)
   223  		}
   224  	}
   225  
   226  	// Note that we don't delete between individual tests here, to verify that
   227  	// subsequent stores/overwrites don't affect storing / retrieving secrets.
   228  	for i, tc := range tests {
   229  		tc := tc
   230  		t.Run(tc.url, func(t *testing.T) {
   231  			c := &credentials.Credentials{
   232  				ServerURL: tc.url,
   233  				Username:  fmt.Sprintf("user-%d", i),
   234  				Secret:    fmt.Sprintf("secret-%d", i),
   235  			}
   236  
   237  			if err := helper.Add(c); err != nil {
   238  				t.Fatalf("Error: failed to store secret for URL: %s: %s", tc.url, err)
   239  			}
   240  			user, secret, err := helper.Get(tc.url)
   241  			if err != nil {
   242  				t.Fatalf("Error: failed to read secret for URL %q: %s", tc.url, err)
   243  			}
   244  			if user != c.Username {
   245  				t.Errorf("Error: expected username %s, got username %s for URL: %s", c.Username, user, tc.url)
   246  			}
   247  			if secret != c.Secret {
   248  				t.Errorf("Error: expected secret %s, got secret %s for URL: %s", c.Secret, secret, tc.url)
   249  			}
   250  		})
   251  	}
   252  }
   253  
   254  func TestMissingCredentials(t *testing.T) {
   255  	const nonExistingCred = "https://adsfasdf.invalid/asdfsdddd"
   256  	helper := Osxkeychain{}
   257  	_, _, err := helper.Get(nonExistingCred)
   258  	if !credentials.IsErrCredentialsNotFound(err) {
   259  		t.Errorf("expected ErrCredentialsNotFound, got %v", err)
   260  	}
   261  	err = helper.Delete(nonExistingCred)
   262  	if !credentials.IsErrCredentialsNotFound(err) {
   263  		t.Errorf("expected ErrCredentialsNotFound, got %v", err)
   264  	}
   265  }
   266  

View as plain text