github.com/containers/podman/v4@v4.9.4/test/e2e/login_logout_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  	"strings"
    10  
    11  	. "github.com/containers/podman/v4/test/utils"
    12  	. "github.com/onsi/ginkgo/v2"
    13  	. "github.com/onsi/gomega"
    14  )
    15  
    16  var _ = Describe("Podman login and logout", func() {
    17  	var (
    18  		err                      error
    19  		authPath                 string
    20  		certPath                 string
    21  		certDirPath              string
    22  		server                   string
    23  		testImg                  string
    24  		registriesConfWithSearch []byte
    25  	)
    26  
    27  	BeforeEach(func() {
    28  		authPath = filepath.Join(podmanTest.TempDir, "auth")
    29  		err := os.Mkdir(authPath, os.ModePerm)
    30  		Expect(err).ToNot(HaveOccurred())
    31  
    32  		htpasswd := SystemExec("htpasswd", []string{"-Bbn", "podmantest", "test"})
    33  		htpasswd.WaitWithDefaultTimeout()
    34  		Expect(htpasswd).Should(ExitCleanly())
    35  
    36  		f, err := os.Create(filepath.Join(authPath, "htpasswd"))
    37  		Expect(err).ToNot(HaveOccurred())
    38  		defer f.Close()
    39  
    40  		_, err = f.WriteString(htpasswd.OutputToString())
    41  		Expect(err).ToNot(HaveOccurred())
    42  		err = f.Sync()
    43  		Expect(err).ToNot(HaveOccurred())
    44  		port := GetPort()
    45  		server = strings.Join([]string{"localhost", strconv.Itoa(port)}, ":")
    46  
    47  		registriesConfWithSearch = []byte(fmt.Sprintf("[registries.search]\nregistries = ['%s']", server))
    48  
    49  		testImg = strings.Join([]string{server, "test-alpine"}, "/")
    50  
    51  		certDirPath = filepath.Join(os.Getenv("HOME"), ".config/containers/certs.d", server)
    52  		err = os.MkdirAll(certDirPath, os.ModePerm)
    53  		Expect(err).ToNot(HaveOccurred())
    54  		cwd, _ := os.Getwd()
    55  		certPath = filepath.Join(cwd, "../", "certs")
    56  
    57  		setup := SystemExec("cp", []string{filepath.Join(certPath, "domain.crt"), filepath.Join(certDirPath, "ca.crt")})
    58  		setup.WaitWithDefaultTimeout()
    59  
    60  		session := podmanTest.Podman([]string{"run", "-d", "-p", strings.Join([]string{strconv.Itoa(port), strconv.Itoa(port)}, ":"),
    61  			"-e", strings.Join([]string{"REGISTRY_HTTP_ADDR=0.0.0.0", strconv.Itoa(port)}, ":"), "--name", "registry", "-v",
    62  			strings.Join([]string{authPath, "/auth:Z"}, ":"), "-e", "REGISTRY_AUTH=htpasswd", "-e",
    63  			"REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", "-e", "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd",
    64  			"-v", strings.Join([]string{certPath, "/certs:Z"}, ":"), "-e", "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt",
    65  			"-e", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key", REGISTRY_IMAGE})
    66  		session.WaitWithDefaultTimeout()
    67  		Expect(session).Should(ExitCleanly())
    68  
    69  		if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) {
    70  			Skip("Cannot start docker registry.")
    71  		}
    72  
    73  		// collision protection: each test uses a unique authfile
    74  		os.Setenv("REGISTRY_AUTH_FILE", filepath.Join(podmanTest.TempDir, "default-auth.json"))
    75  	})
    76  
    77  	AfterEach(func() {
    78  		os.Unsetenv("REGISTRY_AUTH_FILE")
    79  		os.RemoveAll(authPath)
    80  		os.RemoveAll(certDirPath)
    81  	})
    82  
    83  	readAuthInfo := func(filePath string) map[string]interface{} {
    84  		authBytes, err := os.ReadFile(filePath)
    85  		Expect(err).ToNot(HaveOccurred())
    86  
    87  		var authInfo map[string]interface{}
    88  		err = json.Unmarshal(authBytes, &authInfo)
    89  		Expect(err).ToNot(HaveOccurred())
    90  		GinkgoWriter.Println(authInfo)
    91  
    92  		const authsKey = "auths"
    93  		Expect(authInfo).To(HaveKey(authsKey))
    94  
    95  		auths, ok := authInfo[authsKey].(map[string]interface{})
    96  		Expect(ok).To(BeTrue(), "authInfo[%s]", authsKey)
    97  
    98  		return auths
    99  	}
   100  
   101  	It("podman login and logout", func() {
   102  		authFile := os.Getenv("REGISTRY_AUTH_FILE")
   103  		Expect(authFile).NotTo(BeEmpty(), "$REGISTRY_AUTH_FILE")
   104  
   105  		session := podmanTest.Podman([]string{"login", "-u", "podmantest", "-p", "test", server})
   106  		session.WaitWithDefaultTimeout()
   107  		Expect(session).Should(ExitCleanly())
   108  
   109  		// Confirm that file was created, with the desired credentials
   110  		auths := readAuthInfo(authFile)
   111  		Expect(auths).To(HaveKey(server))
   112  		// base64-encoded "podmantest:test"
   113  		Expect(auths[server]).To(HaveKeyWithValue("auth", "cG9kbWFudGVzdDp0ZXN0"))
   114  
   115  		session = podmanTest.Podman([]string{"push", "-q", ALPINE, testImg})
   116  		session.WaitWithDefaultTimeout()
   117  		Expect(session).Should(ExitCleanly())
   118  
   119  		session = podmanTest.Podman([]string{"logout", server})
   120  		session.WaitWithDefaultTimeout()
   121  		Expect(session).Should(ExitCleanly())
   122  
   123  		session = podmanTest.Podman([]string{"push", "-q", ALPINE, testImg})
   124  		session.WaitWithDefaultTimeout()
   125  		Expect(session).To(ExitWithError())
   126  		Expect(session.ErrorToString()).To(ContainSubstring(": authentication required"))
   127  	})
   128  
   129  	It("podman login and logout without registry parameter", func() {
   130  		registriesConf, err := os.CreateTemp("", "TestLoginWithoutParameter")
   131  		Expect(err).ToNot(HaveOccurred())
   132  		defer registriesConf.Close()
   133  		defer os.Remove(registriesConf.Name())
   134  
   135  		err = os.WriteFile(registriesConf.Name(), registriesConfWithSearch, os.ModePerm)
   136  		Expect(err).ToNot(HaveOccurred())
   137  
   138  		// Environment is per-process, so this looks very unsafe; actually it seems fine because tests are not
   139  		// run in parallel unless they opt in by calling t.Parallel().  So don’t do that.
   140  		oldRCP, hasRCP := os.LookupEnv("CONTAINERS_REGISTRIES_CONF")
   141  		defer func() {
   142  			if hasRCP {
   143  				os.Setenv("CONTAINERS_REGISTRIES_CONF", oldRCP)
   144  			} else {
   145  				os.Unsetenv("CONTAINERS_REGISTRIES_CONF")
   146  			}
   147  		}()
   148  		os.Setenv("CONTAINERS_REGISTRIES_CONF", registriesConf.Name())
   149  
   150  		session := podmanTest.Podman([]string{"login", "-u", "podmantest", "-p", "test"})
   151  		session.WaitWithDefaultTimeout()
   152  		Expect(session).Should(ExitCleanly())
   153  
   154  		session = podmanTest.Podman([]string{"logout"})
   155  		session.WaitWithDefaultTimeout()
   156  		Expect(session).Should(ExitCleanly())
   157  	})
   158  
   159  	It("podman login and logout with flag --authfile", func() {
   160  		authFile := filepath.Join(podmanTest.TempDir, "auth.json")
   161  		session := podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "--authfile", authFile, server})
   162  		session.WaitWithDefaultTimeout()
   163  		Expect(session).Should(ExitCleanly())
   164  
   165  		readAuthInfo(authFile)
   166  
   167  		// push should fail with nonexistent authfile
   168  		session = podmanTest.Podman([]string{"push", "-q", "--authfile", "/tmp/nonexistent", ALPINE, testImg})
   169  		session.WaitWithDefaultTimeout()
   170  		Expect(session).To(ExitWithError())
   171  		Expect(session.ErrorToString()).To(Equal("Error: credential file is not accessible: stat /tmp/nonexistent: no such file or directory"))
   172  
   173  		session = podmanTest.Podman([]string{"push", "-q", "--authfile", authFile, ALPINE, testImg})
   174  		session.WaitWithDefaultTimeout()
   175  		Expect(session).Should(ExitCleanly())
   176  
   177  		session = podmanTest.Podman([]string{"run", "-q", "--authfile", authFile, testImg})
   178  		session.WaitWithDefaultTimeout()
   179  		Expect(session).Should(ExitCleanly())
   180  
   181  		// logout should fail with nonexistent authfile
   182  		session = podmanTest.Podman([]string{"logout", "--authfile", "/tmp/nonexistent", server})
   183  		session.WaitWithDefaultTimeout()
   184  		Expect(session).To(ExitWithError())
   185  		Expect(session.ErrorToString()).To(Equal("Error: credential file is not accessible: stat /tmp/nonexistent: no such file or directory"))
   186  
   187  		session = podmanTest.Podman([]string{"logout", "--authfile", authFile, server})
   188  		session.WaitWithDefaultTimeout()
   189  		Expect(session).Should(ExitCleanly())
   190  	})
   191  
   192  	It("podman login and logout --compat-auth-file flag handling", func() {
   193  		// A minimal smoke test
   194  		compatAuthFile := filepath.Join(podmanTest.TempDir, "config.json")
   195  		session := podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "--compat-auth-file", compatAuthFile, server})
   196  		session.WaitWithDefaultTimeout()
   197  		Expect(session).Should(ExitCleanly())
   198  
   199  		readAuthInfo(compatAuthFile)
   200  
   201  		session = podmanTest.Podman([]string{"logout", "--compat-auth-file", compatAuthFile, server})
   202  		session.WaitWithDefaultTimeout()
   203  		Expect(session).Should(ExitCleanly())
   204  
   205  		// logout should fail with nonexistent authfile
   206  		session = podmanTest.Podman([]string{"logout", "--compat-auth-file", "/tmp/nonexistent", server})
   207  		session.WaitWithDefaultTimeout()
   208  		Expect(session).To(ExitWithError())
   209  		Expect(session.ErrorToString()).To(Equal("Error: credential file is not accessible: stat /tmp/nonexistent: no such file or directory"))
   210  
   211  		// inconsistent command line flags are rejected
   212  		// Pre-create the files to make sure we are not hitting the “file not found” path
   213  		authFile := filepath.Join(podmanTest.TempDir, "auth.json")
   214  		err := os.WriteFile(authFile, []byte("{}"), 0o700)
   215  		Expect(err).ToNot(HaveOccurred())
   216  		err = os.WriteFile(compatAuthFile, []byte("{}"), 0o700)
   217  		Expect(err).ToNot(HaveOccurred())
   218  
   219  		session = podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test",
   220  			"--authfile", authFile, "--compat-auth-file", compatAuthFile, server})
   221  		session.WaitWithDefaultTimeout()
   222  		Expect(session).To(ExitWithError())
   223  		Expect(session.ErrorToString()).To(Equal("Error: options for paths to the credential file and to the Docker-compatible credential file can not be set simultaneously"))
   224  
   225  		session = podmanTest.Podman([]string{"logout", "--authfile", authFile, "--compat-auth-file", compatAuthFile, server})
   226  		session.WaitWithDefaultTimeout()
   227  		Expect(session).To(ExitWithError())
   228  		Expect(session.ErrorToString()).To(Equal("Error: options for paths to the credential file and to the Docker-compatible credential file can not be set simultaneously"))
   229  	})
   230  
   231  	It("podman manifest with --authfile", func() {
   232  		os.Unsetenv("REGISTRY_AUTH_FILE")
   233  
   234  		authFile := filepath.Join(podmanTest.TempDir, "auth.json")
   235  		session := podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "--authfile", authFile, server})
   236  		session.WaitWithDefaultTimeout()
   237  		Expect(session).Should(ExitCleanly())
   238  
   239  		readAuthInfo(authFile)
   240  
   241  		session = podmanTest.Podman([]string{"manifest", "create", testImg})
   242  		session.WaitWithDefaultTimeout()
   243  		Expect(session).Should(ExitCleanly())
   244  
   245  		session = podmanTest.Podman([]string{"manifest", "push", "-q", testImg})
   246  		session.WaitWithDefaultTimeout()
   247  		Expect(session).To(ExitWithError())
   248  		Expect(session.ErrorToString()).To(ContainSubstring(": authentication required"))
   249  
   250  		session = podmanTest.Podman([]string{"manifest", "push", "-q", "--authfile", authFile, testImg})
   251  		session.WaitWithDefaultTimeout()
   252  		Expect(session).Should(ExitCleanly())
   253  
   254  		// Now remove the local manifest to trigger remote inspection
   255  		session = podmanTest.Podman([]string{"manifest", "rm", testImg})
   256  		session.WaitWithDefaultTimeout()
   257  		Expect(session).Should(ExitCleanly())
   258  
   259  		session = podmanTest.Podman([]string{"manifest", "inspect", testImg})
   260  		session.WaitWithDefaultTimeout()
   261  		Expect(session).To(ExitWithError())
   262  		Expect(session.ErrorToString()).To(ContainSubstring(": authentication required"))
   263  
   264  		session = podmanTest.Podman([]string{"manifest", "inspect", "--authfile", authFile, testImg})
   265  		session.WaitWithDefaultTimeout()
   266  		Expect(session).Should(ExitCleanly())
   267  	})
   268  
   269  	It("podman login and logout with --tls-verify", func() {
   270  		session := podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "--tls-verify=false", server})
   271  		session.WaitWithDefaultTimeout()
   272  		Expect(session).Should(ExitCleanly())
   273  
   274  		session = podmanTest.Podman([]string{"push", "-q", ALPINE, testImg})
   275  		session.WaitWithDefaultTimeout()
   276  		Expect(session).Should(ExitCleanly())
   277  
   278  		session = podmanTest.Podman([]string{"logout", server})
   279  		session.WaitWithDefaultTimeout()
   280  		Expect(session).Should(ExitCleanly())
   281  	})
   282  	It("podman login and logout with --cert-dir", func() {
   283  		certDir := filepath.Join(podmanTest.TempDir, "certs")
   284  		err := os.MkdirAll(certDir, os.ModePerm)
   285  		Expect(err).ToNot(HaveOccurred())
   286  
   287  		setup := SystemExec("cp", []string{filepath.Join(certPath, "domain.crt"), filepath.Join(certDir, "ca.crt")})
   288  		setup.WaitWithDefaultTimeout()
   289  
   290  		session := podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "--cert-dir", certDir, server})
   291  		session.WaitWithDefaultTimeout()
   292  		Expect(session).Should(ExitCleanly())
   293  
   294  		session = podmanTest.Podman([]string{"push", "-q", "--cert-dir", certDir, ALPINE, testImg})
   295  		session.WaitWithDefaultTimeout()
   296  		Expect(session).Should(ExitCleanly())
   297  
   298  		session = podmanTest.Podman([]string{"logout", server})
   299  		session.WaitWithDefaultTimeout()
   300  		Expect(session).Should(ExitCleanly())
   301  	})
   302  	It("podman login and logout with multi registry", func() {
   303  		certDir := filepath.Join(os.Getenv("HOME"), ".config/containers/certs.d", "localhost:9001")
   304  		err = os.MkdirAll(certDir, os.ModePerm)
   305  		Expect(err).ToNot(HaveOccurred())
   306  
   307  		cwd, _ := os.Getwd()
   308  		certPath = filepath.Join(cwd, "../", "certs")
   309  
   310  		setup := SystemExec("cp", []string{filepath.Join(certPath, "domain.crt"), filepath.Join(certDir, "ca.crt")})
   311  		setup.WaitWithDefaultTimeout()
   312  		defer os.RemoveAll(certDir)
   313  
   314  		// N/B: This second registry container shares the same auth and cert dirs
   315  		//      as the registry started from BeforeEach().  Since this one starts
   316  		//      second, re-labeling the volumes should keep SELinux happy.
   317  		session := podmanTest.Podman([]string{"run", "-d", "-p", "9001:9001", "-e", "REGISTRY_HTTP_ADDR=0.0.0.0:9001", "--name", "registry1", "-v",
   318  			strings.Join([]string{authPath, "/auth:z"}, ":"), "-e", "REGISTRY_AUTH=htpasswd", "-e",
   319  			"REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", "-e", "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd",
   320  			"-v", strings.Join([]string{certPath, "/certs:z"}, ":"), "-e", "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt",
   321  			"-e", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key", REGISTRY_IMAGE})
   322  		session.WaitWithDefaultTimeout()
   323  		Expect(session).Should(ExitCleanly())
   324  
   325  		if !WaitContainerReady(podmanTest, "registry1", "listening on", 20, 1) {
   326  			Skip("Cannot start docker registry.")
   327  		}
   328  
   329  		session = podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", server})
   330  		session.WaitWithDefaultTimeout()
   331  		Expect(session).Should(ExitCleanly())
   332  
   333  		session = podmanTest.Podman([]string{"push", "-q", ALPINE, testImg})
   334  		session.WaitWithDefaultTimeout()
   335  		Expect(session).Should(ExitCleanly())
   336  
   337  		session = podmanTest.Podman([]string{"push", "-q", ALPINE, "localhost:9001/test-alpine"})
   338  		session.WaitWithDefaultTimeout()
   339  		Expect(session).To(ExitWithError())
   340  		Expect(session.ErrorToString()).To(ContainSubstring("/test-alpine: authentication required"))
   341  
   342  		session = podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "localhost:9001"})
   343  		session.WaitWithDefaultTimeout()
   344  		Expect(session).Should(ExitCleanly())
   345  
   346  		session = podmanTest.Podman([]string{"push", "-q", ALPINE, testImg})
   347  		session.WaitWithDefaultTimeout()
   348  		Expect(session).Should(ExitCleanly())
   349  
   350  		session = podmanTest.Podman([]string{"push", "-q", ALPINE, "localhost:9001/test-alpine"})
   351  		session.WaitWithDefaultTimeout()
   352  		Expect(session).Should(ExitCleanly())
   353  
   354  		session = podmanTest.Podman([]string{"logout", server})
   355  		session.WaitWithDefaultTimeout()
   356  		Expect(session).Should(ExitCleanly())
   357  
   358  		session = podmanTest.Podman([]string{"push", "-q", ALPINE, testImg})
   359  		session.WaitWithDefaultTimeout()
   360  		Expect(session).To(ExitWithError())
   361  		Expect(session.ErrorToString()).To(ContainSubstring("/test-alpine: authentication required"))
   362  
   363  		session = podmanTest.Podman([]string{"push", "-q", ALPINE, "localhost:9001/test-alpine"})
   364  		session.WaitWithDefaultTimeout()
   365  		Expect(session).Should(ExitCleanly())
   366  
   367  		session = podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "localhost:9001"})
   368  		session.WaitWithDefaultTimeout()
   369  		Expect(session).Should(ExitCleanly())
   370  
   371  		session = podmanTest.Podman([]string{"logout", "-a"})
   372  		session.WaitWithDefaultTimeout()
   373  		Expect(session).Should(ExitCleanly())
   374  
   375  		session = podmanTest.Podman([]string{"push", "-q", ALPINE, testImg})
   376  		session.WaitWithDefaultTimeout()
   377  		Expect(session).To(ExitWithError())
   378  		Expect(session.ErrorToString()).To(ContainSubstring("/test-alpine: authentication required"))
   379  
   380  		session = podmanTest.Podman([]string{"push", "-q", ALPINE, "localhost:9001/test-alpine"})
   381  		session.WaitWithDefaultTimeout()
   382  		Expect(session).To(ExitWithError())
   383  		Expect(session.ErrorToString()).To(ContainSubstring("/test-alpine: authentication required"))
   384  	})
   385  
   386  	It("podman login and logout with repository", func() {
   387  		authFile := filepath.Join(podmanTest.TempDir, "auth.json")
   388  
   389  		testRepository := server + "/podmantest"
   390  		session := podmanTest.Podman([]string{
   391  			"login",
   392  			"-u", "podmantest",
   393  			"-p", "test",
   394  			"--authfile", authFile,
   395  			testRepository,
   396  		})
   397  		session.WaitWithDefaultTimeout()
   398  		Expect(session).Should(ExitCleanly())
   399  
   400  		authInfo := readAuthInfo(authFile)
   401  		Expect(authInfo).To(HaveKey(testRepository))
   402  
   403  		session = podmanTest.Podman([]string{
   404  			"logout",
   405  			"--authfile", authFile,
   406  			testRepository,
   407  		})
   408  		session.WaitWithDefaultTimeout()
   409  		Expect(session).Should(ExitCleanly())
   410  
   411  		authInfo = readAuthInfo(authFile)
   412  		Expect(authInfo).NotTo(HaveKey(testRepository))
   413  	})
   414  
   415  	It("podman login and logout with repository and specified image", func() {
   416  		authFile := filepath.Join(podmanTest.TempDir, "auth.json")
   417  
   418  		testTarget := server + "/podmantest/test-alpine"
   419  		session := podmanTest.Podman([]string{
   420  			"login",
   421  			"-u", "podmantest",
   422  			"-p", "test",
   423  			"--authfile", authFile,
   424  			testTarget,
   425  		})
   426  		session.WaitWithDefaultTimeout()
   427  		Expect(session).Should(ExitCleanly())
   428  
   429  		authInfo := readAuthInfo(authFile)
   430  		Expect(authInfo).To(HaveKey(testTarget))
   431  
   432  		session = podmanTest.Podman([]string{
   433  			"push", "-q",
   434  			"--authfile", authFile,
   435  			ALPINE, testTarget,
   436  		})
   437  		session.WaitWithDefaultTimeout()
   438  		Expect(session).Should(ExitCleanly())
   439  
   440  	})
   441  
   442  	It("podman login and logout with repository with fallback", func() {
   443  		authFile := filepath.Join(podmanTest.TempDir, "auth.json")
   444  
   445  		testRepos := []string{
   446  			server + "/podmantest",
   447  			server,
   448  		}
   449  		for _, testRepo := range testRepos {
   450  			session := podmanTest.Podman([]string{
   451  				"login",
   452  				"-u", "podmantest",
   453  				"-p", "test",
   454  				"--authfile", authFile,
   455  				testRepo,
   456  			})
   457  			session.WaitWithDefaultTimeout()
   458  			Expect(session).Should(ExitCleanly())
   459  		}
   460  
   461  		authInfo := readAuthInfo(authFile)
   462  		Expect(authInfo).To(HaveKey(testRepos[0]))
   463  		Expect(authInfo).To(HaveKey(testRepos[1]))
   464  
   465  		session := podmanTest.Podman([]string{
   466  			"push", "-q",
   467  			"--authfile", authFile,
   468  			ALPINE, testRepos[0] + "/test-image-alpine",
   469  		})
   470  		session.WaitWithDefaultTimeout()
   471  		Expect(session).Should(ExitCleanly())
   472  
   473  		session = podmanTest.Podman([]string{
   474  			"logout",
   475  			"--authfile", authFile,
   476  			testRepos[0],
   477  		})
   478  		session.WaitWithDefaultTimeout()
   479  		Expect(session).Should(ExitCleanly())
   480  
   481  		session = podmanTest.Podman([]string{
   482  			"push", "-q",
   483  			"--authfile", authFile,
   484  			ALPINE, testRepos[0] + "/test-image-alpine",
   485  		})
   486  		session.WaitWithDefaultTimeout()
   487  		Expect(session).Should(ExitCleanly())
   488  
   489  		session = podmanTest.Podman([]string{
   490  			"logout",
   491  			"--authfile", authFile,
   492  			testRepos[1],
   493  		})
   494  		session.WaitWithDefaultTimeout()
   495  		Expect(session).Should(ExitCleanly())
   496  
   497  		authInfo = readAuthInfo(authFile)
   498  		Expect(authInfo).NotTo(HaveKey(testRepos[0]))
   499  		Expect(authInfo).NotTo(HaveKey(testRepos[1]))
   500  	})
   501  
   502  	It("podman login with http{s} prefix", func() {
   503  		authFile := filepath.Join(podmanTest.TempDir, "auth.json")
   504  
   505  		for _, invalidArg := range []string{
   506  			"https://" + server + "/podmantest",
   507  			"http://" + server + "/podmantest/image:latest",
   508  		} {
   509  			session := podmanTest.Podman([]string{
   510  				"login",
   511  				"-u", "podmantest",
   512  				"-p", "test",
   513  				"--authfile", authFile,
   514  				invalidArg,
   515  			})
   516  			session.WaitWithDefaultTimeout()
   517  			Expect(session).To(ExitCleanly())
   518  		}
   519  	})
   520  
   521  	It("podman login and logout with repository push with invalid auth.json credentials", func() {
   522  		authFile := filepath.Join(podmanTest.TempDir, "auth.json")
   523  		// only `server` contains the correct login data
   524  		err := os.WriteFile(authFile, []byte(fmt.Sprintf(`{"auths": {
   525  			"%s/podmantest": { "auth": "cG9kbWFudGVzdDp3cm9uZw==" },
   526  			"%s": { "auth": "cG9kbWFudGVzdDp0ZXN0" }
   527  		}}`, server, server)), 0644)
   528  		Expect(err).ToNot(HaveOccurred())
   529  
   530  		session := podmanTest.Podman([]string{
   531  			"push", "-q",
   532  			"--authfile", authFile,
   533  			ALPINE, server + "/podmantest/test-image",
   534  		})
   535  		session.WaitWithDefaultTimeout()
   536  		Expect(session).To(ExitWithError())
   537  		Expect(session.ErrorToString()).To(ContainSubstring("/test-image: authentication required"))
   538  
   539  		session = podmanTest.Podman([]string{
   540  			"push", "-q",
   541  			"--authfile", authFile,
   542  			ALPINE, server + "/test-image",
   543  		})
   544  		session.WaitWithDefaultTimeout()
   545  		Expect(session).To(ExitCleanly())
   546  	})
   547  
   548  	It("podman login and logout with repository pull with wrong auth.json credentials", func() {
   549  		authFile := filepath.Join(podmanTest.TempDir, "auth.json")
   550  
   551  		testTarget := server + "/podmantest/test-alpine"
   552  		session := podmanTest.Podman([]string{
   553  			"login",
   554  			"-u", "podmantest",
   555  			"-p", "test",
   556  			"--authfile", authFile,
   557  			testTarget,
   558  		})
   559  		session.WaitWithDefaultTimeout()
   560  		Expect(session).Should(ExitCleanly())
   561  
   562  		session = podmanTest.Podman([]string{
   563  			"push", "-q",
   564  			"--authfile", authFile,
   565  			ALPINE, testTarget,
   566  		})
   567  		session.WaitWithDefaultTimeout()
   568  		Expect(session).Should(ExitCleanly())
   569  
   570  		// only `server + /podmantest` and `server` have the correct login data
   571  		err := os.WriteFile(authFile, []byte(fmt.Sprintf(`{"auths": {
   572  			"%s/podmantest/test-alpine": { "auth": "cG9kbWFudGVzdDp3cm9uZw==" },
   573  			"%s/podmantest": { "auth": "cG9kbWFudGVzdDp0ZXN0" },
   574  			"%s": { "auth": "cG9kbWFudGVzdDp0ZXN0" }
   575  		}}`, server, server, server)), 0644)
   576  		Expect(err).ToNot(HaveOccurred())
   577  
   578  		session = podmanTest.Podman([]string{
   579  			"pull", "-q",
   580  			"--authfile", authFile,
   581  			server + "/podmantest/test-alpine",
   582  		})
   583  		session.WaitWithDefaultTimeout()
   584  		Expect(session).To(ExitWithError())
   585  		Expect(session.ErrorToString()).To(ContainSubstring("/test-alpine: authentication required"))
   586  	})
   587  })