Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions modules/registry/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package registry
import (
"fmt"
"os"
"path/filepath"

"github.com/testcontainers/testcontainers-go"
)
Expand Down Expand Up @@ -37,7 +36,7 @@ func WithData(dataPath string) testcontainers.CustomizeRequestOption {
// the htpasswd file, thanks to the REGISTRY_AUTH_HTPASSWD_PATH environment variable.
func WithHtpasswd(credentials string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) error {
tmpFile, err := os.Create(filepath.Join(os.TempDir(), "htpasswd"))
tmpFile, err := os.CreateTemp("", "htpasswd")
if err != nil {
tmpFile, err = os.Create(".")
if err != nil {
Expand Down
62 changes: 62 additions & 0 deletions modules/registry/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/cpuguy83/dockercfg"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt"

"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/registry"
Expand Down Expand Up @@ -171,6 +172,67 @@ func TestRunContainer_authenticated_withCredentials(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
}

func TestRunContainer_authenticated_htpasswd_atomic_per_container(t *testing.T) {
t.Parallel()
ctx := context.Background()
r := require.New(t)

type container struct {
pass string
registry *registry.RegistryContainer
addr string
}

newContainer := func(password string) container {
hash, err := bcrypt.GenerateFromPassword([]byte(password), 5)
r.NoError(err)

reg, err := registry.Run(
ctx,
registry.DefaultImage,
registry.WithHtpasswd("testuser:"+string(hash)),
)
r.NoError(err)
testcontainers.CleanupContainer(t, reg)

addr, err := reg.Address(ctx)
r.NoError(err)

return container{pass: password, registry: reg, addr: addr}
}

// Create two independent registries with different credentials.
regA := newContainer("passA")
regB := newContainer("passB")

client := http.Client{}

// 1. Wrong password against A must fail.
req, err := http.NewRequest(http.MethodGet, regA.addr+"/v2/_catalog", nil)
r.NoError(err)
req.SetBasicAuth("testuser", regB.pass)
resp, err := client.Do(req)
r.NoError(err)
r.Equal(http.StatusUnauthorized, resp.StatusCode)
_ = resp.Body.Close()

// 2. Correct password against A must succeed.
req.SetBasicAuth("testuser", regA.pass)
resp, err = client.Do(req)
r.NoError(err)
r.Equal(http.StatusOK, resp.StatusCode)
_ = resp.Body.Close()

// 3. Correct password against B must succeed.
req, err = http.NewRequest(http.MethodGet, regB.addr+"/v2/_catalog", nil)
r.NoError(err)
req.SetBasicAuth("testuser", regB.pass)
resp, err = client.Do(req)
r.NoError(err)
r.Equal(http.StatusOK, resp.StatusCode)
_ = resp.Body.Close()
}

func TestRunContainer_wrongData(t *testing.T) {
ctx := context.Background()
registryContainer, err := registry.Run(
Expand Down
Loading