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

[knox] add no cache mode #103

Merged
merged 15 commits into from
Mar 1, 2024
184 changes: 138 additions & 46 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,26 +195,17 @@ type HTTP interface {

// HTTPClient is a client that uses HTTP to talk to Knox.
type HTTPClient struct {
// Host is used as the host for http connections
Host string
//AuthHandler returns the authorization string for authenticating to knox. Users should be prefixed by 0u, machines by 0m. On fail, return empty string.
AuthHandler func() string
// KeyFolder is the location of cached keys on the file system. If empty, does not check for cached keys.
KeyFolder string
// Client is the http client for making network calls
Client HTTP
// Version is the current client version, useful for debugging and sent as a header
Version string
UncachedClient UncachedHTTPClient
}

// NewClient creates a new client to connect to talk to Knox.
func NewClient(host string, client HTTP, authHandler func() string, keyFolder, version string) APIClient {
return &HTTPClient{
Host: host,
Client: client,
AuthHandler: authHandler,
KeyFolder: keyFolder,
Version: version,
KeyFolder: keyFolder,
UncachedClient: NewUncachedClient(host, client, authHandler, version),
}
}

Expand Down Expand Up @@ -244,18 +235,7 @@ func (c *HTTPClient) CacheGetKey(keyID string) (*Key, error) {

// NetworkGetKey gets a knox key by keyID and only uses network without the caches.
func (c *HTTPClient) NetworkGetKey(keyID string) (*Key, error) {
key := &Key{}
err := c.getHTTPData("GET", "/v0/keys/"+keyID+"/", nil, key)
if err != nil {
return nil, err
}

// do not return the invalid format remote keys
if key.ID == "" || key.ACL == nil || key.VersionList == nil || key.VersionHash == "" {
return nil, fmt.Errorf("invalid key content for the remote key")
}

return key, err
return c.UncachedClient.NetworkGetKey(keyID)
}

// GetKey gets a knox key by keyID.
Expand Down Expand Up @@ -292,14 +272,7 @@ func (c *HTTPClient) CacheGetKeyWithStatus(keyID string, status VersionStatus) (
// NetworkGetKeyWithStatus gets a knox key by keyID and given version status (always calls network).
func (c *HTTPClient) NetworkGetKeyWithStatus(keyID string, status VersionStatus) (*Key, error) {
// If clients need to know
krockpot marked this conversation as resolved.
Show resolved Hide resolved
s, err := status.MarshalJSON()
if err != nil {
return nil, err
}

key := &Key{}
err = c.getHTTPData("GET", "/v0/keys/"+keyID+"/?status="+string(s), nil, key)
return key, err
return c.UncachedClient.NetworkGetKeyWithStatus(keyID, status)
}

// GetKeyWithStatus gets a knox key by keyID and status (leverages cache).
Expand All @@ -313,6 +286,123 @@ func (c *HTTPClient) GetKeyWithStatus(keyID string, status VersionStatus) (*Key,

// CreateKey creates a knox key with given keyID data and ACL.
func (c *HTTPClient) CreateKey(keyID string, data []byte, acl ACL) (uint64, error) {
return c.UncachedClient.CreateKey(keyID, data, acl)
}

// GetKeys gets all Knox (if empty map) or gets all keys in map that do not match key version hash.
func (c *HTTPClient) GetKeys(keys map[string]string) ([]string, error) {
return c.UncachedClient.GetKeys(keys)
}

// DeleteKey deletes a key from Knox.
func (c HTTPClient) DeleteKey(keyID string) error {
return c.UncachedClient.DeleteKey(keyID)
}

// GetACL gets a knox key by keyID.
func (c *HTTPClient) GetACL(keyID string) (*ACL, error) {
return c.UncachedClient.GetACL(keyID)
}

// PutAccess will add an ACL rule to a specific key.
func (c *HTTPClient) PutAccess(keyID string, a ...Access) error {
return c.UncachedClient.PutAccess(keyID, a...)
}

// AddVersion adds a key version to a specific key.
func (c *HTTPClient) AddVersion(keyID string, data []byte) (uint64, error) {
return c.UncachedClient.AddVersion(keyID, data)
}

// UpdateVersion either promotes or demotes a specific key version.
func (c *HTTPClient) UpdateVersion(keyID, versionID string, status VersionStatus) error {
return c.UncachedClient.UpdateVersion(keyID, versionID, status)
}

func (c *HTTPClient) getClient() (HTTP, error) {
if c.UncachedClient.Client == nil {
c.UncachedClient.Client = &http.Client{}
}
return c.UncachedClient.Client, nil
}

func (c *HTTPClient) getHTTPData(method string, path string, body url.Values, data interface{}) error {
return c.UncachedClient.getHTTPData(method, path, body, data)
}

// UncachedHTTPClient is a client that uses HTTP to talk to Knox without caching.
type UncachedHTTPClient struct {
yoyouwen marked this conversation as resolved.
Show resolved Hide resolved
// Host is used as the host for http connections
Host string
//AuthHandler returns the authorization string for authenticating to knox. Users should be prefixed by 0u, machines by 0m. On fail, return empty string.
AuthHandler func() string
// Client is the http client for making network calls
Client HTTP
// Version is the current client version, useful for debugging and sent as a header
Version string
}

// NewClient creates a new uncached client to connect to talk to Knox.
func NewUncachedClient(host string, client HTTP, authHandler func() string, version string) UncachedHTTPClient {
return UncachedHTTPClient{
Host: host,
Client: client,
AuthHandler: authHandler,
Version: version,
}
}

// NetworkGetKey gets a knox key by keyID and only uses network without the caches.
func (c *UncachedHTTPClient) NetworkGetKey(keyID string) (*Key, error) {
key := &Key{}
err := c.getHTTPData("GET", "/v0/keys/"+keyID+"/", nil, key)
if err != nil {
return nil, err
}

// do not return the invalid format remote keys
if key.ID == "" || key.ACL == nil || key.VersionList == nil || key.VersionHash == "" {
return nil, fmt.Errorf("invalid key content for the remote key")
}

return key, err
}

// CacheGetKey acts same as NetworkGetKey for UncachedHTTPClient.
func (c *UncachedHTTPClient) CacheGetKey(keyID string) (*Key, error) {
return c.NetworkGetKey(keyID)
}

// GetKey gets a knox key by keyID.
func (c *UncachedHTTPClient) GetKey(keyID string) (*Key, error) {
return c.NetworkGetKey(keyID)
}

// CacheGetKeyWithStatus acts same as NetworkGetKeyWithStatus for UncachedHTTPClient.
func (c *UncachedHTTPClient) CacheGetKeyWithStatus(keyID string, status VersionStatus) (*Key, error) {
return c.NetworkGetKeyWithStatus(keyID, status)
}

// NetworkGetKeyWithStatus gets a knox key by keyID and given version status (always calls network).
func (c *UncachedHTTPClient) NetworkGetKeyWithStatus(keyID string, status VersionStatus) (*Key, error) {
// If clients need to know
s, err := status.MarshalJSON()
if err != nil {
return nil, err
}

key := &Key{}
err = c.getHTTPData("GET", "/v0/keys/"+keyID+"/?status="+string(s), nil, key)
return key, err
}

// GetKeyWithStatus gets a knox key by keyID and status (no cache).
func (c *UncachedHTTPClient) GetKeyWithStatus(keyID string, status VersionStatus) (*Key, error) {
return c.NetworkGetKeyWithStatus(keyID, status)
}

// CreateKey creates a knox key with given keyID data and ACL.
func (c *UncachedHTTPClient) CreateKey(keyID string, data []byte, acl ACL) (uint64, error) {
var i uint64
d := url.Values{}
d.Set("id", keyID)
Expand All @@ -327,7 +417,7 @@ func (c *HTTPClient) CreateKey(keyID string, data []byte, acl ACL) (uint64, erro
}

// GetKeys gets all Knox (if empty map) or gets all keys in map that do not match key version hash.
func (c *HTTPClient) GetKeys(keys map[string]string) ([]string, error) {
func (c *UncachedHTTPClient) GetKeys(keys map[string]string) ([]string, error) {
var l []string

d := url.Values{}
Expand All @@ -340,20 +430,20 @@ func (c *HTTPClient) GetKeys(keys map[string]string) ([]string, error) {
}

// DeleteKey deletes a key from Knox.
func (c HTTPClient) DeleteKey(keyID string) error {
func (c UncachedHTTPClient) DeleteKey(keyID string) error {
err := c.getHTTPData("DELETE", "/v0/keys/"+keyID+"/", nil, nil)
return err
}

// GetACL gets a knox key by keyID.
func (c *HTTPClient) GetACL(keyID string) (*ACL, error) {
func (c *UncachedHTTPClient) GetACL(keyID string) (*ACL, error) {
acl := &ACL{}
err := c.getHTTPData("GET", "/v0/keys/"+keyID+"/access/", nil, acl)
return acl, err
}

// PutAccess will add an ACL rule to a specific key.
func (c *HTTPClient) PutAccess(keyID string, a ...Access) error {
func (c *UncachedHTTPClient) PutAccess(keyID string, a ...Access) error {
d := url.Values{}
s, err := json.Marshal(a)
if err != nil {
Expand All @@ -365,7 +455,7 @@ func (c *HTTPClient) PutAccess(keyID string, a ...Access) error {
}

// AddVersion adds a key version to a specific key.
func (c *HTTPClient) AddVersion(keyID string, data []byte) (uint64, error) {
func (c *UncachedHTTPClient) AddVersion(keyID string, data []byte) (uint64, error) {
var i uint64
d := url.Values{}
d.Set("data", base64.StdEncoding.EncodeToString(data))
Expand All @@ -374,7 +464,7 @@ func (c *HTTPClient) AddVersion(keyID string, data []byte) (uint64, error) {
}

// UpdateVersion either promotes or demotes a specific key version.
func (c *HTTPClient) UpdateVersion(keyID, versionID string, status VersionStatus) error {
func (c *UncachedHTTPClient) UpdateVersion(keyID, versionID string, status VersionStatus) error {
d := url.Values{}
s, err := status.MarshalJSON()
if err != nil {
Expand All @@ -386,14 +476,14 @@ func (c *HTTPClient) UpdateVersion(keyID, versionID string, status VersionStatus
return err
}

func (c *HTTPClient) getClient() (HTTP, error) {
func (c *UncachedHTTPClient) getClient() (HTTP, error) {
if c.Client == nil {
c.Client = &http.Client{}
}
return c.Client, nil
}

func (c *HTTPClient) getHTTPData(method string, path string, body url.Values, data interface{}) error {
func (c *UncachedHTTPClient) getHTTPData(method string, path string, body url.Values, data interface{}) error {
r, err := http.NewRequest(method, "https://"+c.Host+path, bytes.NewBufferString(body.Encode()))

if err != nil {
Expand Down Expand Up @@ -452,12 +542,14 @@ func getHTTPResp(cli HTTP, r *http.Request, resp *Response) error {
// MockClient builds a client that ignores certs and talks to the given host.
func MockClient(host, keyFolder string) *HTTPClient {
return &HTTPClient{
Host: host,
AuthHandler: func() string {
return "TESTAUTH"
},
KeyFolder: keyFolder,
Client: &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}},
Version: "mock",
UncachedClient: UncachedHTTPClient{
Host: host,
AuthHandler: func() string {
return "TESTAUTH"
},
Client: &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}},
github-advanced-security[bot] marked this conversation as resolved.
Dismissed
Show resolved Hide resolved
Version: "mock",
},
}
}
3 changes: 2 additions & 1 deletion client/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ var clientGetKeyMetrics = func(map[string]string) {}
func Run(
client knox.APIClient,
yoyouwen marked this conversation as resolved.
Show resolved Hide resolved
p *VisibilityParams,
loginCommand *Command) {
loginCommand *Command,
) {

cli = client
if p != nil {
Expand Down
5 changes: 5 additions & 0 deletions client/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package client
import (
"encoding/json"
"fmt"
"github.com/pinterest/knox"
"path"
"strconv"
"time"
Expand Down Expand Up @@ -57,6 +58,10 @@ func parseTimeout(val string) (time.Duration, error) {
}

func runRegister(cmd *Command, args []string) *ErrorStatus {
if _, ok := cli.(*knox.UncachedHTTPClient); ok {
fmt.Println("Cannot Register in No Cache mode")
return nil
}
timeout, err := parseTimeout(*registerTimeout)
if err != nil {
return &ErrorStatus{fmt.Errorf("Invalid value for timeout flag: %s", err.Error()), false}
Expand Down
10 changes: 6 additions & 4 deletions cmd/dev_client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ func main() {
}

cli := &knox.HTTPClient{
Host: hostname,
AuthHandler: authHandler,
KeyFolder: keyFolder,
Client: &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}},
KeyFolder: keyFolder,
UncachedClient: knox.UncachedHTTPClient{
Host: hostname,
AuthHandler: authHandler,
Client: &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}},
},
}

loginCommand := client.NewLoginCommand(clientID, tokenEndpoint, "", "", "", "")
Expand Down
Loading