Skip to content
This repository has been archived by the owner on Apr 7, 2024. It is now read-only.

Commit

Permalink
fix: file store to support legacy auth keys in config files (#100)
Browse files Browse the repository at this point in the history
Backporting
[`e8e4f84`](oras-project/oras-go@e8e4f84)
from `oras-go`.

Fix: #1 
Signed-off-by: Sylvia Lei <lixlei@microsoft.com>
  • Loading branch information
Wwwsylvia authored Oct 16, 2023
1 parent f5c812d commit 21321d3
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 1 deletion.
27 changes: 26 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,20 @@ func (cfg *Config) GetCredential(serverAddress string) (auth.Credential, error)

authCfgBytes, ok := cfg.authsCache[serverAddress]
if !ok {
return auth.EmptyCredential, nil
// NOTE: the auth key for the server address may have been stored with
// a http/https prefix in legacy config files, e.g. "registry.example.com"
// can be stored as "https://registry.example.com/".
var matched bool
for addr, auth := range cfg.authsCache {
if toHostname(addr) == serverAddress {
matched = true
authCfgBytes = auth
break
}
}
if !matched {
return auth.EmptyCredential, nil
}
}
var authCfg AuthConfig
if err := json.Unmarshal(authCfgBytes, &authCfg); err != nil {
Expand Down Expand Up @@ -300,3 +313,15 @@ func decodeAuth(authStr string) (username string, password string, err error) {
}
return username, password, nil
}

// toHostname normalizes a server address to just its hostname, removing
// the scheme and the path parts.
// It is used to match keys in the auths map, which may be either stored as
// hostname or as hostname including scheme (in legacy docker config files).
// Reference: https://github.com/docker/cli/blob/v24.0.6/cli/config/credentials/file_store.go#L71
func toHostname(addr string) string {
addr = strings.TrimPrefix(addr, "http://")
addr = strings.TrimPrefix(addr, "https://")
addr, _, _ = strings.Cut(addr, "/")
return addr
}
136 changes: 136 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,94 @@ func TestConfig_GetCredential_validConfig(t *testing.T) {
}
}

func TestConfig_GetCredential_legacyConfig(t *testing.T) {
cfg, err := Load("../../testdata/legacy_auths_config.json")
if err != nil {
t.Fatal("Load() error =", err)
}

tests := []struct {
name string
serverAddress string
want auth.Credential
wantErr bool
}{
{
name: "Regular address matched",
serverAddress: "registry1.example.com",
want: auth.Credential{
Username: "username1",
Password: "password1",
},
},
{
name: "Another entry for the same address matched",
serverAddress: "https://registry1.example.com/",
want: auth.Credential{
Username: "foo",
Password: "bar",
},
},
{
name: "Address with different scheme unmached",
serverAddress: "http://registry1.example.com/",
want: auth.EmptyCredential,
},
{
name: "Address with http prefix matched",
serverAddress: "registry2.example.com",
want: auth.Credential{
Username: "username2",
Password: "password2",
},
},
{
name: "Address with https prefix matched",
serverAddress: "registry3.example.com",
want: auth.Credential{
Username: "username3",
Password: "password3",
},
},
{
name: "Address with http prefix and / suffix matched",
serverAddress: "registry4.example.com",
want: auth.Credential{
Username: "username4",
Password: "password4",
},
},
{
name: "Address with https prefix and / suffix matched",
serverAddress: "registry5.example.com",
want: auth.Credential{
Username: "username5",
Password: "password5",
},
},
{
name: "Address with https prefix and path suffix matched",
serverAddress: "registry6.example.com",
want: auth.Credential{
Username: "username6",
Password: "password6",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := cfg.GetCredential(tt.serverAddress)
if (err != nil) != tt.wantErr {
t.Errorf("Config.GetCredential() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Config.GetCredential() = %v, want %v", got, tt.want)
}
})
}
}

func TestConfig_GetCredential_invalidConfig(t *testing.T) {
cfg, err := Load("../../testdata/invalid_auths_entry_config.json")
if err != nil {
Expand Down Expand Up @@ -1314,3 +1402,51 @@ func Test_decodeAuth(t *testing.T) {
})
}
}

func Test_toHostname(t *testing.T) {
tests := []struct {
name string
addr string
want string
}{
{
addr: "http://test.example.com",
want: "test.example.com",
},
{
addr: "http://test.example.com/",
want: "test.example.com",
},
{
addr: "http://test.example.com/foo/bar",
want: "test.example.com",
},
{
addr: "https://test.example.com",
want: "test.example.com",
},
{
addr: "https://test.example.com/",
want: "test.example.com",
},
{
addr: "http://test.example.com/foo/bar",
want: "test.example.com",
},
{
addr: "test.example.com",
want: "test.example.com",
},
{
addr: "test.example.com/foo/bar/",
want: "test.example.com",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := toHostname(tt.addr); got != tt.want {
t.Errorf("toHostname() = %v, want %v", got, tt.want)
}
})
}
}
25 changes: 25 additions & 0 deletions testdata/legacy_auths_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"auths": {
"registry1.example.com": {
"auth": "dXNlcm5hbWUxOnBhc3N3b3JkMQ=="
},
"http://registry2.example.com": {
"auth": "dXNlcm5hbWUyOnBhc3N3b3JkMg=="
},
"https://registry3.example.com": {
"auth": "dXNlcm5hbWUzOnBhc3N3b3JkMw=="
},
"http://registry4.example.com/": {
"auth": "dXNlcm5hbWU0OnBhc3N3b3JkNA=="
},
"https://registry5.example.com/": {
"auth": "dXNlcm5hbWU1OnBhc3N3b3JkNQ=="
},
"https://registry6.example.com/path/": {
"auth": "dXNlcm5hbWU2OnBhc3N3b3JkNg=="
},
"https://registry1.example.com/": {
"auth": "Zm9vOmJhcg=="
}
}
}

0 comments on commit 21321d3

Please sign in to comment.