Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: validate existence and structure of credentials_file #322

Merged
merged 1 commit into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 39 additions & 23 deletions civo/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ import (
"github.com/civo/terraform-provider-civo/civo/size"
"github.com/civo/terraform-provider-civo/civo/ssh"
"github.com/civo/terraform-provider-civo/civo/volume"
"github.com/civo/terraform-provider-civo/internal/utils"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/mitchellh/go-homedir"
)

var (
Expand Down Expand Up @@ -121,11 +123,11 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
regionValue = region.(string)
}

if token, ok, source := getToken(d); ok {
if token, source, err := getToken(d); err == nil {
tokenValue = token.(string)
tokenSource = source
} else {
return nil, fmt.Errorf("[ERR] No token configuration found in $CIVO_TOKEN or ~/.civo.json. Please go to https://dashboard.civo.com/security to fetch one")
return nil, fmt.Errorf("[ERR] No token configuration found in $CIVO_TOKEN or credentials_file or ~/.civo.json. Please go to https://dashboard.civo.com/security to fetch one: %v", err)
}

if apiEndpoint, ok := d.GetOk("api_endpoint"); ok {
Expand Down Expand Up @@ -160,48 +162,50 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
return client, nil
}

func getToken(d *schema.ResourceData) (interface{}, bool, string) {
var exists = true
func getToken(d *schema.ResourceData) (interface{}, string, error) {

// Gets you the token atrribute value or falls back to reading CIVO_TOKEN environment variable
if token, ok := d.GetOk("token"); ok {
return token, exists, "environment variable"
return token, "environment variable", nil
}

// Check for credentials file specified in provider config
if credFile, ok := d.GetOk("credentials_file"); ok {
token, err := readTokenFromFile(credFile.(string))
path, err := homedir.Expand(credFile.(string))
if err != nil {
return nil, "", fmt.Errorf("error expanding %v: %w", credFile, err)
}
token, err := readTokenFromFile(path)
if err == nil {
return token, exists, "credentials file"
return token, "credentials file", nil
}
return nil, "", fmt.Errorf("error reading from credentials_file: %v", err)
}

// Check for default CLI config file
homeDir, err := getHomeDir()
homeDir, err := homedir.Dir()
if err == nil {
token, err := readTokenFromFile(filepath.Join(homeDir, ".civo.json"))
if err == nil {
return token, exists, "CLI config file"
return token, "CLI config file", nil
}
return nil, "", fmt.Errorf("error reading from ~/.civo.json: %v", err)
}

return nil, !exists, ""
return nil, "", err

}

var getHomeDir = func() (string, error) {
if home := os.Getenv("HOME"); home != "" {
return home, nil
func readTokenFromFile(path string) (string, error) {
// Check file size: 20 MB limit
if err := utils.CheckFileSize(path); err != nil {
return "", err
}
// Fall back to os.UserHomeDir() if HOME is not set
return os.UserHomeDir()
}

func readTokenFromFile(path string) (string, error) {
// read the file
data, err := os.ReadFile(path)

if err != nil {
return "", err
return "", fmt.Errorf("error reading file (%s): %w", path, err)
}

var config struct {
Expand All @@ -211,20 +215,32 @@ func readTokenFromFile(path string) (string, error) {
} `json:"meta"`
}

exampleJSON := `
{
"apikeys": {
"tf_key": "token_here"
},
"meta": {
"current_apikey": "tf_key"
}
}`

if err := json.Unmarshal(data, &config); err != nil {
return "", err
return "", fmt.Errorf("failed to parse JSON from '%s': %w. Please ensure the input JSON file is correctly formatted and all required fields are present. Expected format:\n%s", path, err, exampleJSON)
}

if config.APIKeys == nil || config.Meta.CurrentAPIKey == "" {
return "", fmt.Errorf("invalid structure in '%s', missing required fields. Expected format:\n%s", path, exampleJSON)
}

// Get the current API key name
currentKeyName := config.Meta.CurrentAPIKey

// Fetch the corresponding token
token, ok := config.APIKeys[currentKeyName]

if !ok {
return "", fmt.Errorf("API key '%s' not found", currentKeyName)
return "", fmt.Errorf("API key '%s' not found in '%s'", currentKeyName, path)
}

return token, nil
}

Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ require (
github.com/civo/civogo v0.3.73
github.com/google/uuid v1.3.1
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0
github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.25.0
Expand All @@ -31,6 +30,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.6.0 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/hc-install v0.6.2 // indirect
github.com/hashicorp/hcl/v2 v2.19.1 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
Expand All @@ -45,6 +45,7 @@ require (
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hc-install v0.6.2 h1:V1k+Vraqz4olgZ9UzKiAcbman9i9scg9GgSt/U3mw/M=
github.com/hashicorp/hc-install v0.6.2/go.mod h1:2JBpd+NCFKiHiu/yYCGaPyPHhZLxXTpz8oreHa/a3Ps=
github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI=
Expand Down Expand Up @@ -128,6 +128,8 @@ github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPn
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
Expand Down
23 changes: 23 additions & 0 deletions internal/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ package utils

import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"log"
"os"
"os/exec"
"regexp"
"sort"
Expand All @@ -22,6 +25,11 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
)

const (
// FileSizeLimit limits the size of file to be used by the user
FileSizeLimit = int64(20 * 1024 * 1024) // 20 MB
)

// VersionInfo stores Provider's version Info
type VersionInfo struct {
ProviderSelections map[string]string `json:"provider_selections"`
Expand Down Expand Up @@ -317,3 +325,18 @@ func ValidateUUID(v interface{}, k string) (ws []string, errors []error) {
}
return
}

// CheckFileSize function checks if the file the file size is less than the allowed limit(current: 20MB)
func CheckFileSize(path string) error {
fileInfo, err := os.Stat(path)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("file does not exist: %s", path)
}
return fmt.Errorf("error getting the file info: %w", err)
}
if fileInfo.Size() > FileSizeLimit {
return fmt.Errorf("file size exceeds the allowed limit of %d bytes", FileSizeLimit)
}
return nil
}
Loading