|
1 | 1 | package client
|
2 | 2 |
|
3 | 3 | import (
|
4 |
| - "bytes" |
5 | 4 | "encoding/json"
|
6 | 5 | "fmt"
|
7 |
| - "io/ioutil" |
8 |
| - "path/filepath" |
9 |
| - "time" |
| 6 | + "io" |
| 7 | + "net/http" |
| 8 | + "strings" |
10 | 9 |
|
| 10 | + "github.com/hashicorp/go-multierror" |
11 | 11 | "github.com/jetstack/preflight/api"
|
12 | 12 | )
|
13 | 13 |
|
14 |
| -// These variables are injected at build time. |
15 |
| -var ClientID string |
16 |
| -var ClientSecret string |
17 |
| -var AuthServerDomain string |
| 14 | +var ( |
| 15 | + // ClientID is the auth0 client identifier (injected at build time) |
| 16 | + ClientID string |
18 | 17 |
|
19 |
| -// PreflightClient can be used to talk to the Preflight backend. |
20 |
| -type PreflightClient struct { |
21 |
| - // OAuth2 |
22 |
| - credentials *Credentials |
23 |
| - // accessToken is the current OAuth access token. |
24 |
| - accessToken *accessToken |
| 18 | + // ClientSecret is the auth0 client secret (injected at build time) |
| 19 | + ClientSecret string |
25 | 20 |
|
26 |
| - baseURL string |
27 |
| - |
28 |
| - agentMetadata *api.AgentMetadata |
29 |
| -} |
| 21 | + // AuthServerDomain is the auth0 domain (injected at build time) |
| 22 | + AuthServerDomain string |
| 23 | +) |
30 | 24 |
|
31 |
| -// NewWithNoAuth creates a new client with no authentication. |
32 |
| -func NewWithNoAuth(agentMetadata *api.AgentMetadata, baseURL string) (*PreflightClient, error) { |
33 |
| - if baseURL == "" { |
34 |
| - return nil, fmt.Errorf("cannot create PreflightClient: baseURL cannot be empty") |
| 25 | +type ( |
| 26 | + // The Client interface describes types that perform requests against the Jetstack Secure backend. |
| 27 | + Client interface { |
| 28 | + PostDataReadings(orgID, clusterID string, readings []*api.DataReading) error |
| 29 | + Post(path string, body io.Reader) (*http.Response, error) |
35 | 30 | }
|
36 | 31 |
|
37 |
| - return &PreflightClient{ |
38 |
| - agentMetadata: agentMetadata, |
39 |
| - baseURL: baseURL, |
40 |
| - }, nil |
41 |
| -} |
42 |
| - |
43 |
| -// New creates a new client that uses OAuth2. |
44 |
| -func New(agentMetadata *api.AgentMetadata, credentials *Credentials, baseURL string) (*PreflightClient, error) { |
45 |
| - if err := credentials.validate(); err != nil { |
46 |
| - return nil, fmt.Errorf("cannot create PreflightClient: %v", err) |
47 |
| - } |
48 |
| - if baseURL == "" { |
49 |
| - return nil, fmt.Errorf("cannot create PreflightClient: baseURL cannot be empty") |
| 32 | + // Credentials defines the format of the credentials.json file. |
| 33 | + Credentials struct { |
| 34 | + // UserID is the ID or email for the user or service account. |
| 35 | + UserID string `json:"user_id"` |
| 36 | + // UserSecret is the secret for the user or service account. |
| 37 | + UserSecret string `json:"user_secret"` |
| 38 | + // The following fields are optional as the default behaviour |
| 39 | + // is to use the equivalent variables defined at package level |
| 40 | + // and injected at build time. |
| 41 | + // ClientID is the oauth2 client ID. |
| 42 | + ClientID string `json:"client_id,omitempty"` |
| 43 | + // ClientSecret is the oauth2 client secret. |
| 44 | + ClientSecret string `json:"client_secret,omitempty"` |
| 45 | + // AuthServerDomain is the domain for the auth server. |
| 46 | + AuthServerDomain string `json:"auth_server_domain,omitempty"` |
50 | 47 | }
|
| 48 | +) |
51 | 49 |
|
52 |
| - if !credentials.IsClientSet() { |
53 |
| - credentials.ClientID = ClientID |
54 |
| - credentials.ClientSecret = ClientSecret |
55 |
| - credentials.AuthServerDomain = AuthServerDomain |
| 50 | +// ParseCredentials reads credentials into a struct used. Performs validations. |
| 51 | +func ParseCredentials(data []byte) (*Credentials, error) { |
| 52 | + var credentials Credentials |
| 53 | + |
| 54 | + err := json.Unmarshal(data, &credentials) |
| 55 | + if err != nil { |
| 56 | + return nil, err |
56 | 57 | }
|
57 | 58 |
|
58 |
| - if !credentials.IsClientSet() { |
59 |
| - return nil, fmt.Errorf("cannot create PreflightClient: invalid OAuth2 client configuration") |
| 59 | + if err = credentials.validate(); err != nil { |
| 60 | + return nil, err |
60 | 61 | }
|
61 | 62 |
|
62 |
| - return &PreflightClient{ |
63 |
| - agentMetadata: agentMetadata, |
64 |
| - credentials: credentials, |
65 |
| - baseURL: baseURL, |
66 |
| - accessToken: &accessToken{}, |
67 |
| - }, nil |
| 63 | + return &credentials, nil |
68 | 64 | }
|
69 | 65 |
|
70 |
| -func (c *PreflightClient) usingOAuth2() bool { |
71 |
| - if c.credentials == nil { |
72 |
| - return false |
73 |
| - } |
74 |
| - |
75 |
| - return c.credentials.UserID != "" |
| 66 | +// IsClientSet returns whether the client credentials are set or not. |
| 67 | +func (c *Credentials) IsClientSet() bool { |
| 68 | + return c.ClientID != "" && c.ClientSecret != "" && c.AuthServerDomain != "" |
76 | 69 | }
|
77 | 70 |
|
78 |
| -// PostDataReadings sends a slice of readings to Preflight. |
79 |
| -func (c *PreflightClient) PostDataReadings(orgID, clusterID string, readings []*api.DataReading) error { |
80 |
| - payload := api.DataReadingsPost{ |
81 |
| - AgentMetadata: c.agentMetadata, |
82 |
| - DataGatherTime: time.Now().UTC(), |
83 |
| - DataReadings: readings, |
| 71 | +func (c *Credentials) validate() error { |
| 72 | + var result *multierror.Error |
| 73 | + |
| 74 | + if c == nil { |
| 75 | + return fmt.Errorf("credentials are nil") |
84 | 76 | }
|
85 |
| - data, err := json.Marshal(payload) |
86 |
| - if err != nil { |
87 |
| - return err |
| 77 | + |
| 78 | + if c.UserID == "" { |
| 79 | + result = multierror.Append(result, fmt.Errorf("user_id cannot be empty")) |
88 | 80 | }
|
89 | 81 |
|
90 |
| - res, err := c.Post(filepath.Join("/api/v1/org", orgID, "datareadings", clusterID), bytes.NewBuffer(data)) |
91 |
| - if err != nil { |
92 |
| - return err |
| 82 | + if c.UserSecret == "" { |
| 83 | + result = multierror.Append(result, fmt.Errorf("user_secret cannot be empty")) |
93 | 84 | }
|
94 | 85 |
|
95 |
| - if code := res.StatusCode; code < 200 || code >= 300 { |
96 |
| - errorContent := "" |
97 |
| - body, err := ioutil.ReadAll(res.Body) |
98 |
| - if err == nil { |
99 |
| - errorContent = string(body) |
100 |
| - } |
101 |
| - defer res.Body.Close() |
| 86 | + return result.ErrorOrNil() |
| 87 | +} |
102 | 88 |
|
103 |
| - return fmt.Errorf("received response with status code %d. Body: %s", code, errorContent) |
| 89 | +func fullURL(baseURL, path string) string { |
| 90 | + base := baseURL |
| 91 | + for strings.HasSuffix(base, "/") { |
| 92 | + base = strings.TrimSuffix(base, "/") |
104 | 93 | }
|
105 |
| - |
106 |
| - return nil |
| 94 | + for strings.HasPrefix(path, "/") { |
| 95 | + path = strings.TrimPrefix(path, "/") |
| 96 | + } |
| 97 | + return fmt.Sprintf("%s/%s", base, path) |
107 | 98 | }
|
0 commit comments