credentials.go
1 package config 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "os" 8 "path/filepath" 9 10 "codeflow.dananglin.me.uk/apollo/enbas/internal/utilities" 11 ) 12 13 const ( 14 defaultCredentialsFileName = "credentials.json" 15 ) 16 17 type CredentialsConfig struct { 18 CurrentAccount string `json:"currentAccount"` 19 Credentials map[string]Credentials `json:"credentials"` 20 } 21 22 type Credentials struct { 23 Instance string `json:"instance"` 24 ClientID string `json:"clientId"` 25 ClientSecret string `json:"clientSecret"` 26 AccessToken string `json:"accessToken"` 27 } 28 29 type CredentialsNotFoundError struct { 30 AccountName string 31 } 32 33 func (e CredentialsNotFoundError) Error() string { 34 return "unable to find the credentials for the account '" + e.AccountName + "'" 35 } 36 37 // SaveCredentials saves the credentials into the credentials file within the specified configuration 38 // directory. If the directory is not specified then the default directory is used. If the directory 39 // is not present, it will be created. 40 func SaveCredentials(filePath, username string, credentials Credentials) (string, error) { 41 part := filepath.Dir(filePath) 42 43 // ensure that the directory exists. 44 credentialsDir, err := utilities.CalculateConfigDir(part) 45 if err != nil { 46 return "", fmt.Errorf("unable to calculate the directory to your credentials file: %w", err) 47 } 48 49 if err := utilities.EnsureDirectory(credentialsDir); err != nil { 50 return "", fmt.Errorf("unable to ensure the configuration directory: %w", err) 51 } 52 53 var authConfig CredentialsConfig 54 55 if _, err := os.Stat(filePath); err != nil { 56 if !errors.Is(err, os.ErrNotExist) { 57 return "", fmt.Errorf("unknown error received when running stat on %s: %w", filePath, err) 58 } 59 60 authConfig.Credentials = make(map[string]Credentials) 61 } else { 62 authConfig, err = NewCredentialsConfigFromFile(filePath) 63 if err != nil { 64 return "", fmt.Errorf("unable to retrieve the existing authentication configuration: %w", err) 65 } 66 } 67 68 instance := utilities.GetFQDN(credentials.Instance) 69 70 authenticationName := username + "@" + instance 71 72 authConfig.CurrentAccount = authenticationName 73 74 authConfig.Credentials[authenticationName] = credentials 75 76 if err := saveCredentialsConfigFile(authConfig, filePath); err != nil { 77 return "", fmt.Errorf("unable to save the authentication configuration to file: %w", err) 78 } 79 80 return authenticationName, nil 81 } 82 83 // UpdateCurrentAccount updates the name of the current account in the credentials config file. 84 func UpdateCurrentAccount(account string, filePath string) error { 85 credentialsConfig, err := NewCredentialsConfigFromFile(filePath) 86 if err != nil { 87 return fmt.Errorf("unable to retrieve the existing authentication configuration: %w", err) 88 } 89 90 if _, ok := credentialsConfig.Credentials[account]; !ok { 91 return CredentialsNotFoundError{account} 92 } 93 94 credentialsConfig.CurrentAccount = account 95 96 if err := saveCredentialsConfigFile(credentialsConfig, filePath); err != nil { 97 return fmt.Errorf("unable to save the authentication configuration to file: %w", err) 98 } 99 100 return nil 101 } 102 103 // NewCredentialsConfigFromFile creates a new CredentialsConfig value from reading 104 // the credentials file. 105 func NewCredentialsConfigFromFile(path string) (CredentialsConfig, error) { 106 file, err := utilities.OpenFile(path) 107 if err != nil { 108 return CredentialsConfig{}, fmt.Errorf("unable to open %s: %w", path, err) 109 } 110 defer file.Close() 111 112 var authConfig CredentialsConfig 113 114 if err := json.NewDecoder(file).Decode(&authConfig); err != nil { 115 return CredentialsConfig{}, fmt.Errorf("unable to decode the JSON data: %w", err) 116 } 117 118 return authConfig, nil 119 } 120 121 func saveCredentialsConfigFile(authConfig CredentialsConfig, filePath string) error { 122 file, err := utilities.CreateFile(filePath) 123 if err != nil { 124 return fmt.Errorf("unable to create the file at %s: %w", filePath, err) 125 } 126 defer file.Close() 127 128 encoder := json.NewEncoder(file) 129 encoder.SetIndent("", " ") 130 131 if err := encoder.Encode(authConfig); err != nil { 132 return fmt.Errorf("unable to save the JSON data to the authentication config file: %w", err) 133 } 134 135 return nil 136 } 137 138 func defaultCredentialsConfigFile(configDir string) (string, error) { 139 dir, err := utilities.CalculateConfigDir(configDir) 140 if err != nil { 141 return "", fmt.Errorf("unable to calculate the config directory: %w", err) 142 } 143 144 path, err := utilities.AbsolutePath(filepath.Join(dir, defaultCredentialsFileName)) 145 if err != nil { 146 return "", fmt.Errorf("unable to get the absolute path to the credentials config file: %w", err) 147 } 148 149 return path, nil 150 }