2024-06-02 07:53:13 +01:00
|
|
|
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
2024-02-23 09:44:57 +00:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2024-02-27 09:31:17 +00:00
|
|
|
const (
|
|
|
|
credentialsFileName = "credentials.json"
|
|
|
|
)
|
|
|
|
|
|
|
|
type CredentialsConfig struct {
|
|
|
|
CurrentAccount string `json:"currentAccount"`
|
|
|
|
Credentials map[string]Credentials `json:"credentials"`
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
2024-02-27 09:31:17 +00:00
|
|
|
type Credentials struct {
|
2024-02-23 09:44:57 +00:00
|
|
|
Instance string `json:"instance"`
|
|
|
|
ClientID string `json:"clientId"`
|
|
|
|
ClientSecret string `json:"clientSecret"`
|
|
|
|
AccessToken string `json:"accessToken"`
|
|
|
|
}
|
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
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(configDir, username string, credentials Credentials) (string, error) {
|
|
|
|
if err := ensureConfigDir(calculateConfigDir(configDir)); err != nil {
|
2024-06-02 11:35:43 +01:00
|
|
|
return "", fmt.Errorf("unable to ensure the configuration directory: %w", err)
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
2024-02-27 09:31:17 +00:00
|
|
|
var authConfig CredentialsConfig
|
2024-02-23 09:44:57 +00:00
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
filepath := credentialsConfigFile(configDir)
|
2024-02-23 09:44:57 +00:00
|
|
|
|
|
|
|
if _, err := os.Stat(filepath); err != nil {
|
|
|
|
if !errors.Is(err, os.ErrNotExist) {
|
2024-06-02 11:35:43 +01:00
|
|
|
return "", fmt.Errorf("unknown error received when running stat on %s: %w", filepath, err)
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
2024-02-27 09:31:17 +00:00
|
|
|
authConfig.Credentials = make(map[string]Credentials)
|
2024-02-23 09:44:57 +00:00
|
|
|
} else {
|
2024-05-22 23:30:09 +01:00
|
|
|
authConfig, err = NewCredentialsConfigFromFile(configDir)
|
2024-02-23 09:44:57 +00:00
|
|
|
if err != nil {
|
2024-06-02 11:35:43 +01:00
|
|
|
return "", fmt.Errorf("unable to retrieve the existing authentication configuration: %w", err)
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
instance := ""
|
|
|
|
|
2024-02-27 09:31:17 +00:00
|
|
|
if strings.HasPrefix(credentials.Instance, "https://") {
|
|
|
|
instance = strings.TrimPrefix(credentials.Instance, "https://")
|
|
|
|
} else if strings.HasPrefix(credentials.Instance, "http://") {
|
|
|
|
instance = strings.TrimPrefix(credentials.Instance, "http://")
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
authenticationName := username + "@" + instance
|
|
|
|
|
|
|
|
authConfig.CurrentAccount = authenticationName
|
|
|
|
|
2024-02-27 09:31:17 +00:00
|
|
|
authConfig.Credentials[authenticationName] = credentials
|
2024-02-23 09:44:57 +00:00
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
if err := saveCredentialsConfigFile(authConfig, configDir); err != nil {
|
2024-06-02 11:35:43 +01:00
|
|
|
return "", fmt.Errorf("unable to save the authentication configuration to file: %w", err)
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return authenticationName, nil
|
|
|
|
}
|
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
func UpdateCurrentAccount(account string, configDir string) error {
|
|
|
|
credentialsConfig, err := NewCredentialsConfigFromFile(configDir)
|
2024-02-23 09:44:57 +00:00
|
|
|
if err != nil {
|
2024-06-02 11:35:43 +01:00
|
|
|
return fmt.Errorf("unable to retrieve the existing authentication configuration: %w", err)
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
if _, ok := credentialsConfig.Credentials[account]; !ok {
|
|
|
|
return CredentialsNotFoundError{account}
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
credentialsConfig.CurrentAccount = account
|
2024-02-23 09:44:57 +00:00
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
if err := saveCredentialsConfigFile(credentialsConfig, configDir); err != nil {
|
2024-06-02 11:35:43 +01:00
|
|
|
return fmt.Errorf("unable to save the authentication configuration to file: %w", err)
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
func NewCredentialsConfigFromFile(configDir string) (CredentialsConfig, error) {
|
|
|
|
path := credentialsConfigFile(configDir)
|
2024-02-23 09:44:57 +00:00
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
file, err := os.Open(path)
|
2024-02-23 09:44:57 +00:00
|
|
|
if err != nil {
|
2024-05-22 23:30:09 +01:00
|
|
|
return CredentialsConfig{}, fmt.Errorf("unable to open %s, %w", path, err)
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
2024-05-22 23:30:09 +01:00
|
|
|
defer file.Close()
|
2024-02-23 09:44:57 +00:00
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
var authConfig CredentialsConfig
|
2024-02-23 09:44:57 +00:00
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
if err := json.NewDecoder(file).Decode(&authConfig); err != nil {
|
2024-06-02 11:35:43 +01:00
|
|
|
return CredentialsConfig{}, fmt.Errorf("unable to decode the JSON data: %w", err)
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
return authConfig, nil
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
2024-05-22 23:30:09 +01:00
|
|
|
func saveCredentialsConfigFile(authConfig CredentialsConfig, configDir string) error {
|
|
|
|
path := credentialsConfigFile(configDir)
|
|
|
|
|
|
|
|
file, err := os.Create(path)
|
2024-02-23 09:44:57 +00:00
|
|
|
if err != nil {
|
2024-06-02 11:35:43 +01:00
|
|
|
return fmt.Errorf("unable to open %s: %w", path, err)
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
encoder := json.NewEncoder(file)
|
|
|
|
encoder.SetIndent("", " ")
|
|
|
|
|
|
|
|
if err := encoder.Encode(authConfig); err != nil {
|
2024-06-02 11:35:43 +01:00
|
|
|
return fmt.Errorf("unable to save the JSON data to the authentication config file: %w", err)
|
2024-02-23 09:44:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2024-05-22 23:30:09 +01:00
|
|
|
|
|
|
|
func credentialsConfigFile(configDir string) string {
|
|
|
|
return filepath.Join(calculateConfigDir(configDir), credentialsFileName)
|
|
|
|
}
|