Skip to content

Commit

Permalink
feat: add configuration via config file (#156)
Browse files Browse the repository at this point in the history
Uses a configuration file to change settings in CertGuard

Issue: #79
  • Loading branch information
pimg authored Jan 23, 2025
1 parent 7676a1b commit 0103e2b
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 21 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ With CertGuard it is currently possible to:
![demo](docs/demo.gif)

## File locations
CertGuard uses two file locations:
CertGuard uses following default file locations:
- `~/.cache/certguard` location of the database/storage file
- `~/.cache/certguard/import` import directory for importing CRLs from file
- `~/.local/share/certguard` for the `debug.log` file
- `~/.config/certguard` for the `config.yaml` file

All these locations can be changed in the [config](#configuration) file.

## Themes
CertGuard has predefined themes that can be switched using the `--theme` argument. Currently supported themes are:
Expand Down Expand Up @@ -55,6 +58,17 @@ All information on CRL's and revoked certificates are stored on a local SQLite d
The Database schema used for Certguard only stores public information:
![database schema](docs/db_schema.svg)

## Configuration
CertGuard can be configured using one of three ways:
1. command line flags
2. environment variables
3. config file

The precedence is `command line flags` > `environment variables` > `config file` > `defaults`

A sample config file is included in the repo: `config.yaml`
The default locations CertGuard looks for the config file are the current directory (`.`) and `$HOME/.config/certguard`

## Development
A MAKE file has been included for convenience:
- `make run` builds and run the `certguard` application in `debug` mode
Expand Down
72 changes: 60 additions & 12 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"path/filepath"

tea "github.com/charmbracelet/bubbletea"
"github.com/pimg/certguard/config"
"github.com/pimg/certguard/internal/adapter/db"
"github.com/pimg/certguard/internal/ports/models"
cmds "github.com/pimg/certguard/internal/ports/models/commands"
Expand All @@ -17,31 +18,39 @@ import (
"github.com/spf13/cobra"
)

var v *config.ViperConfig

func init() {
rootCmd.PersistentFlags().Bool("debug", false, "enables debug logging to a file located in ~/.local/certguard/debug.log")
rootCmd.PersistentFlags().String("theme", "dracula", "set the theme of the application. Allowed values: 'dracula', 'gruvbox'")
v = config.NewViperConfig()
rootCmd.PersistentFlags().BoolVarP(&v.Config().Log.Debug, "debug", "d", false, "enables debug logging to a file located in ~/.local/certguard/debug.log")
rootCmd.PersistentFlags().StringVarP(&v.Config().Theme.Name, "theme", "t", "dracula", "set the theme of the application. Allowed values: 'dracula', 'gruvbox'")

// bind Cobra flags to viper config
_ = v.BindPFlag("config.theme.name", rootCmd.PersistentFlags().Lookup("theme"))
_ = v.BindPFlag("config.log.debug", rootCmd.PersistentFlags().Lookup("debug"))
}

var rootCmd = &cobra.Command{
Version: "v0.0.1",
Use: "certguard",
Long: "Certguard can download, store and inspect x.509 Certificate Revocation Lists",
Example: "certguard",
RunE: runInteractiveCertGuard,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return v.InitializeConfig()
},
RunE: runInteractiveCertGuard,
}

func runInteractiveCertGuard(cmd *cobra.Command, args []string) error {
debug, _ := cmd.Flags().GetBool("debug")
theme, _ := cmd.Flags().GetString("theme")
debug := v.Config().Log.Debug
theme := v.Config().Theme.Name

if debug {
homeDir, err := os.UserHomeDir()
logDir, err := logDir()
if err != nil {
fmt.Println("fatal:", err)
os.Exit(1)
return err
}

logDir := filepath.Join(homeDir, ".local", "share", "certguard")
err = os.MkdirAll(logDir, 0o777)
if err != nil {
return err
Expand All @@ -55,7 +64,12 @@ func runInteractiveCertGuard(cmd *cobra.Command, args []string) error {
defer f.Close()
}

cacheDir, err := determineCacheDir()
cacheDir, err := cacheDir()
if err != nil {
return err
}

importDir, err := importDir()
if err != nil {
return err
}
Expand Down Expand Up @@ -86,7 +100,7 @@ func runInteractiveCertGuard(cmd *cobra.Command, args []string) error {
return err
}

storage, err := crl.NewStorage(libsqlStorage, cacheDir)
storage, err := crl.NewStorage(libsqlStorage, cacheDir, importDir)
if err != nil {
return err
}
Expand All @@ -107,7 +121,41 @@ func Execute() error {
return rootCmd.Execute()
}

func determineCacheDir() (string, error) {
func cacheDir() (string, error) {
if v.Config().CacheDirectory != "" {
return v.Config().CacheDirectory, nil
}

return defaultDir()
}

func importDir() (string, error) {
if v.Config().ImportDirectory != "" {
return v.Config().ImportDirectory, nil
}

dir, err := defaultDir()
if err != nil {
return "", err
}

return filepath.Join(dir, "import"), nil
}

func logDir() (string, error) {
if v.Config().Log.Directory != "" {
return v.Config().Log.Directory, nil
}

homeDir, err := os.UserHomeDir()
if err != nil {
return "", errors.New("could not create file path to User home dir, logging will not be enabled")
}

return filepath.Join(homeDir, ".local", "share", "certguard"), nil
}

func defaultDir() (string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", errors.New("could not create file path to User home dir, Cache will not be enabled")
Expand Down
5 changes: 5 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
config:
theme:
name: gruvbox
log:
debug: true
21 changes: 21 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package config

type Config struct {
CacheDirectory string
ImportDirectory string
Log Log
Theme Theme
}

type Log struct {
Debug bool
Directory string
}

type Theme struct {
Name string
}

func New() *Config {
return &Config{}
}
67 changes: 67 additions & 0 deletions config/file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package config

import (
"strings"

"github.com/spf13/viper"
)

const (
configFileName = "config"
envPrefix = "CG"
)

type ViperConfig struct {
*viper.Viper
cfg *Config
}

func NewViperConfig() *ViperConfig {
return &ViperConfig{viper.New(), New()}
}

func (v *ViperConfig) Config() *Config {
return v.cfg
}

func (v *ViperConfig) InitializeConfig() error {
// Set the base name of the config file, without the file extension.
v.SetConfigName(configFileName)
v.SetConfigType("yaml")

// Set as many paths as you like where viper should look for the
// config file. We are only looking in the current working directory.
v.AddConfigPath(".")
v.AddConfigPath("$HOME/.config/certguard")

// Attempt to read the config file, gracefully ignoring errors
// caused by a config file not being found. Return an error
// if we cannot parse the config file.
if err := v.ReadInConfig(); err != nil {
// It's okay if there isn't a config file
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return err
}
}

// When we bind flags to environment variables expect that the
// environment variables are prefixed, e.g. a flag like --number
// binds to an environment variable STING_NUMBER. This helps
// avoid conflicts.
v.SetEnvPrefix(envPrefix)

// Environment variables can't have dashes in them, so bind them to their equivalent
// keys with underscores, e.g. --favorite-color to STING_FAVORITE_COLOR
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))

// Bind to environment variables
// Works great for simple config names, but needs help for names
// like --favorite-color which we fix in the setFlags function
v.AutomaticEnv()

v.cfg.Theme.Name = v.GetString("config.theme.name")
v.cfg.Log.Debug = v.GetBool("config.log.debug")
v.cfg.Log.Directory = v.GetString("config.log.file")

return nil
}
19 changes: 17 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/charmbracelet/lipgloss v1.0.0
github.com/rubenv/sql-migrate v1.7.1
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.10.0
github.com/tursodatabase/go-libsql v0.0.0-20241113154718-293fe7f21b08
golang.org/x/crypto v0.32.0
Expand All @@ -19,26 +20,40 @@ require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/x/ansi v0.4.5 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240721121621-c0bdc870f11c // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sahilm/fuzzy v0.1.1 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 0103e2b

Please sign in to comment.