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 }