Skip to content

Commit

Permalink
Merge pull request #511 from wasim-nihal/username-file-12576
Browse files Browse the repository at this point in the history
Adding support for file based configuration of basic auth username in http client config
  • Loading branch information
roidelapluie committed Sep 4, 2023
2 parents 94bf982 + ed1ca57 commit 4047c78
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 7 deletions.
32 changes: 25 additions & 7 deletions config/http_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ func (tv *TLSVersion) String() string {
// BasicAuth contains basic HTTP authentication credentials.
type BasicAuth struct {
Username string `yaml:"username" json:"username"`
UsernameFile string `yaml:"username_file,omitempty" json:"username_file,omitempty"`
Password Secret `yaml:"password,omitempty" json:"password,omitempty"`
PasswordFile string `yaml:"password_file,omitempty" json:"password_file,omitempty"`
}
Expand All @@ -139,6 +140,7 @@ func (a *BasicAuth) SetDirectory(dir string) {
return
}
a.PasswordFile = JoinDir(dir, a.PasswordFile)
a.UsernameFile = JoinDir(dir, a.UsernameFile)
}

// Authorization contains HTTP authorization credentials.
Expand Down Expand Up @@ -334,6 +336,9 @@ func (c *HTTPClientConfig) Validate() error {
if (c.BasicAuth != nil || c.OAuth2 != nil) && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) {
return fmt.Errorf("at most one of basic_auth, oauth2, bearer_token & bearer_token_file must be configured")
}
if c.BasicAuth != nil && (string(c.BasicAuth.Username) != "" && c.BasicAuth.UsernameFile != "") {
return fmt.Errorf("at most one of basic_auth username & username_file must be configured")
}
if c.BasicAuth != nil && (string(c.BasicAuth.Password) != "" && c.BasicAuth.PasswordFile != "") {
return fmt.Errorf("at most one of basic_auth password & password_file must be configured")
}
Expand Down Expand Up @@ -555,7 +560,7 @@ func NewRoundTripperFromConfig(cfg HTTPClientConfig, name string, optFuncs ...HT
}

if cfg.BasicAuth != nil {
rt = NewBasicAuthRoundTripper(cfg.BasicAuth.Username, cfg.BasicAuth.Password, cfg.BasicAuth.PasswordFile, rt)
rt = NewBasicAuthRoundTripper(cfg.BasicAuth.Username, cfg.BasicAuth.Password, cfg.BasicAuth.UsernameFile, cfg.BasicAuth.PasswordFile, rt)
}

if cfg.OAuth2 != nil {
Expand Down Expand Up @@ -645,30 +650,43 @@ func (rt *authorizationCredentialsFileRoundTripper) CloseIdleConnections() {
type basicAuthRoundTripper struct {
username string
password Secret
usernameFile string
passwordFile string
rt http.RoundTripper
}

// NewBasicAuthRoundTripper will apply a BASIC auth authorization header to a request unless it has
// already been set.
func NewBasicAuthRoundTripper(username string, password Secret, passwordFile string, rt http.RoundTripper) http.RoundTripper {
return &basicAuthRoundTripper{username, password, passwordFile, rt}
func NewBasicAuthRoundTripper(username string, password Secret, usernameFile, passwordFile string, rt http.RoundTripper) http.RoundTripper {
return &basicAuthRoundTripper{username, password, usernameFile, passwordFile, rt}
}

func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
var username string
var password string
if len(req.Header.Get("Authorization")) != 0 {
return rt.rt.RoundTrip(req)
}
req = cloneRequest(req)
if rt.usernameFile != "" {
usernameBytes, err := os.ReadFile(rt.usernameFile)
if err != nil {
return nil, fmt.Errorf("unable to read basic auth username file %s: %s", rt.usernameFile, err)
}
username = strings.TrimSpace(string(usernameBytes))
} else {
username = rt.username
}
if rt.passwordFile != "" {
bs, err := os.ReadFile(rt.passwordFile)
passwordBytes, err := os.ReadFile(rt.passwordFile)
if err != nil {
return nil, fmt.Errorf("unable to read basic auth password file %s: %s", rt.passwordFile, err)
}
req.SetBasicAuth(rt.username, strings.TrimSpace(string(bs)))
password = strings.TrimSpace(string(passwordBytes))
} else {
req.SetBasicAuth(rt.username, strings.TrimSpace(string(rt.password)))
password = string(rt.password)
}
req = cloneRequest(req)
req.SetBasicAuth(username, password)
return rt.rt.RoundTrip(req)
}

Expand Down
30 changes: 30 additions & 0 deletions config/http_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ var invalidHTTPClientConfigs = []struct {
httpClientConfigFile: "testdata/http.conf.basic-auth.too-much.bad.yaml",
errMsg: "at most one of basic_auth password & password_file must be configured",
},
{
httpClientConfigFile: "testdata/http.conf.basic-auth.bad-username.yaml",
errMsg: "at most one of basic_auth username & username_file must be configured",
},
{
httpClientConfigFile: "testdata/http.conf.mix-bearer-and-creds.bad.yaml",
errMsg: "authorization is not compatible with bearer_token & bearer_token_file",
Expand Down Expand Up @@ -896,6 +900,32 @@ func TestBasicAuthPasswordFile(t *testing.T) {
}
}

func TestBasicUsernameFile(t *testing.T) {
cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.basic-auth.username-file.good.yaml")
if err != nil {
t.Fatalf("Error loading HTTP client config: %v", err)
}
client, err := NewClientFromConfig(*cfg, "test")
if err != nil {
t.Fatalf("Error creating HTTP Client: %v", err)
}

rt, ok := client.Transport.(*basicAuthRoundTripper)
if !ok {
t.Fatalf("Error casting to basic auth transport, %v", client.Transport)
}

if rt.username != "" {
t.Errorf("Bad HTTP client username: %s", rt.username)
}
if string(rt.usernameFile) != "testdata/basic-auth-username" {
t.Errorf("Bad HTTP client usernameFile: %s", rt.usernameFile)
}
if string(rt.passwordFile) != "testdata/basic-auth-password" {
t.Errorf("Bad HTTP client passwordFile: %s", rt.passwordFile)
}
}

func getCertificateBlobs(t *testing.T) map[string][]byte {
files := []string{
TLSCAChainPath,
Expand Down
1 change: 1 addition & 0 deletions config/testdata/basic-auth-username
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
testuser
4 changes: 4 additions & 0 deletions config/testdata/http.conf.basic-auth.bad-username.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
basic_auth:
username: user
username_file: testdata/basic-auth-username
password: foo
3 changes: 3 additions & 0 deletions config/testdata/http.conf.basic-auth.username-file.good.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
basic_auth:
username_file: testdata/basic-auth-username
password_file: testdata/basic-auth-password

0 comments on commit 4047c78

Please sign in to comment.