|
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