Skip to content

Commit

Permalink
Fix keys path for MacOS, and pull fs ops in their own internal package
Browse files Browse the repository at this point in the history
Former-commit-id: f9abc08
  • Loading branch information
r-n-o committed Dec 13, 2022
1 parent 50b870b commit 9683667
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 67 deletions.
76 changes: 76 additions & 0 deletions internal/clifs/clifs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Package to encapsulate CLI filesystem operations
package clifs

import (
"fmt"
"io/fs"
"os"
"strings"

"github.com/pkg/errors"
"github.com/tkhq/tkcli/internal/apikey"
)

// Given a user-specified directory, return the path.
// The logic is in the case where users do not specify a folder.
// If the folder isn't specified, we default to $XDG_CONFIG_HOME/.config/turnkey/keys.
// If this env var isn't set, we default to $HOME/.config/turnkey/keys
// If $HOME isn't set, this function returns an error.
func GetKeyDirPath(userSpecifiedPath string) (string, error) {
if userSpecifiedPath == "" {
var configHome string
if os.Getenv("XDG_CONFIG_HOME") != "" {
configHome = os.Getenv("XDG_CONFIG_HOME")
} else {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", errors.Wrap(err, "error while reading user home directory")
}
configHome = homeDir + "/.config"
}
return configHome + "/turnkey/keys", nil
} else {
if _, err := os.Stat(userSpecifiedPath); !os.IsNotExist(err) {
return userSpecifiedPath, nil
} else {
return "", errors.Errorf("Cannot put key files in %s: %v", userSpecifiedPath, err)
}
}
}

func CreateFile(path string, content string, mode fs.FileMode) error {
return os.WriteFile(path, []byte(content), mode)
}

func GetFileContent(path string) (string, error) {
bytes, err := os.ReadFile(path)
if err != nil {
return "", err
}
return string(bytes), nil
}

func GetApiKey(key string) (*apikey.ApiKey, error) {
var keyPath string
if !strings.Contains(key, "/") && !strings.Contains(key, ".") {
keysDirectory, err := GetKeyDirPath("")
if err != nil {
return nil, errors.Wrap(err, "unable to get keys directory path")
}
keyPath = fmt.Sprintf("%s/%s.private", keysDirectory, key)
} else {
// We have a full file path. Try loading it directly
keyPath = key
}

bytes, err := GetFileContent(keyPath)
if err != nil {
return nil, errors.Wrap(err, "unable to load private key:")
}

apiKey, err := apikey.FromTkPrivateKey(string(bytes))
if err != nil {
return nil, errors.Wrap(err, "could recover API key from private key file content:")
}
return apiKey, nil
}
66 changes: 66 additions & 0 deletions internal/clifs/clifs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package clifs_test

import (
"io/ioutil"
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/tkhq/tkcli/internal/clifs"
)

// MacOSX has $HOME set by default
func TestGetKeyDirPathMacOSX(t *testing.T) {
os.Setenv("HOME", "/home/dir")
defer os.Unsetenv("HOME")

// Need to unset this explicitly: the test runner has this set by default!
originalValue := os.Getenv("XDG_CONFIG_HOME")
os.Unsetenv("XDG_CONFIG_HOME")
defer os.Setenv("XDG_CONFIG_HOME", originalValue)

dir, err := clifs.GetKeyDirPath("")
assert.Nil(t, err)
assert.Equal(t, dir, "/home/dir/.config/turnkey/keys")
}

// On UNIX, we expect XDG_CONFIG_HOME to be set
// If it's not set, we're back to a MacOSX-like system
func TestGetKeyDirPathUnix(t *testing.T) {
os.Setenv("XDG_CONFIG_HOME", "/special/dir")
defer os.Unsetenv("XDG_CONFIG_HOME")

os.Setenv("HOME", "/home/dir")
defer os.Unsetenv("HOME")

dir, err := clifs.GetKeyDirPath("")
assert.Nil(t, err)
assert.Equal(t, dir, "/special/dir/turnkey/keys")
}

// In the case where we don't have a $HOME defined, bail!
func TestGetKeyDirPathDysfunctionalOS(t *testing.T) {
originalValue := os.Getenv("HOME")
os.Unsetenv("HOME")
defer os.Setenv("HOME", originalValue)

dir, err := clifs.GetKeyDirPath("")
assert.Equal(t, dir, "")
assert.Equal(t, "error while reading user home directory: $HOME is not defined", err.Error())
}

// If calling with a path, we should get this back if the path exists
// If not we should get an error
func TestGetKeyDirPathOverride(t *testing.T) {
tmpDir, err := ioutil.TempDir("/tmp", "keys")
defer os.RemoveAll(tmpDir)
assert.Nil(t, err)

dir, err := clifs.GetKeyDirPath("/does/not/exist")
assert.Equal(t, "Cannot put key files in /does/not/exist: stat /does/not/exist: no such file or directory", err.Error())
assert.Equal(t, "", dir)

dir, err = clifs.GetKeyDirPath(tmpDir)
assert.Nil(t, err)
assert.Equal(t, tmpDir, dir)
}
76 changes: 9 additions & 67 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"encoding/json"
"fmt"
"io/fs"
"log"
"net/http"
"os"
Expand All @@ -12,8 +11,10 @@ import (

"github.com/pkg/errors"
"github.com/tkhq/tkcli/internal/apikey"
"github.com/tkhq/tkcli/internal/clifs"
"github.com/tkhq/tkcli/internal/display"
"github.com/tkhq/tkcli/internal/flags"

"github.com/urfave/cli/v2"
)

Expand Down Expand Up @@ -51,7 +52,7 @@ func main() {
fmt.Println(string(jsonBytes))
return nil
} else {
tkDirPath, err := getKeyDirPath(folder)
tkDirPath, err := clifs.GetKeyDirPath(folder)
if err != nil {
log.Fatalln(err)
return cli.Exit("Could not create determine key directory location", 1)
Expand All @@ -66,8 +67,8 @@ func main() {

publicKeyFile := fmt.Sprintf("%s/%s.public", tkDirPath, apiKeyName)
privateKeyFile := fmt.Sprintf("%s/%s.private", tkDirPath, apiKeyName)
createFile(publicKeyFile, apiKey.TkPublicKey, 0755)
createFile(privateKeyFile, apiKey.TkPrivateKey, 0700)
clifs.CreateFile(publicKeyFile, apiKey.TkPublicKey, 0755)
clifs.CreateFile(privateKeyFile, apiKey.TkPrivateKey, 0700)

jsonBytes, err := json.MarshalIndent(map[string]interface{}{
"publicKeyFile": publicKeyFile,
Expand Down Expand Up @@ -108,7 +109,7 @@ func main() {
signaturePayload := apikey.SerializeRequest(method, host, path, body)

key := cCtx.String("key")
apiKey, err := getApiKey(key)
apiKey, err := clifs.GetApiKey(key)
if err != nil {
log.Fatalf("Unable to retrieve API key: %v", err)
}
Expand Down Expand Up @@ -164,7 +165,7 @@ func main() {
signaturePayload := apikey.SerializeRequest(method, host, path, body)

key := cCtx.String("key")
apiKey, err := getApiKey(key)
apiKey, err := clifs.GetApiKey(key)
if err != nil {
log.Fatalf("Unable to retrieve API key: %v", err)
}
Expand Down Expand Up @@ -204,7 +205,7 @@ func main() {

var keyPath string
if !strings.Contains(key, "/") && !strings.Contains(key, ".") {
keysDirectory, err := getKeyDirPath("")
keysDirectory, err := clifs.GetKeyDirPath("")
if err != nil {
log.Fatalln(err)
return cli.Exit("Could not load keys directory path", 1)
Expand All @@ -214,7 +215,7 @@ func main() {
// We have a full file path. Try loading it directly
keyPath = key
}
bytes, err := getFileContent(keyPath)
bytes, err := clifs.GetFileContent(keyPath)
if err != nil {
log.Fatalln(err)
return cli.Exit("Could load private key", 1)
Expand Down Expand Up @@ -251,65 +252,6 @@ func main() {
}
}

// Given a user-specified directory, return the path.
// The logic is in the case where users do not specify a folder.
// If the folder isn't specified, we default to $XDG_CONFIG_HOME/.config/turnkey/keys.
// If this env var isn't set, we default to $HOME/.config/turnkey/keys
// If $HOME isn't set, this function returns an error.
func getKeyDirPath(userSpecifiedPath string) (string, error) {
if userSpecifiedPath == "" {
userConfigDir, err := os.UserConfigDir()
if err != nil {
return "", nil
}
return userConfigDir + "turnkey/keys", nil
} else {
if _, err := os.Stat(userSpecifiedPath); !os.IsNotExist(err) {
return userSpecifiedPath, nil
} else {
return "", errors.Errorf("Cannot put key files in %s: %v", userSpecifiedPath, err)
}
}

}

func createFile(path string, content string, mode fs.FileMode) error {
return os.WriteFile(path, []byte(content), mode)
}

func getFileContent(path string) (string, error) {
bytes, err := os.ReadFile(path)
if err != nil {
return "", err
}
return string(bytes), nil
}

func getApiKey(key string) (*apikey.ApiKey, error) {
var keyPath string
if !strings.Contains(key, "/") && !strings.Contains(key, ".") {
keysDirectory, err := getKeyDirPath("")
if err != nil {
return nil, errors.Wrap(err, "unable to get keys directory path")
}
keyPath = fmt.Sprintf("%s/%s.private", keysDirectory, key)
} else {
// We have a full file path. Try loading it directly
keyPath = key
}

bytes, err := getFileContent(keyPath)
if err != nil {
return nil, errors.Wrap(err, "unable to load private key:")
}

apiKey, err := apikey.FromTkPrivateKey(string(bytes))
if err != nil {
return nil, errors.Wrap(err, "could recover API key from private key file content:")
}
return apiKey, nil
}

func generateCurlCommand(apiKey *apikey.ApiKey, method, host, path, body, signature string) string {
if method == "POST" {
return fmt.Sprintf("curl -X POST -d'%s' -H'%s' -v 'https://%s%s'", body, approvalHeader(apiKey, signature), host, path)
Expand Down

0 comments on commit 9683667

Please sign in to comment.