Skip to content

Commit

Permalink
Merge pull request #2 from tkhq/rno/cli-fs
Browse files Browse the repository at this point in the history
Fix keys path for MacOS, and pull fs ops in their own internal package

Former-commit-id: d3b7793
  • Loading branch information
r-n-o authored Dec 13, 2022
2 parents 50b870b + 9683667 commit 8b9aada
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
@@ -3,7 +3,6 @@ package main
import (
"encoding/json"
"fmt"
"io/fs"
"log"
"net/http"
"os"
@@ -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"
)

@@ -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)
@@ -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,
@@ -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)
}
@@ -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)
}
@@ -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)
@@ -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)
@@ -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)

0 comments on commit 8b9aada

Please sign in to comment.