Rewrite webhook config structure

It now allows more than just secret/secretFile.

Signed-off-by: Steven Kriegler <61625851+justusbunsi@users.noreply.github.com>
This commit is contained in:
justusbunsi 2021-06-21 11:00:31 +02:00
parent 71b19c35b8
commit 9fc9323f23
No known key found for this signature in database
GPG key ID: 990B348ECAC9C7DB
3 changed files with 114 additions and 82 deletions

View file

@ -11,10 +11,10 @@ gitea:
# request will be ignored. # request will be ignored.
# The bot looks for `X-Gitea-Signature` header containing the sha256 hmac hash of the plain text secret. If the header # The bot looks for `X-Gitea-Signature` header containing the sha256 hmac hash of the plain text secret. If the header
# exists and no webhookSecret is defined here, the bot will ignore the request, because it cannot be validated. # exists and no webhookSecret is defined here, the bot will ignore the request, because it cannot be validated.
webhookSecret: webhook:
value: "" secret: ""
# # or path to file containing the plain text secret # # or path to file containing the plain text secret
# file: /path/to/gitea/webhook/secret # secretFile: /path/to/gitea/webhook/secret
# List of repository the used Gitea account has access to and shall be handled by the bot. Other repository webhooks # List of repository the used Gitea account has access to and shall be handled by the bot. Other repository webhooks
# will be ignored. # will be ignored.
@ -42,10 +42,10 @@ sonarqube:
# The bot looks for `X-Sonar-Webhook-HMAC-SHA256` header containing the sha256 hmac hash of the plain text secret. # The bot looks for `X-Sonar-Webhook-HMAC-SHA256` header containing the sha256 hmac hash of the plain text secret.
# If the header exists and no webhookSecret is defined here, the bot will ignore the request, because it cannot be # If the header exists and no webhookSecret is defined here, the bot will ignore the request, because it cannot be
# validated. # validated.
webhookSecret: webhook:
value: "" secret: ""
# # or path to file containing the plain text secret # # or path to file containing the plain text secret
# file: /path/to/sonarqube/webhook/secret # secretFile: /path/to/sonarqube/webhook/secret
# List of project keys from inside SonarQube that should be handled. Webhooks containing other projects will be ignored. # List of project keys from inside SonarQube that should be handled. Webhooks containing other projects will be ignored.
# If empty array given, don't filter requests for repositories and proceed them all. # If empty array given, don't filter requests for repositories and proceed them all.

View file

@ -13,22 +13,22 @@ type GiteaRepository struct {
Name string Name string
} }
type WebhookSecret struct { type Webhook struct {
Value string Secret string
File string SecretFile string
} }
type GiteaConfig struct { type GiteaConfig struct {
Url string Url string
Token string Token string
WebhookSecret WebhookSecret `mapstructure:"webhookSecret"` Webhook Webhook `mapstructure:"webhook"`
Repositories []GiteaRepository Repositories []GiteaRepository
} }
type SonarQubeConfig struct { type SonarQubeConfig struct {
Url string Url string
Token string Token string
WebhookSecret WebhookSecret `mapstructure:"webhookSecret"` Webhook Webhook `mapstructure:"webhook"`
Projects []string Projects []string
} }
@ -51,13 +51,13 @@ func init() {
func ApplyConfigDefaults() { func ApplyConfigDefaults() {
viper.SetDefault("gitea.url", "") viper.SetDefault("gitea.url", "")
viper.SetDefault("gitea.token", "") viper.SetDefault("gitea.token", "")
viper.SetDefault("gitea.webhookSecret.value", "") viper.SetDefault("gitea.webhook.secret", "")
viper.SetDefault("gitea.webhookSecret.file", "") viper.SetDefault("gitea.webhook.secretFile", "")
viper.SetDefault("gitea.repositories", []GiteaRepository{}) viper.SetDefault("gitea.repositories", []GiteaRepository{})
viper.SetDefault("sonarqube.url", "") viper.SetDefault("sonarqube.url", "")
viper.SetDefault("sonarqube.token", "") viper.SetDefault("sonarqube.token", "")
viper.SetDefault("sonarqube.webhookSecret.value", "") viper.SetDefault("sonarqube.webhook.secret", "")
viper.SetDefault("sonarqube.webhookSecret.file", "") viper.SetDefault("sonarqube.webhook.secretFile", "")
viper.SetDefault("sonarqube.projects", []string{}) viper.SetDefault("sonarqube.projects", []string{})
} }
@ -91,11 +91,11 @@ func Load(configPath string) {
Gitea = fullConfig.Gitea Gitea = fullConfig.Gitea
SonarQube = fullConfig.SonarQube SonarQube = fullConfig.SonarQube
if Gitea.WebhookSecret.File != "" { if Gitea.Webhook.SecretFile != "" {
Gitea.WebhookSecret.Value = ReadSecretFile(Gitea.WebhookSecret.File) Gitea.Webhook.Secret = ReadSecretFile(Gitea.Webhook.SecretFile)
} }
if SonarQube.WebhookSecret.File != "" { if SonarQube.Webhook.SecretFile != "" {
SonarQube.WebhookSecret.Value = ReadSecretFile(SonarQube.WebhookSecret.File) SonarQube.Webhook.Secret = ReadSecretFile(SonarQube.Webhook.SecretFile)
} }
} }

View file

@ -13,25 +13,16 @@ var defaultConfigInlineSecrets []byte = []byte(
`gitea: `gitea:
url: https://example.com/gitea url: https://example.com/gitea
token: d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565 token: d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565
webhookSecret: webhook:
value: haxxor-gitea-secret secret: haxxor-gitea-secret
repositories: [] repositories:
sonarqube: - owner: some-owner
url: https://example.com/sonarqube name: a-repository-name
token: a09eb5785b25bb2cbacf48808a677a0709f02d8e
webhookSecret:
value: haxxor-sonarqube-secret
projects: []
`)
var incompleteConfig []byte = []byte(
`gitea:
url: https://example.com/gitea
webhookSecret:
value: haxxor-gitea-secret
sonarqube: sonarqube:
url: https://example.com/sonarqube url: https://example.com/sonarqube
token: a09eb5785b25bb2cbacf48808a677a0709f02d8e token: a09eb5785b25bb2cbacf48808a677a0709f02d8e
webhook:
secret: haxxor-sonarqube-secret
projects: [] projects: []
`) `)
@ -63,17 +54,22 @@ func TestLoadGiteaStructure(t *testing.T) {
expected := GiteaConfig{ expected := GiteaConfig{
Url: "https://example.com/gitea", Url: "https://example.com/gitea",
Token: "d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565", Token: "d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565",
WebhookSecret: WebhookSecret{ Webhook: Webhook{
Value: "haxxor-gitea-secret", Secret: "haxxor-gitea-secret",
},
Repositories: []GiteaRepository{
GiteaRepository{
Owner: "some-owner",
Name: "a-repository-name",
},
}, },
Repositories: []GiteaRepository{},
} }
assert.EqualValues(t, expected, Gitea) assert.EqualValues(t, expected, Gitea)
} }
func TestLoadGiteaStructureWithEnvInjectedWebhookSecret(t *testing.T) { func TestLoadGiteaStructureInjectedEnvs(t *testing.T) {
os.Setenv("PRBOT_GITEA_WEBHOOKSECRET_VALUE", "injected-secret") os.Setenv("PRBOT_GITEA_WEBHOOK_SECRET", "injected-webhook-secret")
os.Setenv("PRBOT_GITEA_TOKEN", "injected-token") os.Setenv("PRBOT_GITEA_TOKEN", "injected-token")
WriteConfigFile(t, defaultConfigInlineSecrets) WriteConfigFile(t, defaultConfigInlineSecrets)
Load(os.TempDir()) Load(os.TempDir())
@ -81,37 +77,90 @@ func TestLoadGiteaStructureWithEnvInjectedWebhookSecret(t *testing.T) {
expected := GiteaConfig{ expected := GiteaConfig{
Url: "https://example.com/gitea", Url: "https://example.com/gitea",
Token: "injected-token", Token: "injected-token",
WebhookSecret: WebhookSecret{ Webhook: Webhook{
Value: "injected-secret", Secret: "injected-webhook-secret",
},
Repositories: []GiteaRepository{
GiteaRepository{
Owner: "some-owner",
Name: "a-repository-name",
},
}, },
Repositories: []GiteaRepository{},
} }
assert.EqualValues(t, expected, Gitea) assert.EqualValues(t, expected, Gitea)
t.Cleanup(func() { t.Cleanup(func() {
os.Unsetenv("PRBOT_GITEA_WEBHOOKSECRET_VALUE") os.Unsetenv("PRBOT_GITEA_WEBHOOK_SECRET")
os.Unsetenv("PRBOT_GITEA_TOKEN") os.Unsetenv("PRBOT_GITEA_TOKEN")
}) })
} }
func TestLoadStructureWithResolvedWebhookFileFromEnvInjected(t *testing.T) { func TestLoadSonarQubeStructure(t *testing.T) {
secretFile := path.Join(os.TempDir(), "webhook-secret-sonarqube") WriteConfigFile(t, defaultConfigInlineSecrets)
_ = ioutil.WriteFile(secretFile, []byte(`totally-secret`),0444)
os.Setenv("PRBOT_GITEA_WEBHOOKSECRET_FILE", secretFile)
os.Setenv("PRBOT_SONARQUBE_WEBHOOKSECRET_FILE", secretFile)
os.Setenv("PRBOT_GITEA_TOKEN", "injected-token")
WriteConfigFile(t, incompleteConfig)
Load(os.TempDir()) Load(os.TempDir())
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
Token: "a09eb5785b25bb2cbacf48808a677a0709f02d8e",
Webhook: Webhook{
Secret: "haxxor-sonarqube-secret",
},
Projects: []string{},
}
assert.EqualValues(t, expected, SonarQube)
}
func TestLoadSonarQubeStructureInjectedEnvs(t *testing.T) {
os.Setenv("PRBOT_SONARQUBE_WEBHOOK_SECRET", "injected-webhook-secret")
os.Setenv("PRBOT_SONARQUBE_TOKEN", "injected-token")
WriteConfigFile(t, defaultConfigInlineSecrets)
Load(os.TempDir())
expected := SonarQubeConfig{
Url: "https://example.com/sonarqube",
Token: "injected-token",
Webhook: Webhook{
Secret: "injected-webhook-secret",
},
Projects: []string{},
}
assert.EqualValues(t, expected, SonarQube)
t.Cleanup(func() {
os.Unsetenv("PRBOT_SONARQUBE_WEBHOOK_SECRET")
os.Unsetenv("PRBOT_SONARQUBE_TOKEN")
})
}
func TestLoadStructureWithFileReferenceResolving(t *testing.T) {
giteaSecretFile := path.Join(os.TempDir(), "webhook-secret-gitea")
sonarqubeSecretFile := path.Join(os.TempDir(), "webhook-secret-sonarqube")
_ = ioutil.WriteFile(giteaSecretFile, []byte(`gitea-totally-secret`),0444)
_ = ioutil.WriteFile(sonarqubeSecretFile, []byte(`sonarqube-totally-secret`),0444)
WriteConfigFile(t, []byte(
`gitea:
url: https://example.com/gitea
token: d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565
repositories: []
sonarqube:
url: https://example.com/sonarqube
token: a09eb5785b25bb2cbacf48808a677a0709f02d8e
projects: []
`))
os.Setenv("PRBOT_GITEA_WEBHOOK_SECRETFILE", giteaSecretFile)
os.Setenv("PRBOT_SONARQUBE_WEBHOOK_SECRETFILE", sonarqubeSecretFile)
expectedGitea := GiteaConfig{ expectedGitea := GiteaConfig{
Url: "https://example.com/gitea", Url: "https://example.com/gitea",
Token: "injected-token", Token: "d0fcdeb5eaa99c506831f9eb4e63fc7cc484a565",
WebhookSecret: WebhookSecret{ Webhook: Webhook{
Value: "totally-secret", Secret: "gitea-totally-secret",
File: secretFile, SecretFile: giteaSecretFile,
}, },
Repositories: []GiteaRepository{}, Repositories: []GiteaRepository{},
} }
@ -119,38 +168,21 @@ func TestLoadStructureWithResolvedWebhookFileFromEnvInjected(t *testing.T) {
expectedSonarQube := SonarQubeConfig{ expectedSonarQube := SonarQubeConfig{
Url: "https://example.com/sonarqube", Url: "https://example.com/sonarqube",
Token: "a09eb5785b25bb2cbacf48808a677a0709f02d8e", Token: "a09eb5785b25bb2cbacf48808a677a0709f02d8e",
WebhookSecret: WebhookSecret{ Webhook: Webhook{
Value: "totally-secret", Secret: "sonarqube-totally-secret",
File: secretFile, SecretFile: sonarqubeSecretFile,
}, },
Projects: []string{}, Projects: []string{},
} }
Load(os.TempDir())
assert.EqualValues(t, expectedGitea, Gitea) assert.EqualValues(t, expectedGitea, Gitea)
assert.EqualValues(t, expectedSonarQube, SonarQube) assert.EqualValues(t, expectedSonarQube, SonarQube)
t.Cleanup(func() { t.Cleanup(func() {
os.Remove(secretFile) os.Remove(giteaSecretFile)
os.Unsetenv("PRBOT_SONARQUBE_WEBHOOKSECRET_FILE") os.Remove(sonarqubeSecretFile)
os.Unsetenv("PRBOT_GITEA_TOKEN") os.Unsetenv("PRBOT_GITEA_WEBHOOK_SECRETFILE")
}) os.Unsetenv("PRBOT_SONARQUBE_WEBHOOK_SECRETFILE")
}
func TestReadSecretFileWhenDirectoryProvided(t *testing.T) {
assert.Panics(t, func() { ReadSecretFile(os.TempDir()) }, "No panic while trying to read content from directory")
}
func TestReadSecretFileWhenMissingFileProvided(t *testing.T) {
assert.Panics(t, func() { ReadSecretFile(path.Join(os.TempDir(), "secret-file")) }, "No panic while trying to read missing file")
}
func TestReadSecretFile(t *testing.T) {
secretFile := path.Join(os.TempDir(), "secret-file")
_ = ioutil.WriteFile(secretFile, []byte(`awesome-secret-content`),0444)
assert.Equal(t, "awesome-secret-content", ReadSecretFile(secretFile))
t.Cleanup(func() {
os.Remove(secretFile)
}) })
} }