enbas/internal/config/credentials.go
Dan Anglin d38431d9e8
All checks were successful
Tests / test (pull_request) Successful in 17s
REUSE Compliance Check / check (push) Successful in 6s
fix: error if home config directory not found
Summary:

If the user hasn't supplied the path to a configuration directory and
Enbas fails to find the user's default home configuration directory,
Enbas will now return an error message back to the user. Previously
Enbas would try to, instead, calculate the configuration directory
using the user's current directory.

Changes:

- Return an error if the user's home configuration directory cannot
  be found.
- Add tests to the internal.config package.
- Update the tests in internal.utilities package.

PR: #54
2024-08-18 21:13:08 +01:00

149 lines
4.3 KiB
Go

package config
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
)
const (
defaultCredentialsFileName = "credentials.json"
)
type CredentialsConfig struct {
CurrentAccount string `json:"currentAccount"`
Credentials map[string]Credentials `json:"credentials"`
}
type Credentials struct {
Instance string `json:"instance"`
ClientID string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
AccessToken string `json:"accessToken"`
}
type CredentialsNotFoundError struct {
AccountName string
}
func (e CredentialsNotFoundError) Error() string {
return "unable to find the credentials for the account '" + e.AccountName + "'"
}
// SaveCredentials saves the credentials into the credentials file within the specified configuration
// directory. If the directory is not specified then the default directory is used. If the directory
// is not present, it will be created.
func SaveCredentials(filePath, username string, credentials Credentials) (string, error) {
part := filepath.Dir(filePath)
// ensure that the directory exists.
credentialsDir, err := utilities.CalculateConfigDir(part)
if err != nil {
return "", fmt.Errorf("unable to calculate the directory to your credentials file: %w", err)
}
if err := utilities.EnsureDirectory(credentialsDir); err != nil {
return "", fmt.Errorf("unable to ensure the configuration directory: %w", err)
}
var authConfig CredentialsConfig
if _, err := os.Stat(filePath); err != nil {
if !errors.Is(err, os.ErrNotExist) {
return "", fmt.Errorf("unknown error received when running stat on %s: %w", filePath, err)
}
authConfig.Credentials = make(map[string]Credentials)
} else {
authConfig, err = NewCredentialsConfigFromFile(filePath)
if err != nil {
return "", fmt.Errorf("unable to retrieve the existing authentication configuration: %w", err)
}
}
instance := utilities.GetFQDN(credentials.Instance)
authenticationName := username + "@" + instance
authConfig.CurrentAccount = authenticationName
authConfig.Credentials[authenticationName] = credentials
if err := saveCredentialsConfigFile(authConfig, filePath); err != nil {
return "", fmt.Errorf("unable to save the authentication configuration to file: %w", err)
}
return authenticationName, nil
}
func UpdateCurrentAccount(account string, filePath string) error {
credentialsConfig, err := NewCredentialsConfigFromFile(filePath)
if err != nil {
return fmt.Errorf("unable to retrieve the existing authentication configuration: %w", err)
}
if _, ok := credentialsConfig.Credentials[account]; !ok {
return CredentialsNotFoundError{account}
}
credentialsConfig.CurrentAccount = account
if err := saveCredentialsConfigFile(credentialsConfig, filePath); err != nil {
return fmt.Errorf("unable to save the authentication configuration to file: %w", err)
}
return nil
}
// NewCredentialsConfigFromFile creates a new CredentialsConfig value from reading
// the credentials file.
func NewCredentialsConfigFromFile(filePath string) (CredentialsConfig, error) {
file, err := os.Open(filePath)
if err != nil {
return CredentialsConfig{}, fmt.Errorf("unable to open %s: %w", filePath, err)
}
defer file.Close()
var authConfig CredentialsConfig
if err := json.NewDecoder(file).Decode(&authConfig); err != nil {
return CredentialsConfig{}, fmt.Errorf("unable to decode the JSON data: %w", err)
}
return authConfig, nil
}
func saveCredentialsConfigFile(authConfig CredentialsConfig, filePath string) error {
file, err := os.Create(filePath)
if err != nil {
return fmt.Errorf("unable to create the file at %s: %w", filePath, err)
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
if err := encoder.Encode(authConfig); err != nil {
return fmt.Errorf("unable to save the JSON data to the authentication config file: %w", err)
}
return nil
}
func defaultCredentialsConfigFile(configDir string) (string, error) {
dir, err := utilities.CalculateConfigDir(configDir)
if err != nil {
return "", fmt.Errorf("unable to calculate the config directory: %w", err)
}
path, err := utilities.AbsolutePath(filepath.Join(dir, defaultCredentialsFileName))
if err != nil {
return "", fmt.Errorf("unable to get the absolute path to the credentials config file: %w", err)
}
return path, nil
}