<commit_before>package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	"github.com/containers/image/v5/docker/reference"
	"github.com/containers/image/v5/types"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestGetPathToAuth(t *testing.T) {
	const linux = "linux"
	const darwin = "darwin"

	uid := fmt.Sprintf("%d", os.Getuid())
	// We don’t have to override the home directory for this because use of this path does not depend
	// on any state of the filesystem.
	darwinDefault := filepath.Join(os.Getenv("HOME"), ".config", "containers", "auth.json")

	tmpDir, err := ioutil.TempDir("", "TestGetPathToAuth")
	require.NoError(t, err)
	defer os.RemoveAll(tmpDir)

	// Environment is per-process, so this looks very unsafe; actually it seems fine because tests are not
	// run in parallel unless they opt in by calling t.Parallel().  So don’t do that.
	oldXRD, hasXRD := os.LookupEnv("XDG_RUNTIME_DIR")
	defer func() {
		if hasXRD {
			os.Setenv("XDG_RUNTIME_DIR", oldXRD)
		} else {
			os.Unsetenv("XDG_RUNTIME_DIR")
		}
	}()

	for _, c := range []struct {
		sys          *types.SystemContext
		os           string
		xrd          string
		expected     string
		legacyFormat bool
	}{
		// Default paths
		{&types.SystemContext{}, linux, "", "/run/containers/" + uid + "/auth.json", false},
		{&types.SystemContext{}, darwin, "", darwinDefault, false},
		{nil, linux, "", "/run/containers/" + uid + "/auth.json", false},
		{nil, darwin, "", darwinDefault, false},
		// SystemContext overrides
		{&types.SystemContext{AuthFilePath: "/absolute/path"}, linux, "", "/absolute/path", false},
		{&types.SystemContext{AuthFilePath: "/absolute/path"}, darwin, "", "/absolute/path", false},
		{&types.SystemContext{LegacyFormatAuthFilePath: "/absolute/path"}, linux, "", "/absolute/path", true},
		{&types.SystemContext{LegacyFormatAuthFilePath: "/absolute/path"}, darwin, "", "/absolute/path", true},
		{&types.SystemContext{RootForImplicitAbsolutePaths: "/prefix"}, linux, "", "/prefix/run/containers/" + uid + "/auth.json", false},
		{&types.SystemContext{RootForImplicitAbsolutePaths: "/prefix"}, darwin, "", "/prefix/run/containers/" + uid + "/auth.json", false},
		// XDG_RUNTIME_DIR defined
		{nil, linux, tmpDir, tmpDir + "/containers/auth.json", false},
		{nil, darwin, tmpDir, darwinDefault, false},
		{nil, linux, tmpDir + "/thisdoesnotexist", "", false},
		{nil, darwin, tmpDir + "/thisdoesnotexist", darwinDefault, false},
	} {
		if c.xrd != "" {
			os.Setenv("XDG_RUNTIME_DIR", c.xrd)
		} else {
			os.Unsetenv("XDG_RUNTIME_DIR")
		}
		res, lf, err := getPathToAuthWithOS(c.sys, c.os)
		if c.expected == "" {
			assert.Error(t, err)
		} else {
			require.NoError(t, err)
			assert.Equal(t, c.expected, res)
			assert.Equal(t, c.legacyFormat, lf)
		}
	}
}

func TestGetAuth(t *testing.T) {
	origXDG := os.Getenv("XDG_RUNTIME_DIR")
	tmpXDGRuntimeDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("using temporary XDG_RUNTIME_DIR directory: %q", tmpXDGRuntimeDir)
	// override XDG_RUNTIME_DIR
	os.Setenv("XDG_RUNTIME_DIR", tmpXDGRuntimeDir)
	defer func() {
		err := os.RemoveAll(tmpXDGRuntimeDir)
		if err != nil {
			t.Logf("failed to cleanup temporary home directory %q: %v", tmpXDGRuntimeDir, err)
		}
		os.Setenv("XDG_RUNTIME_DIR", origXDG)
	}()

	// override PATH for executing credHelper
	curtDir, err := os.Getwd()
	require.NoError(t, err)
	origPath := os.Getenv("PATH")
	newPath := fmt.Sprintf("%s:%s", filepath.Join(curtDir, "testdata"), origPath)
	os.Setenv("PATH", newPath)
	t.Logf("using PATH: %q", newPath)
	defer func() {
		os.Setenv("PATH", origPath)
	}()

	tmpHomeDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("using temporary home directory: %q", tmpHomeDir)
	defer func() {
		err := os.RemoveAll(tmpHomeDir)
		if err != nil {
			t.Logf("failed to cleanup temporary home directory %q: %v", tmpHomeDir, err)
		}
	}()

	configDir1 := filepath.Join(tmpXDGRuntimeDir, "containers")
	if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
		configDir1 = filepath.Join(tmpHomeDir, ".config", "containers")
	}
	if err := os.MkdirAll(configDir1, 0700); err != nil {
		t.Fatal(err)
	}
	configDir2 := filepath.Join(tmpHomeDir, ".docker")
	if err := os.MkdirAll(configDir2, 0700); err != nil {
		t.Fatal(err)
	}
	configPaths := [2]string{filepath.Join(configDir1, "auth.json"), filepath.Join(configDir2, "config.json")}

	for _, configPath := range configPaths {
		for _, tc := range []struct {
			name            string
			ref             string
			hostname        string
			path            string
			expected        types.DockerAuthConfig
			expectedError   error
			sys             *types.SystemContext
			testPreviousAPI bool
		}{
			{
				name:            "no auth config",
				hostname:        "index.docker.io",
				testPreviousAPI: true,
			},
			{
				name: "empty hostname",
				path: filepath.Join("testdata", "example.json"),
			},
			{
				name:     "match one",
				hostname: "example.org",
				path:     filepath.Join("testdata", "example.json"),
				expected: types.DockerAuthConfig{
					Username: "example",
					Password: "<PASSWORD>",
				},
				testPreviousAPI: true,
			},
			{
				name:            "match none",
				hostname:        "registry.example.org",
				path:            filepath.Join("testdata", "example.json"),
				testPreviousAPI: true,
			},
			{
				name:     "match docker.io",
				hostname: "docker.io",
				path:     filepath.Join("testdata", "full.json"),
				expected: types.DockerAuthConfig{
					Username: "docker",
					Password: "io",
				},
				testPreviousAPI: true,
			},
			{
				name:     "match docker.io normalized",
				hostname: "docker.io",
				path:     filepath.Join("testdata", "abnormal.json"),
				expected: types.DockerAuthConfig{
					Username: "index",
					Password: "<PASSWORD>",
				},
				testPreviousAPI: true,
			},
			{
				name:     "normalize registry",
				hostname: "normalize.example.org",
				path:     filepath.Join("testdata", "full.json"),
				expected: types.DockerAuthConfig{
					Username: "normalize",
					Password: "<PASSWORD>",
				},
				testPreviousAPI: true,
			},
			{
				name:     "match localhost",
				hostname: "localhost",
				path:     filepath.Join("testdata", "full.json"),
				expected: types.DockerAuthConfig{
					Username: "local",
					Password: "<PASSWORD>",
				},
				testPreviousAPI: true,
			},
			{
				name:     "match ip",
				hostname: "10.10.30.45:5000",
				path:     filepath.Join("testdata", "full.json"),
				expected: types.DockerAuthConfig{
					Username: "<PASSWORD>",
					Password: "<PASSWORD>",
				},
				testPreviousAPI: true,
			},
			{
				name:     "match port",
				hostname: "localhost:5000",
				path:     filepath.Join("testdata", "abnormal.json"),
				expected: types.DockerAuthConfig{
					Username: "local",
					Password: "<PASSWORD>",
				},
				testPreviousAPI: true,
			},
			{
				name:     "use system context",
				hostname: "example.org",
				path:     filepath.Join("testdata", "example.json"),
				expected: types.DockerAuthConfig{
					Username: "foo",
					Password: "<PASSWORD>",
				},
				sys: &types.SystemContext{
					DockerAuthConfig: &types.DockerAuthConfig{
						Username: "foo",
						Password: "<PASSWORD>",
					},
				},
				testPreviousAPI: true,
			},
			{
				name:     "identity token",
				hostname: "example.org",
				path:     filepath.Join("testdata", "example_identitytoken.json"),
				expected: types.DockerAuthConfig{
					Username:      "00000000-0000-0000-0000-000000000000",
					Password:      "",
					IdentityToken: "some very long identity token",
				},
				testPreviousAPI: true,
			},
			{
				name:            "match none (empty.json)",
				hostname:        "https://localhost:5000",
				path:            filepath.Join("testdata", "empty.json"),
				testPreviousAPI: true,
			},
			{
				name:     "credhelper from registries.conf",
				hostname: "registry-a.com",
				sys: &types.SystemContext{
					SystemRegistriesConfPath:    filepath.Join("testdata", "cred-helper.conf"),
					SystemRegistriesConfDirPath: filepath.Join("testdata", "IdoNotExist"),
				},
				expected: types.DockerAuthConfig{
					Username: "foo",
					Password: "<PASSWORD>",
				},
				testPreviousAPI: true,
			},
			{
				name:     "identity token credhelper from registries.conf",
				hostname: "registry-b.com",
				sys: &types.SystemContext{
					SystemRegistriesConfPath:    filepath.Join("testdata", "cred-helper.conf"),
					SystemRegistriesConfDirPath: filepath.Join("testdata", "IdoNotExist"),
				},
				expected: types.DockerAuthConfig{
					IdentityToken: "<PASSWORD>",
				},
				testPreviousAPI: true,
			},
			{
				name:     "match ref image",
				hostname: "example.org",
				ref:      "example.org/repo/image:latest",
				path:     filepath.Join("testdata", "refpath.json"),
				expected: types.DockerAuthConfig{
					Username: "example",
					Password: "<PASSWORD>",
				},
				testPreviousAPI: false,
			},
			{
				name:     "match ref repo",
				hostname: "example.org",
				ref:      "example.org/repo",
				path:     filepath.Join("testdata", "refpath.json"),
				expected: types.DockerAuthConfig{
					Username: "example",
					Password: "<PASSWORD>",
				},
				testPreviousAPI: false,
			},
			{
				name:     "match ref host",
				hostname: "example.org",
				ref:      "example.org/image:latest",
				path:     filepath.Join("testdata", "refpath.json"),
				expected: types.DockerAuthConfig{
					Username: "local",
					Password: "<PASSWORD>",
				},
				testPreviousAPI: false,
			},
		} {
			t.Run(tc.name, func(t *testing.T) {
				if err := os.RemoveAll(configPath); err != nil {
					t.Fatal(err)
				}

				if tc.path != "" {
					contents, err := ioutil.ReadFile(tc.path)
					if err != nil {
						t.Fatal(err)
					}

					if err := ioutil.WriteFile(configPath, contents, 0640); err != nil {
						t.Fatal(err)
					}
				}

				var sys *types.SystemContext
				if tc.sys != nil {<commit_msg>Fold most expected: constants in TestGetAuth to a single line<commit_after>package config

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	"github.com/containers/image/v5/docker/reference"
	"github.com/containers/image/v5/types"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestGetPathToAuth(t *testing.T) {
	const linux = "linux"
	const darwin = "darwin"

	uid := fmt.Sprintf("%d", os.Getuid())
	// We don’t have to override the home directory for this because use of this path does not depend
	// on any state of the filesystem.
	darwinDefault := filepath.Join(os.Getenv("HOME"), ".config", "containers", "auth.json")

	tmpDir, err := ioutil.TempDir("", "TestGetPathToAuth")
	require.NoError(t, err)
	defer os.RemoveAll(tmpDir)

	// Environment is per-process, so this looks very unsafe; actually it seems fine because tests are not
	// run in parallel unless they opt in by calling t.Parallel().  So don’t do that.
	oldXRD, hasXRD := os.LookupEnv("XDG_RUNTIME_DIR")
	defer func() {
		if hasXRD {
			os.Setenv("XDG_RUNTIME_DIR", oldXRD)
		} else {
			os.Unsetenv("XDG_RUNTIME_DIR")
		}
	}()

	for _, c := range []struct {
		sys          *types.SystemContext
		os           string
		xrd          string
		expected     string
		legacyFormat bool
	}{
		// Default paths
		{&types.SystemContext{}, linux, "", "/run/containers/" + uid + "/auth.json", false},
		{&types.SystemContext{}, darwin, "", darwinDefault, false},
		{nil, linux, "", "/run/containers/" + uid + "/auth.json", false},
		{nil, darwin, "", darwinDefault, false},
		// SystemContext overrides
		{&types.SystemContext{AuthFilePath: "/absolute/path"}, linux, "", "/absolute/path", false},
		{&types.SystemContext{AuthFilePath: "/absolute/path"}, darwin, "", "/absolute/path", false},
		{&types.SystemContext{LegacyFormatAuthFilePath: "/absolute/path"}, linux, "", "/absolute/path", true},
		{&types.SystemContext{LegacyFormatAuthFilePath: "/absolute/path"}, darwin, "", "/absolute/path", true},
		{&types.SystemContext{RootForImplicitAbsolutePaths: "/prefix"}, linux, "", "/prefix/run/containers/" + uid + "/auth.json", false},
		{&types.SystemContext{RootForImplicitAbsolutePaths: "/prefix"}, darwin, "", "/prefix/run/containers/" + uid + "/auth.json", false},
		// XDG_RUNTIME_DIR defined
		{nil, linux, tmpDir, tmpDir + "/containers/auth.json", false},
		{nil, darwin, tmpDir, darwinDefault, false},
		{nil, linux, tmpDir + "/thisdoesnotexist", "", false},
		{nil, darwin, tmpDir + "/thisdoesnotexist", darwinDefault, false},
	} {
		if c.xrd != "" {
			os.Setenv("XDG_RUNTIME_DIR", c.xrd)
		} else {
			os.Unsetenv("XDG_RUNTIME_DIR")
		}
		res, lf, err := getPathToAuthWithOS(c.sys, c.os)
		if c.expected == "" {
			assert.Error(t, err)
		} else {
			require.NoError(t, err)
			assert.Equal(t, c.expected, res)
			assert.Equal(t, c.legacyFormat, lf)
		}
	}
}

func TestGetAuth(t *testing.T) {
	origXDG := os.Getenv("XDG_RUNTIME_DIR")
	tmpXDGRuntimeDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("using temporary XDG_RUNTIME_DIR directory: %q", tmpXDGRuntimeDir)
	// override XDG_RUNTIME_DIR
	os.Setenv("XDG_RUNTIME_DIR", tmpXDGRuntimeDir)
	defer func() {
		err := os.RemoveAll(tmpXDGRuntimeDir)
		if err != nil {
			t.Logf("failed to cleanup temporary home directory %q: %v", tmpXDGRuntimeDir, err)
		}
		os.Setenv("XDG_RUNTIME_DIR", origXDG)
	}()

	// override PATH for executing credHelper
	curtDir, err := os.Getwd()
	require.NoError(t, err)
	origPath := os.Getenv("PATH")
	newPath := fmt.Sprintf("%s:%s", filepath.Join(curtDir, "testdata"), origPath)
	os.Setenv("PATH", newPath)
	t.Logf("using PATH: %q", newPath)
	defer func() {
		os.Setenv("PATH", origPath)
	}()

	tmpHomeDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("using temporary home directory: %q", tmpHomeDir)
	defer func() {
		err := os.RemoveAll(tmpHomeDir)
		if err != nil {
			t.Logf("failed to cleanup temporary home directory %q: %v", tmpHomeDir, err)
		}
	}()

	configDir1 := filepath.Join(tmpXDGRuntimeDir, "containers")
	if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
		configDir1 = filepath.Join(tmpHomeDir, ".config", "containers")
	}
	if err := os.MkdirAll(configDir1, 0700); err != nil {
		t.Fatal(err)
	}
	configDir2 := filepath.Join(tmpHomeDir, ".docker")
	if err := os.MkdirAll(configDir2, 0700); err != nil {
		t.Fatal(err)
	}
	configPaths := [2]string{filepath.Join(configDir1, "auth.json"), filepath.Join(configDir2, "config.json")}

	for _, configPath := range configPaths {
		for _, tc := range []struct {
			name            string
			ref             string
			hostname        string
			path            string
			expected        types.DockerAuthConfig
			expectedError   error
			sys             *types.SystemContext
			testPreviousAPI bool
		}{
			{
				name:            "no auth config",
				hostname:        "index.docker.io",
				testPreviousAPI: true,
			},
			{
				name: "empty hostname",
				path: filepath.Join("testdata", "example.json"),
			},
			{
				name:            "match one",
				hostname:        "example.org",
				path:            filepath.Join("testdata", "example.json"),
				expected:        types.DockerAuthConfig{Username: "example", Password: "<PASSWORD>"},
				testPreviousAPI: true,
			},
			{
				name:            "match none",
				hostname:        "registry.example.org",
				path:            filepath.Join("testdata", "example.json"),
				testPreviousAPI: true,
			},
			{
				name:            "match docker.io",
				hostname:        "docker.io",
				path:            filepath.Join("testdata", "full.json"),
				expected:        types.DockerAuthConfig{Username: "docker", Password: "io"},
				testPreviousAPI: true,
			},
			{
				name:            "match docker.io normalized",
				hostname:        "docker.io",
				path:            filepath.Join("testdata", "abnormal.json"),
				expected:        types.DockerAuthConfig{Username: "index", Password: "<PASSWORD>"},
				testPreviousAPI: true,
			},
			{
				name:            "normalize registry",
				hostname:        "normalize.example.org",
				path:            filepath.Join("testdata", "full.json"),
				expected:        types.DockerAuthConfig{Username: "normalize", Password: "<PASSWORD>"},
				testPreviousAPI: true,
			},
			{
				name:            "match localhost",
				hostname:        "localhost",
				path:            filepath.Join("testdata", "full.json"),
				expected:        types.DockerAuthConfig{Username: "local", Password: "<PASSWORD>"},
				testPreviousAPI: true,
			},
			{
				name:            "match ip",
				hostname:        "10.10.30.45:5000",
				path:            filepath.Join("testdata", "full.json"),
				expected:        types.DockerAuthConfig{Username: "10.10", Password: "<PASSWORD>"},
				testPreviousAPI: true,
			},
			{
				name:            "match port",
				hostname:        "localhost:5000",
				path:            filepath.Join("testdata", "abnormal.json"),
				expected:        types.DockerAuthConfig{Username: "local", Password: "<PASSWORD>"},
				testPreviousAPI: true,
			},
			{
				name:     "use system context",
				hostname: "example.org",
				path:     filepath.Join("testdata", "example.json"),
				expected: types.DockerAuthConfig{Username: "foo", Password: "<PASSWORD>"},
				sys: &types.SystemContext{
					DockerAuthConfig: &types.DockerAuthConfig{
						Username: "foo",
						Password: "<PASSWORD>",
					},
				},
				testPreviousAPI: true,
			},
			{
				name:     "identity token",
				hostname: "example.org",
				path:     filepath.Join("testdata", "example_identitytoken.json"),
				expected: types.DockerAuthConfig{
					Username:      "00000000-0000-0000-0000-000000000000",
					Password:      "",
					IdentityToken: "<PASSWORD> token",
				},
				testPreviousAPI: true,
			},
			{
				name:            "match none (empty.json)",
				hostname:        "https://localhost:5000",
				path:            filepath.Join("testdata", "empty.json"),
				testPreviousAPI: true,
			},
			{
				name:     "credhelper from registries.conf",
				hostname: "registry-a.com",
				sys: &types.SystemContext{
					SystemRegistriesConfPath:    filepath.Join("testdata", "cred-helper.conf"),
					SystemRegistriesConfDirPath: filepath.Join("testdata", "IdoNotExist"),
				},
				expected:        types.DockerAuthConfig{Username: "foo", Password: "<PASSWORD>"},
				testPreviousAPI: true,
			},
			{
				name:     "identity token credhelper from registries.conf",
				hostname: "registry-b.com",
				sys: &types.SystemContext{
					SystemRegistriesConfPath:    filepath.Join("testdata", "cred-helper.conf"),
					SystemRegistriesConfDirPath: filepath.Join("testdata", "IdoNotExist"),
				},
				expected:        types.DockerAuthConfig{IdentityToken: "<PASSWORD>"},
				testPreviousAPI: true,
			},
			{
				name:            "match ref image",
				hostname:        "example.org",
				ref:             "example.org/repo/image:latest",
				path:            filepath.Join("testdata", "refpath.json"),
				expected:        types.DockerAuthConfig{Username: "example", Password: "<PASSWORD>"},
				testPreviousAPI: false,
			},
			{
				name:            "match ref repo",
				hostname:        "example.org",
				ref:             "example.org/repo",
				path:            filepath.Join("testdata", "refpath.json"),
				expected:        types.DockerAuthConfig{Username: "example", Password: "<PASSWORD>"},
				testPreviousAPI: false,
			},
			{
				name:            "match ref host",
				hostname:        "example.org",
				ref:             "example.org/image:latest",
				path:            filepath.Join("testdata", "refpath.json"),
				expected:        types.DockerAuthConfig{Username: "local", Password: "<PASSWORD>"},
				testPreviousAPI: false,
			},
		} {
			t.Run(tc.name, func(t *testing.T) {
				if err := os.RemoveAll(configPath); err != nil {
					t.Fatal(err)
				}

				if tc.path != "" {
					contents, err := ioutil.ReadFile(tc.path)
					if err != nil {
						t.Fatal(err)
					}

					if err := ioutil.WriteFile(configPath, contents, 0640); err != nil {
						t.Fatal(err)
					}
				}

				var sys *types.SystemContext
				if tc.sys != nil {
{'fraction_non_alphanumeric': 0.15632827464269927, 'fraction_numerical': 0.007465252197829183, 'mean_word_length': 3.000392927308448, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 31, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '342137', 'n_tokens_mistral': 8208, 'n_tokens_neox': 6836, 'n_words': 1413}