Merge pull request #4412 from thaJeztah/auth_use_config

cli/command: ResolveAuthConfig, GetDefaultAuthConfig: take ConfigFile as arg
This commit is contained in:
Sebastiaan van Stijn 2023-08-08 22:01:13 +02:00 committed by GitHub
commit 0c5eb94eb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 38 additions and 60 deletions

View File

@ -189,7 +189,7 @@ func (cli *DockerCli) ManifestStore() manifeststore.Store {
// registry // registry
func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.RegistryClient { func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.RegistryClient {
resolver := func(ctx context.Context, index *registry.IndexInfo) registry.AuthConfig { resolver := func(ctx context.Context, index *registry.IndexInfo) registry.AuthConfig {
return ResolveAuthConfig(ctx, cli, index) return ResolveAuthConfig(cli.ConfigFile(), index)
} }
return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure) return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure)
} }

View File

@ -114,7 +114,7 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, options *createOptio
// FIXME(thaJeztah): this is the only code-path that uses APIClient.ImageCreate. Rewrite this to use the regular "pull" code (or vice-versa). // FIXME(thaJeztah): this is the only code-path that uses APIClient.ImageCreate. Rewrite this to use the regular "pull" code (or vice-versa).
func pullImage(ctx context.Context, dockerCli command.Cli, image string, opts *createOptions) error { func pullImage(ctx context.Context, dockerCli command.Cli, image string, opts *createOptions) error {
encodedAuth, err := command.RetrieveAuthTokenFromImage(ctx, dockerCli, image) encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), image)
if err != nil { if err != nil {
return err return err
} }

View File

@ -76,7 +76,7 @@ func RunPush(dockerCli command.Cli, opts pushOptions) error {
ctx := context.Background() ctx := context.Background()
// Resolve the Auth config relevant for this server // Resolve the Auth config relevant for this server
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), repoInfo.Index)
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
if err != nil { if err != nil {
return err return err

View File

@ -339,6 +339,6 @@ func TagTrusted(ctx context.Context, cli command.Cli, trustedRef reference.Canon
// AuthResolver returns an auth resolver function from a command.Cli // AuthResolver returns an auth resolver function from a command.Cli
func AuthResolver(cli command.Cli) func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig { func AuthResolver(cli command.Cli) func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
return func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig { return func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
return command.ResolveAuthConfig(ctx, cli, index) return command.ResolveAuthConfig(cli.ConfigFile(), index)
} }
} }

View File

@ -79,19 +79,18 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti
return types.PluginInstallOptions{}, errors.Errorf("invalid name: %s", ref.String()) return types.PluginInstallOptions{}, errors.Errorf("invalid name: %s", ref.String())
} }
trusted, err := image.TrustedReference(context.Background(), dockerCli, nt) trusted, err := image.TrustedReference(ctx, dockerCli, nt)
if err != nil { if err != nil {
return types.PluginInstallOptions{}, err return types.PluginInstallOptions{}, err
} }
remote = reference.FamiliarString(trusted) remote = reference.FamiliarString(trusted)
} }
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), repoInfo.Index)
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
if err != nil { if err != nil {
return types.PluginInstallOptions{}, err return types.PluginInstallOptions{}, err
} }
registryAuthFunc := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, cmdName)
options := types.PluginInstallOptions{ options := types.PluginInstallOptions{
RegistryAuth: encodedAuth, RegistryAuth: encodedAuth,
@ -99,7 +98,7 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti
Disabled: opts.disable, Disabled: opts.disable,
AcceptAllPermissions: opts.grantPerms, AcceptAllPermissions: opts.grantPerms,
AcceptPermissionsFunc: acceptPrivileges(dockerCli, opts.remote), AcceptPermissionsFunc: acceptPrivileges(dockerCli, opts.remote),
PrivilegeFunc: registryAuthFunc, PrivilegeFunc: command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, cmdName),
Args: opts.args, Args: opts.args,
} }
return options, nil return options, nil

View File

@ -55,7 +55,7 @@ func runPush(dockerCli command.Cli, opts pushOptions) error {
if err != nil { if err != nil {
return err return err
} }
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), repoInfo.Index)
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
if err != nil { if err != nil {
return err return err

View File

@ -2,13 +2,13 @@ package command
import ( import (
"bufio" "bufio"
"context"
"fmt" "fmt"
"io" "io"
"os" "os"
"runtime" "runtime"
"strings" "strings"
"github.com/docker/cli/cli/config/configfile"
configtypes "github.com/docker/cli/cli/config/types" configtypes "github.com/docker/cli/cli/config/types"
"github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/streams"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
@ -26,7 +26,7 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf
fmt.Fprintf(cli.Out(), "\nPlease login prior to %s:\n", cmdName) fmt.Fprintf(cli.Out(), "\nPlease login prior to %s:\n", cmdName)
indexServer := registry.GetAuthConfigKey(index) indexServer := registry.GetAuthConfigKey(index)
isDefaultRegistry := indexServer == registry.IndexServer isDefaultRegistry := indexServer == registry.IndexServer
authConfig, err := GetDefaultAuthConfig(cli, true, indexServer, isDefaultRegistry) authConfig, err := GetDefaultAuthConfig(cli.ConfigFile(), true, indexServer, isDefaultRegistry)
if err != nil { if err != nil {
fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", indexServer, err) fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", indexServer, err)
} }
@ -44,26 +44,26 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf
// //
// It is similar to [registry.ResolveAuthConfig], but uses the credentials- // It is similar to [registry.ResolveAuthConfig], but uses the credentials-
// store, instead of looking up credentials from a map. // store, instead of looking up credentials from a map.
func ResolveAuthConfig(_ context.Context, cli Cli, index *registrytypes.IndexInfo) registrytypes.AuthConfig { func ResolveAuthConfig(cfg *configfile.ConfigFile, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
configKey := index.Name configKey := index.Name
if index.Official { if index.Official {
configKey = registry.IndexServer configKey = registry.IndexServer
} }
a, _ := cli.ConfigFile().GetAuthConfig(configKey) a, _ := cfg.GetAuthConfig(configKey)
return registrytypes.AuthConfig(a) return registrytypes.AuthConfig(a)
} }
// GetDefaultAuthConfig gets the default auth config given a serverAddress // GetDefaultAuthConfig gets the default auth config given a serverAddress
// If credentials for given serverAddress exists in the credential store, the configuration will be populated with values in it // If credentials for given serverAddress exists in the credential store, the configuration will be populated with values in it
func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (registrytypes.AuthConfig, error) { func GetDefaultAuthConfig(cfg *configfile.ConfigFile, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (registrytypes.AuthConfig, error) {
if !isDefaultRegistry { if !isDefaultRegistry {
serverAddress = registry.ConvertToHostname(serverAddress) serverAddress = registry.ConvertToHostname(serverAddress)
} }
authconfig := configtypes.AuthConfig{} authconfig := configtypes.AuthConfig{}
var err error var err error
if checkCredStore { if checkCredStore {
authconfig, err = cli.ConfigFile().GetAuthConfig(serverAddress) authconfig, err = cfg.GetAuthConfig(serverAddress)
if err != nil { if err != nil {
return registrytypes.AuthConfig{ return registrytypes.AuthConfig{
ServerAddress: serverAddress, ServerAddress: serverAddress,
@ -72,8 +72,7 @@ func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, is
} }
authconfig.ServerAddress = serverAddress authconfig.ServerAddress = serverAddress
authconfig.IdentityToken = "" authconfig.IdentityToken = ""
res := registrytypes.AuthConfig(authconfig) return registrytypes.AuthConfig(authconfig), nil
return res, nil
} }
// ConfigureAuth handles prompting of user's username and password if needed // ConfigureAuth handles prompting of user's username and password if needed
@ -172,9 +171,9 @@ func promptWithDefault(out io.Writer, prompt string, configDefault string) {
// //
// For details on base64url encoding, see: // For details on base64url encoding, see:
// - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5 // - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5
func RetrieveAuthTokenFromImage(ctx context.Context, cli Cli, image string) (string, error) { func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (string, error) {
// Retrieve encoded auth token from the image reference // Retrieve encoded auth token from the image reference
authConfig, err := resolveAuthConfigFromImage(ctx, cli, image) authConfig, err := resolveAuthConfigFromImage(cfg, image)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -186,7 +185,7 @@ func RetrieveAuthTokenFromImage(ctx context.Context, cli Cli, image string) (str
} }
// resolveAuthConfigFromImage retrieves that AuthConfig using the image string // resolveAuthConfigFromImage retrieves that AuthConfig using the image string
func resolveAuthConfigFromImage(ctx context.Context, cli Cli, image string) (registrytypes.AuthConfig, error) { func resolveAuthConfigFromImage(cfg *configfile.ConfigFile, image string) (registrytypes.AuthConfig, error) {
registryRef, err := reference.ParseNormalizedNamed(image) registryRef, err := reference.ParseNormalizedNamed(image)
if err != nil { if err != nil {
return registrytypes.AuthConfig{}, err return registrytypes.AuthConfig{}, err
@ -195,5 +194,5 @@ func resolveAuthConfigFromImage(ctx context.Context, cli Cli, image string) (reg
if err != nil { if err != nil {
return registrytypes.AuthConfig{}, err return registrytypes.AuthConfig{}, err
} }
return ResolveAuthConfig(ctx, cli, repoInfo.Index), nil return ResolveAuthConfig(cfg, repoInfo.Index), nil
} }

View File

@ -117,7 +117,7 @@ func runLogin(dockerCli command.Cli, opts loginOptions) error { //nolint:gocyclo
} }
isDefaultRegistry := serverAddress == registry.IndexServer isDefaultRegistry := serverAddress == registry.IndexServer
authConfig, err := command.GetDefaultAuthConfig(dockerCli, opts.user == "" && opts.password == "", serverAddress, isDefaultRegistry) authConfig, err := command.GetDefaultAuthConfig(dockerCli.ConfigFile(), opts.user == "" && opts.password == "", serverAddress, isDefaultRegistry)
if err == nil && authConfig.Username != "" && authConfig.Password != "" { if err == nil && authConfig.Username != "" && authConfig.Password != "" {
response, err = loginWithCredStoreCreds(ctx, dockerCli, &authConfig) response, err = loginWithCredStoreCreds(ctx, dockerCli, &authConfig)
} }

View File

@ -54,13 +54,13 @@ func runSearch(dockerCli command.Cli, options searchOptions) error {
return err return err
} }
ctx := context.Background() authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), indexInfo)
authConfig := command.ResolveAuthConfig(ctx, dockerCli, indexInfo)
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
if err != nil { if err != nil {
return err return err
} }
ctx := context.Background()
requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, indexInfo, "search") requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, indexInfo, "search")
results, err := dockerCli.Client().ImageSearch(ctx, options.term, types.ImageSearchOptions{ results, err := dockerCli.Client().ImageSearch(ctx, options.term, types.ImageSearchOptions{
RegistryAuth: encodedAuth, RegistryAuth: encodedAuth,

View File

@ -1,26 +1,17 @@
package command_test package command_test
import ( import (
"bytes"
"context"
"fmt" "fmt"
"testing" "testing"
. "github.com/docker/cli/cli/command" // Prevents a circular import with "github.com/docker/cli/internal/test" . "github.com/docker/cli/cli/command" // Prevents a circular import with "github.com/docker/cli/internal/test"
"github.com/docker/cli/cli/config/configfile"
configtypes "github.com/docker/cli/cli/config/types" configtypes "github.com/docker/cli/cli/config/types"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/system"
"github.com/docker/docker/client"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp" is "gotest.tools/v3/assert/cmp"
) )
type fakeClient struct {
client.Client
infoFunc func() (system.Info, error)
}
var testAuthConfigs = []registry.AuthConfig{ var testAuthConfigs = []registry.AuthConfig{
{ {
ServerAddress: "https://index.docker.io/v1/", ServerAddress: "https://index.docker.io/v1/",
@ -34,13 +25,6 @@ var testAuthConfigs = []registry.AuthConfig{
}, },
} }
func (cli *fakeClient) Info(_ context.Context) (system.Info, error) {
if cli.infoFunc != nil {
return cli.infoFunc()
}
return system.Info{}, nil
}
func TestGetDefaultAuthConfig(t *testing.T) { func TestGetDefaultAuthConfig(t *testing.T) {
testCases := []struct { testCases := []struct {
checkCredStore bool checkCredStore bool
@ -77,15 +61,13 @@ func TestGetDefaultAuthConfig(t *testing.T) {
expectedAuthConfig: testAuthConfigs[1], expectedAuthConfig: testAuthConfigs[1],
}, },
} }
cli := test.NewFakeCli(&fakeClient{}) cfg := configfile.New("filename")
errBuf := new(bytes.Buffer)
cli.SetErr(errBuf)
for _, authconfig := range testAuthConfigs { for _, authconfig := range testAuthConfigs {
cli.ConfigFile().GetCredentialsStore(authconfig.ServerAddress).Store(configtypes.AuthConfig(authconfig)) cfg.GetCredentialsStore(authconfig.ServerAddress).Store(configtypes.AuthConfig(authconfig))
} }
for _, tc := range testCases { for _, tc := range testCases {
serverAddress := tc.inputServerAddress serverAddress := tc.inputServerAddress
authconfig, err := GetDefaultAuthConfig(cli, tc.checkCredStore, serverAddress, serverAddress == "https://index.docker.io/v1/") authconfig, err := GetDefaultAuthConfig(cfg, tc.checkCredStore, serverAddress, serverAddress == "https://index.docker.io/v1/")
if tc.expectedErr != "" { if tc.expectedErr != "" {
assert.Check(t, err != nil) assert.Check(t, err != nil)
assert.Check(t, is.Equal(tc.expectedErr, err.Error())) assert.Check(t, is.Equal(tc.expectedErr, err.Error()))
@ -97,15 +79,14 @@ func TestGetDefaultAuthConfig(t *testing.T) {
} }
func TestGetDefaultAuthConfig_HelperError(t *testing.T) { func TestGetDefaultAuthConfig_HelperError(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{}) cfg := configfile.New("filename")
errBuf := new(bytes.Buffer) cfg.CredentialsStore = "fake-does-not-exist"
cli.SetErr(errBuf)
cli.ConfigFile().CredentialsStore = "fake-does-not-exist" const serverAddress = "test-server-address"
serverAddress := "test-server-address"
expectedAuthConfig := registry.AuthConfig{ expectedAuthConfig := registry.AuthConfig{
ServerAddress: serverAddress, ServerAddress: serverAddress,
} }
authconfig, err := GetDefaultAuthConfig(cli, true, serverAddress, serverAddress == "https://index.docker.io/v1/") authconfig, err := GetDefaultAuthConfig(cfg, true, serverAddress, serverAddress == "https://index.docker.io/v1/")
assert.Check(t, is.DeepEqual(expectedAuthConfig, authconfig)) assert.Check(t, is.DeepEqual(expectedAuthConfig, authconfig))
assert.Check(t, is.ErrorContains(err, "docker-credential-fake-does-not-exist")) assert.Check(t, is.ErrorContains(err, "docker-credential-fake-does-not-exist"))
} }

View File

@ -112,7 +112,7 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, opts *serviceOptions
// only send auth if flag was set // only send auth if flag was set
if opts.registryAuth { if opts.registryAuth {
// Retrieve encoded auth token from the image reference // Retrieve encoded auth token from the image reference
encodedAuth, err := command.RetrieveAuthTokenFromImage(ctx, dockerCli, opts.image) encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), opts.image)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,7 +1,6 @@
package service package service
import ( import (
"context"
"encoding/hex" "encoding/hex"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
@ -39,7 +38,7 @@ func resolveServiceImageDigestContentTrust(dockerCli command.Cli, service *swarm
return errors.New("failed to resolve image digest using content trust: reference is not tagged") return errors.New("failed to resolve image digest using content trust: reference is not tagged")
} }
resolvedImage, err := trustedResolveDigest(context.Background(), dockerCli, taggedRef) resolvedImage, err := trustedResolveDigest(dockerCli, taggedRef)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to resolve image digest using content trust") return errors.Wrap(err, "failed to resolve image digest using content trust")
} }
@ -51,13 +50,13 @@ func resolveServiceImageDigestContentTrust(dockerCli command.Cli, service *swarm
return nil return nil
} }
func trustedResolveDigest(ctx context.Context, cli command.Cli, ref reference.NamedTagged) (reference.Canonical, error) { func trustedResolveDigest(cli command.Cli, ref reference.NamedTagged) (reference.Canonical, error) {
repoInfo, err := registry.ParseRepositoryInfo(ref) repoInfo, err := registry.ParseRepositoryInfo(ref)
if err != nil { if err != nil {
return nil, err return nil, err
} }
authConfig := command.ResolveAuthConfig(ctx, cli, repoInfo.Index) authConfig := command.ResolveAuthConfig(cli.ConfigFile(), repoInfo.Index)
notaryRepo, err := trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, &authConfig, "pull") notaryRepo, err := trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, &authConfig, "pull")
if err != nil { if err != nil {

View File

@ -223,7 +223,7 @@ func runUpdate(dockerCli command.Cli, flags *pflag.FlagSet, options *serviceOpti
// Retrieve encoded auth token from the image reference // Retrieve encoded auth token from the image reference
// This would be the old image if it didn't change in this update // This would be the old image if it didn't change in this update
image := spec.TaskTemplate.ContainerSpec.Image image := spec.TaskTemplate.ContainerSpec.Image
encodedAuth, err := command.RetrieveAuthTokenFromImage(ctx, dockerCli, image) encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), image)
if err != nil { if err != nil {
return err return err
} }

View File

@ -198,7 +198,7 @@ func deployServices(ctx context.Context, dockerCli command.Cli, services map[str
if sendAuth { if sendAuth {
// Retrieve encoded auth token from the image reference // Retrieve encoded auth token from the image reference
encodedAuth, err = command.RetrieveAuthTokenFromImage(ctx, dockerCli, image) encodedAuth, err = command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), image)
if err != nil { if err != nil {
return err return err
} }

View File

@ -93,7 +93,7 @@ func runSignImage(cli command.Cli, options signOptions) error {
} }
fmt.Fprintf(cli.Err(), "Signing and pushing trust data for local image %s, may overwrite remote trust data\n", imageName) fmt.Fprintf(cli.Err(), "Signing and pushing trust data for local image %s, may overwrite remote trust data\n", imageName)
authConfig := command.ResolveAuthConfig(ctx, cli, imgRefAndAuth.RepoInfo().Index) authConfig := command.ResolveAuthConfig(cli.ConfigFile(), imgRefAndAuth.RepoInfo().Index)
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig) encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
if err != nil { if err != nil {
return err return err