mirror of
https://github.com/TwiN/gatus.git
synced 2024-11-24 17:04:42 +01:00
Start working on #8: Support basic authentication for the dashboard
This commit is contained in:
parent
70c9c4b87c
commit
9220a777bb
@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/TwinProduction/gatus/alerting"
|
"github.com/TwinProduction/gatus/alerting"
|
||||||
"github.com/TwinProduction/gatus/alerting/provider"
|
"github.com/TwinProduction/gatus/alerting/provider"
|
||||||
"github.com/TwinProduction/gatus/core"
|
"github.com/TwinProduction/gatus/core"
|
||||||
|
"github.com/TwinProduction/gatus/security"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
@ -18,16 +19,18 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNoServiceInConfig = errors.New("configuration file should contain at least 1 service")
|
ErrNoServiceInConfig = errors.New("configuration file should contain at least 1 service")
|
||||||
ErrConfigFileNotFound = errors.New("configuration file not found")
|
ErrConfigFileNotFound = errors.New("configuration file not found")
|
||||||
ErrConfigNotLoaded = errors.New("configuration is nil")
|
ErrConfigNotLoaded = errors.New("configuration is nil")
|
||||||
config *Config
|
ErrInvalidSecurityConfig = errors.New("invalid security configuration")
|
||||||
|
config *Config
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config is the main configuration structure
|
// Config is the main configuration structure
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Metrics bool `yaml:"metrics"`
|
Metrics bool `yaml:"metrics"`
|
||||||
Debug bool `yaml:"debug"`
|
Debug bool `yaml:"debug"`
|
||||||
|
Security *security.Config `yaml:"security"`
|
||||||
Alerting *alerting.Config `yaml:"alerting"`
|
Alerting *alerting.Config `yaml:"alerting"`
|
||||||
Services []*core.Service `yaml:"services"`
|
Services []*core.Service `yaml:"services"`
|
||||||
}
|
}
|
||||||
@ -83,6 +86,7 @@ func parseAndValidateConfigBytes(yamlBytes []byte) (config *Config, err error) {
|
|||||||
err = ErrNoServiceInConfig
|
err = ErrNoServiceInConfig
|
||||||
} else {
|
} else {
|
||||||
validateAlertingConfig(config)
|
validateAlertingConfig(config)
|
||||||
|
validateSecurityConfig(config)
|
||||||
validateServicesConfig(config)
|
validateServicesConfig(config)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -98,6 +102,20 @@ func validateServicesConfig(config *Config) {
|
|||||||
log.Printf("[config][validateServicesConfig] Validated %d services", len(config.Services))
|
log.Printf("[config][validateServicesConfig] Validated %d services", len(config.Services))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateSecurityConfig(config *Config) {
|
||||||
|
if config.Security != nil {
|
||||||
|
if config.Security.IsValid() {
|
||||||
|
if config.Debug {
|
||||||
|
log.Printf("[config][validateSecurityConfig] Basic security configuration has been validated")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If there was an attempt to configure security, then it must mean that some confidential or private
|
||||||
|
// data are exposed. As a result, we'll force a panic because it's better to be safe than sorry.
|
||||||
|
panic(ErrInvalidSecurityConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func validateAlertingConfig(config *Config) {
|
func validateAlertingConfig(config *Config) {
|
||||||
if config.Alerting == nil {
|
if config.Alerting == nil {
|
||||||
log.Printf("[config][validateAlertingConfig] Alerting is not configured")
|
log.Printf("[config][validateAlertingConfig] Alerting is not configured")
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/TwinProduction/gatus/core"
|
"github.com/TwinProduction/gatus/core"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -217,3 +218,56 @@ services:
|
|||||||
t.Fatal("PagerDuty alerting config should've been invalid")
|
t.Fatal("PagerDuty alerting config should've been invalid")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseAndValidateConfigBytesWithInvalidSecurityConfig(t *testing.T) {
|
||||||
|
defer func() { recover() }()
|
||||||
|
_, _ = parseAndValidateConfigBytes([]byte(`
|
||||||
|
security:
|
||||||
|
basic:
|
||||||
|
username: "admin"
|
||||||
|
password-sha512: "invalid-sha512-hash"
|
||||||
|
services:
|
||||||
|
- name: twinnation
|
||||||
|
url: https://twinnation.org/actuator/health
|
||||||
|
conditions:
|
||||||
|
- "[STATUS] == 200"
|
||||||
|
`))
|
||||||
|
t.Error("Function should've panicked")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseAndValidateConfigBytesWithValidSecurityConfig(t *testing.T) {
|
||||||
|
const expectedUsername = "admin"
|
||||||
|
const expectedPasswordHash = "6b97ed68d14eb3f1aa959ce5d49c7dc612e1eb1dafd73b1e705847483fd6a6c809f2ceb4e8df6ff9984c6298ff0285cace6614bf8daa9f0070101b6c89899e22"
|
||||||
|
config, err := parseAndValidateConfigBytes([]byte(fmt.Sprintf(`
|
||||||
|
security:
|
||||||
|
basic:
|
||||||
|
username: "%s"
|
||||||
|
password-sha512: "%s"
|
||||||
|
services:
|
||||||
|
- name: twinnation
|
||||||
|
url: https://twinnation.org/actuator/health
|
||||||
|
conditions:
|
||||||
|
- "[STATUS] == 200"
|
||||||
|
`, expectedUsername, expectedPasswordHash)))
|
||||||
|
if err != nil {
|
||||||
|
t.Error("No error should've been returned")
|
||||||
|
}
|
||||||
|
if config == nil {
|
||||||
|
t.Fatal("Config shouldn't have been nil")
|
||||||
|
}
|
||||||
|
if config.Security == nil {
|
||||||
|
t.Fatal("config.Security shouldn't have been nil")
|
||||||
|
}
|
||||||
|
if !config.Security.IsValid() {
|
||||||
|
t.Error("Security config should've been valid")
|
||||||
|
}
|
||||||
|
if config.Security.Basic == nil {
|
||||||
|
t.Fatal("config.Security.Basic shouldn't have been nil")
|
||||||
|
}
|
||||||
|
if config.Security.Basic.Username != expectedUsername {
|
||||||
|
t.Errorf("config.Security.Basic.Username should've been %s, but was %s", expectedUsername, config.Security.Basic.Username)
|
||||||
|
}
|
||||||
|
if config.Security.Basic.PasswordSha512Hash != expectedPasswordHash {
|
||||||
|
t.Errorf("config.Security.Basic.PasswordSha512Hash should've been %s, but was %s", expectedPasswordHash, config.Security.Basic.PasswordSha512Hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
18
security/security.go
Normal file
18
security/security.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Basic *BasicConfig `yaml:"basic"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) IsValid() bool {
|
||||||
|
return c.Basic != nil && c.Basic.IsValid()
|
||||||
|
}
|
||||||
|
|
||||||
|
type BasicConfig struct {
|
||||||
|
Username string `yaml:"username"`
|
||||||
|
PasswordSha512Hash string `yaml:"password-sha512"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BasicConfig) IsValid() bool {
|
||||||
|
return len(c.Username) > 0 && len(c.PasswordSha512Hash) == 128
|
||||||
|
}
|
23
security/security_test.go
Normal file
23
security/security_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestBasicConfig_IsValid(t *testing.T) {
|
||||||
|
basicConfig := &BasicConfig{
|
||||||
|
Username: "admin",
|
||||||
|
PasswordSha512Hash: Sha512("test"),
|
||||||
|
}
|
||||||
|
if !basicConfig.IsValid() {
|
||||||
|
t.Error("basicConfig should've been valid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBasicConfig_IsValidWhenPasswordIsInvalid(t *testing.T) {
|
||||||
|
basicConfig := &BasicConfig{
|
||||||
|
Username: "admin",
|
||||||
|
PasswordSha512Hash: "",
|
||||||
|
}
|
||||||
|
if basicConfig.IsValid() {
|
||||||
|
t.Error("basicConfig shouldn't have been valid")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user