goshort/internal/config/config.go
Gustavo Maronato 4fef573447
Some checks failed
Check / checks (push) Failing after 4m11s
added frontend oidc support
2024-03-09 05:42:36 -05:00

224 lines
7.1 KiB
Go

//nolint:forbidigo // Allow printing for prettyprint
package config
import (
"fmt"
"net"
"net/url"
"time"
errs "git.maronato.dev/maronato/goshort/internal/errs"
)
const (
DBTypeMemory = "memory"
DBTypeSQLite = "sqlite"
)
func GetDBTypeList() []string {
return []string{
DBTypeMemory,
DBTypeSQLite,
}
}
type VerboseLevel int
const (
VerboseLevelQuiet VerboseLevel = iota - 1
VerboseLevelInfo
VerboseLevelAccessLogs
VerboseLevelDebug
)
const (
// DefaultProd is the default mode.
DefaultProd = false
// DefaultDBType is the default type of database to use.
DefaultDBType = DBTypeSQLite
// DefaultDBURL is the default connection string for the database.
DefaultDBURL = "goshort.db"
// DefaultPort is the default port to listen on.
DefaultPort = "8080"
// DefaultHost is the default host to listen on. This is set to 0.0.0.0 when running in Docker.
DefaultHost = "localhost"
// DefaultUIPort is the default port to listen on for the UI.
DefaultUIPort = "3000"
// DefaultDebug is the default debug mode.
DefaultDebug = false
// DefaultSessionDuration is the default session duration.
DefaultSessionDuration = 7 * 24 * time.Hour
// DefaultDisableRegistration is the default value for diable registration.
DefaultDisableRegistration = false
// DefaultVerbose is the default verbosity level.
DefaultVerbose = VerboseLevelInfo
// DefaultQuiet is the default quiet mode.
DefaultQuiet = false
// DefaultDisableCredentialsLogin is the default value for diable credential login.
DefaultDisableCredentialsLogin = false
// DefaultOIDCIssuerName is the default name of the OIDC issuer.
DefaultOIDCIssuerName = "OpenID"
)
const (
// ReadTimeout is the maximum duration for reading the entire
// request, including the body.
ReadTimeout = 5 * time.Second
// WriteTimeout is the maximum duration before timing out
// writes of the response.
WriteTimeout = 10 * time.Second
// IdleTimeout is the maximum amount of time to wait for the
// next request when keep-alives are enabled.
IdleTimeout = 30 * time.Second
// ReadHeaderTimeout is the amount of time allowed to read
// request headers.
ReadHeaderTimeout = 2 * time.Second
// RequestTimeout is the maximum duration for the entire
// request.
RequestTimeout = 7 * 24 * time.Hour
)
// Config defines the default configuration for the backend.
type Config struct {
// Prod is a flag that indicates if the server is running in production mode.
Prod bool `json:"prod"`
// Debug is a flag that indicates if the server is running in debug mode.
Debug bool
// Host is the host to listen on.
Host string
// Port is the port to listen on.
Port string
// UiPort is the port to listen on for the UI.
UIPort string
// DBType is the type of database to use.
DBType string
// DBURL is the connection string for the database.
DBURL string
// SessionDuration is the duration of the session.
SessionDuration time.Duration
// DisableRegistration defines whether or not registration are disabled.
DisableRegistration bool `json:"disableRegistration"`
// Verbose defines the verbosity level.
Verbose VerboseLevel `json:"verbose"`
// Quiet defines whether or not the server should be quiet.
Quiet bool
// DisableCredentialsLogin defines whether or not the server should disable credential login.
DisableCredentialsLogin bool `json:"disableCredentialLogin"`
// OIDCIssuerURL is the URL of the OIDC issuer.
OIDCIssuerURL string `json:"oidcIssuerUrl"`
// OIDCIssueName is the name of the OIDC issuer.
OIDCIssuerName string
// OIDCClientID is the client ID for OIDC.
OIDCClientID string
// OIDCClientSecret is the client secret for OIDC.
OIDCClientSecret string
// OIDCRedirectURL is the redirect URL for OIDC.
OIDCRedirectURL string
}
func NewConfig() *Config {
return &Config{
Prod: DefaultProd,
Debug: DefaultDebug,
Host: DefaultHost,
Port: DefaultPort,
UIPort: DefaultUIPort,
DBType: DefaultDBType,
DBURL: DefaultDBURL,
SessionDuration: DefaultSessionDuration,
DisableRegistration: DefaultDisableRegistration,
Verbose: DefaultVerbose,
Quiet: DefaultQuiet,
DisableCredentialsLogin: DefaultDisableCredentialsLogin,
OIDCIssuerName: DefaultOIDCIssuerName,
}
}
// Validate validates the configuration modifies user-inaccessible fields.
func Validate(cfg *Config) error {
// Host and port have to be valid.
if _, err := url.ParseRequestURI("http://" + net.JoinHostPort(cfg.Host, cfg.Port)); err != nil {
return errs.Errorf(fmt.Sprintf("invalid host and/or port: %s", err), errs.ErrInvalidConfig)
}
// UI port has to be valid.
if cfg.UIPort != "" {
if _, err := url.ParseRequestURI("http://" + net.JoinHostPort(cfg.Host, cfg.UIPort)); err != nil {
return errs.Errorf(fmt.Sprintf("invalid UI port: %s", err), errs.ErrInvalidConfig)
}
}
// Verbose has to be valid.
if cfg.Verbose < VerboseLevelInfo || cfg.Verbose > VerboseLevelDebug {
return errs.Errorf(fmt.Sprintf("invalid verbosity level: %d", cfg.Verbose), errs.ErrInvalidConfig)
}
if cfg.Verbose != VerboseLevelInfo && cfg.Quiet {
return errs.Errorf("cannot set both verbose and quiet", errs.ErrInvalidConfig)
}
if cfg.DBType != "" {
// DB type has to be valid.
valid := false
for _, dbType := range GetDBTypeList() {
if cfg.DBType == dbType {
valid = true
break
}
}
if !valid {
return errs.Errorf(fmt.Sprintf("invalid database type: %s", cfg.DBType), errs.ErrInvalidConfig)
}
}
// If verbose == 2, set debug to true for backwards compatibility.
if cfg.Verbose >= VerboseLevelDebug {
cfg.Debug = true
}
// If quiet is true, set verbose to -1.
if cfg.Quiet {
cfg.Verbose = VerboseLevelQuiet
}
// Either OIDC or credential login has to be enabled.
if cfg.DisableCredentialsLogin && cfg.OIDCIssuerURL == "" {
return errs.Errorf("either OIDC or credential login has to be enabled", errs.ErrInvalidConfig)
}
if cfg.OIDCIssuerURL != "" {
// If OIDC is enabled, all OIDC fields have to be set.
if cfg.OIDCClientID == "" || cfg.OIDCClientSecret == "" || cfg.OIDCRedirectURL == "" {
return errs.Errorf("all OIDC fields have to be set", errs.ErrInvalidConfig)
}
// OIDC fields must be valid
if _, err := url.ParseRequestURI(cfg.OIDCIssuerURL); err != nil {
return errs.Errorf(fmt.Sprintf("invalid OIDC issuer URL: %s", err), errs.ErrInvalidConfig)
}
if _, err := url.ParseRequestURI(cfg.OIDCRedirectURL); err != nil {
return errs.Errorf(fmt.Sprintf("invalid OIDC redirect URL: %s", err), errs.ErrInvalidConfig)
}
}
return nil
}
func (c *Config) PrettyPrint() {
fmt.Println("Configuration:")
fmt.Println(" - Production mode:", c.Prod)
fmt.Println(" - Debug mode:", c.Debug)
fmt.Println(" - Host:", c.Host)
fmt.Println(" - Port:", c.Port)
fmt.Println(" - UI Port:", c.UIPort)
fmt.Println(" - Database type:", c.DBType)
fmt.Println(" - Database URL:", c.DBURL)
fmt.Println(" - Session duration:", c.SessionDuration)
fmt.Println(" - Disable registration:", c.DisableRegistration)
fmt.Println(" - Verbose level:", c.Verbose)
fmt.Println(" - Quiet mode:", c.Quiet)
}