This patch enables the CLI to natively pick up the `DOCKER_AUTH_CONFIG`
environment variable and use it as a credential store.
The `DOCKER_AUTH_CONFIG` value should be a JSON object and must store
the credentials in a base64 encoded string under the `auth` key.
Specifying additional fields will cause the parser to fail.
For example:
`printf "username:pat" | openssl base64 -A`
`export DOCKER_AUTH_CONFIG='{
"auths": {
"https://index.docker.io/v1/": {
"auth": "aGk6KTpkY2tyX3BhdF9oZWxsbw=="
}
}
}'`
Credentials stored in `DOCKER_AUTH_CONFIG` would take precedence over any
credential stored in the file store (`~/.docker/config.json`) or native store
(credential helper).
Destructive actions, such as deleting a credential would result in a noop if
found in the environment credential. Credentials found in the file or
native store would get removed.
Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
132 lines
4.5 KiB
Go
132 lines
4.5 KiB
Go
package memorystore
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/docker/cli/cli/config/types"
|
|
"gotest.tools/v3/assert"
|
|
is "gotest.tools/v3/assert/cmp"
|
|
)
|
|
|
|
func TestMemoryStore(t *testing.T) {
|
|
config := map[string]types.AuthConfig{
|
|
"https://example.test": {
|
|
Username: "something-something",
|
|
ServerAddress: "https://example.test",
|
|
Auth: "super_secret_token",
|
|
},
|
|
}
|
|
|
|
fallbackConfig := map[string]types.AuthConfig{
|
|
"https://only-in-file.example.test": {
|
|
Username: "something-something",
|
|
ServerAddress: "https://only-in-file.example.test",
|
|
Auth: "super_secret_token",
|
|
},
|
|
}
|
|
|
|
fallbackStore, err := New(WithAuthConfig(fallbackConfig))
|
|
assert.NilError(t, err)
|
|
|
|
memoryStore, err := New(WithAuthConfig(config), WithFallbackStore(fallbackStore))
|
|
assert.NilError(t, err)
|
|
|
|
t.Run("get credentials from memory store", func(t *testing.T) {
|
|
c, err := memoryStore.Get("https://example.test")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, c, config["https://example.test"])
|
|
})
|
|
|
|
t.Run("get credentials from fallback store", func(t *testing.T) {
|
|
c, err := memoryStore.Get("https://only-in-file.example.test")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, c, fallbackConfig["https://only-in-file.example.test"])
|
|
})
|
|
|
|
t.Run("storing credentials in memory store should also be in defined fallback store", func(t *testing.T) {
|
|
err := memoryStore.Store(types.AuthConfig{
|
|
Username: "not-in-store",
|
|
ServerAddress: "https://not-in-store.example.test",
|
|
Auth: "not-in-store_token",
|
|
})
|
|
assert.NilError(t, err)
|
|
c, err := memoryStore.Get("https://not-in-store.example.test")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, c.Username, "not-in-store")
|
|
assert.Equal(t, c.ServerAddress, "https://not-in-store.example.test")
|
|
assert.Equal(t, c.Auth, "not-in-store_token")
|
|
|
|
cc, err := fallbackStore.Get("https://not-in-store.example.test")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, cc.Username, "not-in-store")
|
|
assert.Equal(t, cc.ServerAddress, "https://not-in-store.example.test")
|
|
assert.Equal(t, cc.Auth, "not-in-store_token")
|
|
})
|
|
|
|
t.Run("delete credentials should remove credentials from memory store and fallback store", func(t *testing.T) {
|
|
err := memoryStore.Store(types.AuthConfig{
|
|
Username: "a-new-credential",
|
|
ServerAddress: "https://a-new-credential.example.test",
|
|
Auth: "a-new-credential_token",
|
|
})
|
|
assert.NilError(t, err)
|
|
err = memoryStore.Erase("https://a-new-credential.example.test")
|
|
assert.NilError(t, err)
|
|
_, err = memoryStore.Get("https://a-new-credential.example.test")
|
|
assert.Check(t, is.ErrorIs(err, errValueNotFound))
|
|
_, err = fallbackStore.Get("https://a-new-credential.example.test")
|
|
assert.Check(t, is.ErrorIs(err, errValueNotFound))
|
|
})
|
|
}
|
|
|
|
func TestMemoryStoreWithoutFallback(t *testing.T) {
|
|
config := map[string]types.AuthConfig{
|
|
"https://example.test": {
|
|
Username: "something-something",
|
|
ServerAddress: "https://example.test",
|
|
Auth: "super_secret_token",
|
|
},
|
|
}
|
|
|
|
memoryStore, err := New(WithAuthConfig(config))
|
|
assert.NilError(t, err)
|
|
|
|
t.Run("get credentials from memory store without fallback", func(t *testing.T) {
|
|
c, err := memoryStore.Get("https://example.test")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, c, config["https://example.test"])
|
|
})
|
|
|
|
t.Run("get non-existing credentials from memory store should error", func(t *testing.T) {
|
|
_, err := memoryStore.Get("https://not-in-store.example.test")
|
|
assert.Check(t, is.ErrorIs(err, errValueNotFound))
|
|
})
|
|
|
|
t.Run("case store credentials", func(t *testing.T) {
|
|
err := memoryStore.Store(types.AuthConfig{
|
|
Username: "not-in-store",
|
|
ServerAddress: "https://not-in-store.example.test",
|
|
Auth: "not-in-store_token",
|
|
})
|
|
assert.NilError(t, err)
|
|
c, err := memoryStore.Get("https://not-in-store.example.test")
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, c.Username, "not-in-store")
|
|
assert.Equal(t, c.ServerAddress, "https://not-in-store.example.test")
|
|
assert.Equal(t, c.Auth, "not-in-store_token")
|
|
})
|
|
|
|
t.Run("delete credentials should remove credentials from memory store", func(t *testing.T) {
|
|
err := memoryStore.Store(types.AuthConfig{
|
|
Username: "a-new-credential",
|
|
ServerAddress: "https://a-new-credential.example.test",
|
|
Auth: "a-new-credential_token",
|
|
})
|
|
assert.NilError(t, err)
|
|
err = memoryStore.Erase("https://a-new-credential.example.test")
|
|
assert.NilError(t, err)
|
|
_, err = memoryStore.Get("https://a-new-credential.example.test")
|
|
assert.Check(t, is.ErrorIs(err, errValueNotFound))
|
|
})
|
|
}
|