mirror of https://github.com/helm/helm
Merge c55b81e10c
into a32d47e7b0
commit
6b10d446af
@ -0,0 +1,157 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package netrc
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DefaultPath returns the default path to the .netrc file
|
||||
func DefaultPath() string {
|
||||
if os.Getenv("NETRC") != "" {
|
||||
return os.Getenv("NETRC")
|
||||
}
|
||||
return filepath.Join(os.Getenv("HOME"), ".netrc")
|
||||
}
|
||||
|
||||
// Credentials represents a machine entry in .netrc file
|
||||
type Credentials struct {
|
||||
Machine string
|
||||
Login string
|
||||
Password string
|
||||
}
|
||||
|
||||
// GetCredentials returns the credentials for the given URL from .netrc file
|
||||
func GetCredentials(urlStr string) (*Credentials, error) {
|
||||
netrcPath := DefaultPath()
|
||||
if _, err := os.Stat(netrcPath); os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(netrcPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
host := u.Host
|
||||
if strings.Contains(host, ":") {
|
||||
host = strings.Split(host, ":")[0]
|
||||
}
|
||||
|
||||
parser := newParser(string(data))
|
||||
machines, err := parser.parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, m := range machines {
|
||||
if m.Machine == host {
|
||||
return &Credentials{
|
||||
Machine: m.Machine,
|
||||
Login: m.Login,
|
||||
Password: m.Password,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type machine struct {
|
||||
Machine string
|
||||
Login string
|
||||
Password string
|
||||
}
|
||||
|
||||
type parser struct {
|
||||
input string
|
||||
pos int
|
||||
}
|
||||
|
||||
func newParser(input string) *parser {
|
||||
return &parser{input: input}
|
||||
}
|
||||
|
||||
func (p *parser) parse() ([]machine, error) {
|
||||
var machines []machine
|
||||
var current machine
|
||||
|
||||
for p.pos < len(p.input) {
|
||||
token := p.nextToken()
|
||||
if token == "" {
|
||||
break
|
||||
}
|
||||
|
||||
switch token {
|
||||
case "machine":
|
||||
if current.Machine != "" {
|
||||
machines = append(machines, current)
|
||||
current = machine{}
|
||||
}
|
||||
current.Machine = p.nextToken()
|
||||
case "login":
|
||||
current.Login = p.nextToken()
|
||||
case "password":
|
||||
current.Password = p.nextToken()
|
||||
}
|
||||
}
|
||||
|
||||
if current.Machine != "" {
|
||||
machines = append(machines, current)
|
||||
}
|
||||
|
||||
return machines, nil
|
||||
}
|
||||
|
||||
func (p *parser) nextToken() string {
|
||||
// Skip whitespace
|
||||
for p.pos < len(p.input) && (p.input[p.pos] == ' ' || p.input[p.pos] == '\t' || p.input[p.pos] == '\n' || p.input[p.pos] == '\r') {
|
||||
p.pos++
|
||||
}
|
||||
|
||||
if p.pos >= len(p.input) {
|
||||
return ""
|
||||
}
|
||||
|
||||
start := p.pos
|
||||
if p.input[p.pos] == '"' {
|
||||
p.pos++
|
||||
start = p.pos
|
||||
for p.pos < len(p.input) && p.input[p.pos] != '"' {
|
||||
p.pos++
|
||||
}
|
||||
if p.pos < len(p.input) {
|
||||
token := p.input[start:p.pos]
|
||||
p.pos++
|
||||
return token
|
||||
}
|
||||
} else {
|
||||
for p.pos < len(p.input) && p.input[p.pos] != ' ' && p.input[p.pos] != '\t' && p.input[p.pos] != '\n' && p.input[p.pos] != '\r' {
|
||||
p.pos++
|
||||
}
|
||||
}
|
||||
|
||||
return p.input[start:p.pos]
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package netrc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetCredentials(t *testing.T) {
|
||||
// Create a temporary .netrc file
|
||||
content := `machine example.com
|
||||
login testuser
|
||||
password testpass
|
||||
machine other.com
|
||||
login otheruser
|
||||
password otherpass`
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
netrcPath := filepath.Join(tmpDir, ".netrc")
|
||||
if err := os.WriteFile(netrcPath, []byte(content), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Set NETRC env var to point to our test file
|
||||
oldNetrc := os.Getenv("NETRC")
|
||||
defer os.Setenv("NETRC", oldNetrc)
|
||||
os.Setenv("NETRC", netrcPath)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
wantUser string
|
||||
wantPass string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "basic URL",
|
||||
url: "https://example.com/repo",
|
||||
wantUser: "testuser",
|
||||
wantPass: "testpass",
|
||||
},
|
||||
{
|
||||
name: "URL with port",
|
||||
url: "https://example.com:443/repo",
|
||||
wantUser: "testuser",
|
||||
wantPass: "testpass",
|
||||
},
|
||||
{
|
||||
name: "other domain",
|
||||
url: "https://other.com/repo",
|
||||
wantUser: "otheruser",
|
||||
wantPass: "otherpass",
|
||||
},
|
||||
{
|
||||
name: "no match",
|
||||
url: "https://nomatch.com/repo",
|
||||
wantUser: "",
|
||||
wantPass: "",
|
||||
},
|
||||
{
|
||||
name: "invalid URL",
|
||||
url: "://invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := GetCredentials(tt.url)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GetCredentials() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if tt.wantErr {
|
||||
return
|
||||
}
|
||||
if tt.wantUser == "" && tt.wantPass == "" {
|
||||
if got != nil {
|
||||
t.Errorf("GetCredentials() = %v, want nil", got)
|
||||
}
|
||||
return
|
||||
}
|
||||
if got == nil {
|
||||
t.Fatal("GetCredentials() = nil, want credentials")
|
||||
}
|
||||
if got.Login != tt.wantUser {
|
||||
t.Errorf("GetCredentials() username = %v, want %v", got.Login, tt.wantUser)
|
||||
}
|
||||
if got.Password != tt.wantPass {
|
||||
t.Errorf("GetCredentials() password = %v, want %v", got.Password, tt.wantPass)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNetrc(t *testing.T) {
|
||||
content := `# comment line
|
||||
machine example.com
|
||||
login user1
|
||||
password pass1
|
||||
machine other.com login user2 password pass2
|
||||
machine "quoted.com"
|
||||
login "user 3"
|
||||
password "pass 3"
|
||||
`
|
||||
p := newParser(content)
|
||||
machines, err := p.parse()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(machines) != 3 {
|
||||
t.Errorf("Expected 3 machines, got %d", len(machines))
|
||||
}
|
||||
|
||||
expected := []machine{
|
||||
{Machine: "example.com", Login: "user1", Password: "pass1"},
|
||||
{Machine: "other.com", Login: "user2", Password: "pass2"},
|
||||
{Machine: "quoted.com", Login: "user 3", Password: "pass 3"},
|
||||
}
|
||||
|
||||
for i, e := range expected {
|
||||
if machines[i] != e {
|
||||
t.Errorf("machine[%d] = %v, want %v", i, machines[i], e)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package registry
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/pkg/netrc"
|
||||
)
|
||||
|
||||
func TestClientNetrcAuth(t *testing.T) {
|
||||
// Create a temporary .netrc file
|
||||
content := `machine example.com
|
||||
login testuser
|
||||
password testpass`
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
netrcPath := filepath.Join(tmpDir, ".netrc")
|
||||
if err := os.WriteFile(netrcPath, []byte(content), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Set NETRC env var to point to our test file
|
||||
oldNetrc := os.Getenv("NETRC")
|
||||
defer os.Setenv("NETRC", oldNetrc)
|
||||
os.Setenv("NETRC", netrcPath)
|
||||
|
||||
// Create a new client
|
||||
client, err := NewClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test that credentials from .netrc are used
|
||||
creds, err := netrc.GetCredentials("https://example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if creds == nil {
|
||||
t.Fatal("Expected credentials from .netrc, got nil")
|
||||
}
|
||||
if creds.Login != "testuser" {
|
||||
t.Errorf("Expected username 'testuser', got '%s'", creds.Login)
|
||||
}
|
||||
if creds.Password != "testpass" {
|
||||
t.Errorf("Expected password 'testpass', got '%s'", creds.Password)
|
||||
}
|
||||
|
||||
// Test that explicit credentials override .netrc
|
||||
client, err = NewClient(
|
||||
ClientOptBasicAuth("explicituser", "explicitpass"),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if client.username != "explicituser" {
|
||||
t.Errorf("Expected username 'explicituser', got '%s'", client.username)
|
||||
}
|
||||
if client.password != "explicitpass" {
|
||||
t.Errorf("Expected password 'explicitpass', got '%s'", client.password)
|
||||
}
|
||||
}
|
Loading…
Reference in new issue