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 }