Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): more env var support #367

Merged
merged 1 commit into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 7 additions & 3 deletions config/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,14 @@ func (s *WebSettingsBasicAuth) String(prefix string) (str string) {
// CheckValues will ensure that the values are SHA256 hashed.
func (ba *WebSettingsBasicAuth) CheckValues() {
// Username
ba.UsernameHash = util.GetHash(ba.Username)
ba.UsernameHash = util.GetHash(util.EvalEnvVars(ba.Username))
// Password
ba.PasswordHash = util.GetHash(ba.Password)
ba.Password = util.FmtHash(ba.PasswordHash)
password := util.EvalEnvVars(ba.Password)
ba.PasswordHash = util.GetHash(password)
if password == ba.Password {
// Password doesn't include an env var, so hash the config val.
ba.Password = util.FmtHash(ba.PasswordHash)
}
}

// FaviconSettings contains the favicon override settings.
Expand Down
146 changes: 133 additions & 13 deletions config/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
func TestSettingsBase_CheckValues(t *testing.T) {
// GIVEN a Settings struct with some values set
tests := map[string]struct {
env map[string]string
had Settings
want Settings
wantUsernameHash string
Expand All @@ -44,19 +45,23 @@ func TestSettingsBase_CheckValues(t *testing.T) {
Web: WebSettings{
BasicAuth: nil}}},
},
"BasicAuth - hashed Username and str Password": {
"BasicAuth - hashed Username and str env Password": {
env: map[string]string{
"TESTSETTINGSBASE_CHECKVALUES_ONE": "ass"},
had: Settings{
SettingsBase: SettingsBase{
Web: WebSettings{
BasicAuth: &WebSettingsBasicAuth{
Username: util.FmtHash(util.GetHash("user")),
Password: "pass"}}}},
Password: "p${TESTSETTINGSBASE_CHECKVALUES_ONE}"}}}},
want: Settings{
SettingsBase: SettingsBase{
Web: WebSettings{
BasicAuth: &WebSettingsBasicAuth{
Username: util.FmtHash(util.GetHash("user")),
Password: util.FmtHash(util.GetHash("pass"))}}}},
Password: "p${TESTSETTINGSBASE_CHECKVALUES_ONE}"}}}},
wantUsernameHash: util.FmtHash(util.GetHash("user")),
wantPasswordHash: util.FmtHash(util.GetHash("pass")),
},
"Favicon - empty": {
had: Settings{
Expand Down Expand Up @@ -88,6 +93,11 @@ func TestSettingsBase_CheckValues(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()

for k, v := range tc.env {
os.Setenv(k, v)
defer os.Unsetenv(k)
}

// WHEN CheckValues is called on it
tc.had.CheckValues()

Expand All @@ -100,14 +110,20 @@ func TestSettingsBase_CheckValues(t *testing.T) {
}
// AND the BasicAuth username and password are hashed (if they exist)
if tc.want.Web.BasicAuth != nil {
wantUsernameHash := util.GetHash(tc.want.Web.BasicAuth.Username)
if tc.had.Web.BasicAuth.UsernameHash != wantUsernameHash {
t.Errorf("want: %x\ngot: %x",
wantUsernameHash := util.FmtHash(util.GetHash(tc.want.Web.BasicAuth.Username))
if tc.wantUsernameHash != "" {
wantUsernameHash = tc.wantUsernameHash
}
if util.FmtHash(tc.had.Web.BasicAuth.UsernameHash) != wantUsernameHash {
t.Errorf("want: %q\ngot: %q",
wantUsernameHash, tc.had.Web.BasicAuth.UsernameHash)
}
wantPasswordHash := util.GetHash(tc.want.Web.BasicAuth.Password)
if tc.had.Web.BasicAuth.PasswordHash != wantPasswordHash {
t.Errorf("want: %x\ngot: %x",
wantPasswordHash := util.FmtHash(util.GetHash(tc.want.Web.BasicAuth.Password))
if tc.wantPasswordHash != "" {
wantPasswordHash = tc.wantPasswordHash
}
if util.FmtHash(tc.had.Web.BasicAuth.PasswordHash) != wantPasswordHash {
t.Errorf("want: %q\ngot: %q",
wantPasswordHash, tc.had.Web.BasicAuth.PasswordHash)
}
}
Expand Down Expand Up @@ -680,8 +696,10 @@ func TestSettings_WebBasicAuthPasswordHash(t *testing.T) {
func TestWebSettings_CheckValues(t *testing.T) {
// GIVEN a WebSettings struct with some values set
tests := map[string]struct {
had WebSettings
want WebSettings
env map[string]string
had WebSettings
want WebSettings
wantUsernameHash string
}{
"BasicAuth - empty": {
had: WebSettings{
Expand Down Expand Up @@ -709,6 +727,34 @@ func TestWebSettings_CheckValues(t *testing.T) {
Username: "user",
Password: util.FmtHash(util.GetHash("pass"))}},
},
"BasicAuth - Username and password from env vars": {
env: map[string]string{
"TESTWEBSETTINGS_CHECKVALUES_ONE": "user",
"TESTWEBSETTINGS_CHECKVALUES_TWO": "pass"},
had: WebSettings{
BasicAuth: &WebSettingsBasicAuth{
Username: "${TESTWEBSETTINGS_CHECKVALUES_ONE}",
Password: "${TESTWEBSETTINGS_CHECKVALUES_TWO}"}},
want: WebSettings{
BasicAuth: &WebSettingsBasicAuth{
Username: "${TESTWEBSETTINGS_CHECKVALUES_ONE}",
Password: "${TESTWEBSETTINGS_CHECKVALUES_TWO}"}},
wantUsernameHash: util.FmtHash(util.GetHash("user")),
},
"BasicAuth - Username and password from env vars partial": {
env: map[string]string{
"TESTWEBSETTINGS_CHECKVALUES_THREE": "er",
"TESTWEBSETTINGS_CHECKVALUES_FOUR": "ss"},
had: WebSettings{
BasicAuth: &WebSettingsBasicAuth{
Username: "us${TESTWEBSETTINGS_CHECKVALUES_THREE}",
Password: "pa${TESTWEBSETTINGS_CHECKVALUES_FOUR}"}},
want: WebSettings{
BasicAuth: &WebSettingsBasicAuth{
Username: "us${TESTWEBSETTINGS_CHECKVALUES_THREE}",
Password: "pa${TESTWEBSETTINGS_CHECKVALUES_FOUR}"}},
wantUsernameHash: util.FmtHash(util.GetHash("user")),
},
"Favicon - empty": {
had: WebSettings{
Favicon: &FaviconSettings{}},
Expand Down Expand Up @@ -747,6 +793,11 @@ func TestWebSettings_CheckValues(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()

for k, v := range tc.env {
os.Setenv(k, v)
defer os.Unsetenv(k)
}

// WHEN CheckValues is called on it
tc.had.CheckValues()

Expand All @@ -757,15 +808,25 @@ func TestWebSettings_CheckValues(t *testing.T) {
t.Errorf("want:\n%v\ngot:\n%v",
wantStr, hadStr)
}
if tc.wantUsernameHash != "" {
got := util.FmtHash(tc.had.BasicAuth.UsernameHash)
if got != tc.wantUsernameHash {
t.Errorf("Username hash\nwant: %q\ngot: %q",
tc.wantUsernameHash, got)
}
}
})
}
}

func TestWebSettingsBasicAuth_CheckValues(t *testing.T) {
// GIVEN a WebSettingsBasicAuth struct with some values set
tests := map[string]struct {
had WebSettingsBasicAuth
want WebSettingsBasicAuth
env map[string]string
had WebSettingsBasicAuth
want WebSettingsBasicAuth
wantUsernameHash string
wantPasswordHash string
}{
"str Username": {
had: WebSettingsBasicAuth{
Expand All @@ -789,6 +850,38 @@ func TestWebSettingsBasicAuth_CheckValues(t *testing.T) {
Username: "user",
Password: util.FmtHash(util.GetHash("pass"))},
},
"str env Web.BasicAuth.Username and str env Web.BasicAuth.Password": {
env: map[string]string{
"TESTWEBSETTINGSBASICAUTH_CHECKVALUES_ONE": "user",
"TESTWEBSETTINGSBASICAUTH_CHECKVALUES_TWO": "pass"},
had: WebSettingsBasicAuth{
Username: "${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_ONE}",
Password: "${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_TWO}"},
want: WebSettingsBasicAuth{
Username: "${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_ONE}",
Password: "${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_TWO}"},
},
"str env partial Web.BasicAuth.Username and str env partial Web.BasicAuth.Password": {
env: map[string]string{
"TESTWEBSETTINGSBASICAUTH_CHECKVALUES_THREE": "user",
"TESTWEBSETTINGSBASICAUTH_CHECKVALUES_FOUR": "pass"},
had: WebSettingsBasicAuth{
Username: "a${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_THREE}",
Password: "b${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_FOUR}"},
want: WebSettingsBasicAuth{
Username: "a${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_THREE}",
Password: "b${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_FOUR}"},
},
"str env undefined Web.BasicAuth.Username and str env undefined Web.BasicAuth.Password": {
had: WebSettingsBasicAuth{
Username: "a${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_UNDEFINED}",
Password: "b${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_UNDEFINED}"},
want: WebSettingsBasicAuth{
Username: "a${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_UNDEFINED}",
Password: util.FmtHash(util.GetHash("b${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_UNDEFINED}"))},
wantUsernameHash: util.FmtHash(util.GetHash("a${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_UNDEFINED}")),
wantPasswordHash: util.FmtHash(util.GetHash("b${TESTWEBSETTINGSBASICAUTH_CHECKVALUES_UNDEFINED}")),
},
"str Web.BasicAuth.Username and Web.BasicAuth.Password already hashed": {
had: WebSettingsBasicAuth{
Username: "user",
Expand Down Expand Up @@ -819,6 +912,11 @@ func TestWebSettingsBasicAuth_CheckValues(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()

for k, v := range tc.env {
os.Setenv(k, v)
defer os.Unsetenv(k)
}

// WHEN CheckValues is called on it
tc.had.CheckValues()

Expand All @@ -829,6 +927,28 @@ func TestWebSettingsBasicAuth_CheckValues(t *testing.T) {
t.Errorf("want:\n%v\ngot:\n%v",
wantStr, hadStr)
}
// AND the UsernameHash is calculated correctly
want := util.FmtHash(util.GetHash(
util.EvalEnvVars(tc.want.Username)))
if tc.wantUsernameHash != "" {
want = tc.wantUsernameHash
}
got := util.FmtHash(tc.had.UsernameHash)
if got != want {
t.Errorf("Username Hash\nwant: %s\ngot: %s",
want, got)
}
// AND the PasswordHash is calculated correctly
want = util.FmtHash(util.GetHash(
util.EvalEnvVars(tc.want.Password)))
if tc.wantPasswordHash != "" {
want = tc.wantPasswordHash
}
got = util.FmtHash(tc.had.PasswordHash)
if got != want {
t.Errorf("Password Hash\nwant: %s\ngot: %s",
want, got)
}
})
}
}
19 changes: 10 additions & 9 deletions service/deployed_version/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,23 +116,23 @@ func TestLookup_Query(t *testing.T) {
wantVersion: "[0-9]{4}",
errRegex: "^$",
url: "https://release-argus.io",
regex: "([0-9]+) The Argus Developers",
regex: `([0-9]+)\s<[^>]+>The Argus Developers`,
},
"url from env": {
env: map[string]string{"TESTLOOKUP_DV_QUERY_ONE": "https://release-argus.io"},
noSemanticVersioning: true,
wantVersion: "[0-9]{4}",
errRegex: "^$",
env: map[string]string{"TESTLOOKUP_DV_QUERY_ONE": "https://release-argus.io"},
url: "${TESTLOOKUP_DV_QUERY_ONE}",
regex: "([0-9]+) The Argus Developers",
regex: `([0-9]+)\s<[^>]+>The Argus Developers`,
},
"url from env partial": {
env: map[string]string{"TESTLOOKUP_DV_QUERY_TWO": "release-argus"},
noSemanticVersioning: true,
wantVersion: "[0-9]{4}",
errRegex: "^$",
env: map[string]string{"TESTLOOKUP_DV_QUERY_TWO": "release-argus"},
url: "https://${TESTLOOKUP_DV_QUERY_TWO}.io",
regex: "([0-9]+) The Argus Developers",
regex: `([0-9]+)\s<[^>]+>The Argus Developers`,
},
"passing regex with no capture group": {
noSemanticVersioning: true,
Expand All @@ -145,7 +145,7 @@ func TestLookup_Query(t *testing.T) {
noSemanticVersioning: true,
errRegex: "^$",
url: "https://release-argus.io",
regex: "([0-9]+) (The) (Argus) (Developers)",
regex: `([0-9]+)\s<[^>]+>(The) (Argus) (Developers)`,
regexTemplate: stringPtr("$2 $1 $4, $3"),
wantVersion: "The [0-9]+ Developers, Argus",
},
Expand All @@ -157,19 +157,19 @@ func TestLookup_Query(t *testing.T) {
"handle non-semantic (only major) version": {
noSemanticVersioning: false,
url: "https://release-argus.io",
regex: "([0-9]+) The Argus Developers",
regex: `([0-9]+)\s<[^>]+>The Argus Developers`,
},
"want semantic versioning but get non-semantic version": {
noSemanticVersioning: false,
errRegex: "failed converting .* to a semantic version",
url: "https://release-argus.io",
regex: "([0-9]+ )The Argus Developers",
regex: `([0-9]+\s)<[^>]+>The Argus Developers`,
},
"allow non-semantic versioning and get non-semantic version": {
noSemanticVersioning: true,
errRegex: "^$",
url: "https://release-argus.io",
regex: "([0-9]+) The Argus Developers",
regex: `([0-9]+\s)<[^>]+>The Argus Developers`,
},
"valid semantic version": {
errRegex: "^$",
Expand All @@ -196,6 +196,7 @@ func TestLookup_Query(t *testing.T) {

for k, v := range tc.env {
os.Setenv(k, v)
defer os.Unsetenv(k)
}
dvl := testLookup()
dvl.URL = tc.url
Expand Down
2 changes: 1 addition & 1 deletion web/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ func testDeployedVersion() *deployedver.Lookup {
var (
allowInvalidCerts = false
json = "something"
regex = "([0-9]+) The Argus Developers"
regex = `([0-9]+)\s<[^>]+>The Argus Developers`
regexTemplate = "v$1"
url = "https://release-argus.io"
)
Expand Down
5 changes: 3 additions & 2 deletions webhook/headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func (w *WebHook) setCustomHeaders(req *http.Request) {
ID: *w.ServiceStatus.ServiceID,
LatestVersion: w.ServiceStatus.LatestVersion()}
for _, header := range *customHeaders {
value := util.TemplateString(header.Value, serviceInfo)
req.Header[header.Key] = []string{value}
key := util.EvalEnvVars(header.Key)
value := util.TemplateString(util.EvalEnvVars(header.Value), serviceInfo)
req.Header[key] = []string{value}
}
}
17 changes: 17 additions & 0 deletions webhook/headers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package webhook
import (
"net/http"
"net/http/httptest"
"os"
"testing"

svcstatus "github.com/release-argus/Argus/service/status"
Expand All @@ -29,6 +30,7 @@ func TestWebHook_SetCustomHeaders(t *testing.T) {
latestVersion := "1.2.3"
serviceID := "service"
tests := map[string]struct {
env map[string]string
root *Headers
main *Headers
dfault *Headers
Expand Down Expand Up @@ -115,12 +117,27 @@ func TestWebHook_SetCustomHeaders(t *testing.T) {
"X-Service": serviceID,
"X-Version": latestVersion},
},
"header with env var": {
env: map[string]string{"FOO": "bar"},
root: &Headers{
{Key: "X-Service", Value: "{{ service_id }}"},
{Key: "X-Version", Value: "{{ version }}"},
{Key: "X-Foo", Value: "b${FOO}r"}},
want: map[string]string{
"X-Service": serviceID,
"X-Version": latestVersion,
"X-Foo": "bbarr"},
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
t.Parallel()

for k, v := range tc.env {
os.Setenv(k, v)
defer os.Unsetenv(k)
}
req := httptest.NewRequest(http.MethodGet, "/approvals", nil)
webhook := WebHook{
ServiceStatus: &svcstatus.Status{
Expand Down