...
1 package client
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "net/http"
8
9 "github.com/shurcooL/graphql"
10
11 "edge-infra.dev/pkg/edge/client"
12 )
13
14 const (
15
16 Endpoint = "api-endpoint"
17
18 Username = "username"
19
20 Password = "password"
21
22 Organization = "organization"
23
24 Timeout = "api-timeout"
25 )
26
27
28 type Config struct {
29 Endpoint string
30 Username string
31 Password string
32 Organization string
33 TotpToken string
34 BearerToken string
35 Version string
36 }
37
38
39 func (c Config) Valid() bool {
40 return c.Endpoint != "" && (c.hasValidUserPassword() || c.hasValidTopToken() || c.hasValidBearerToken())
41 }
42
43 func (c Config) hasValidTopToken() bool {
44 return c.TotpToken != ""
45 }
46
47 func (c Config) hasValidBearerToken() bool {
48 return c.BearerToken != ""
49 }
50
51 func (c Config) hasValidUserPassword() bool {
52 return c.Username != "" && c.Password != ""
53 }
54
55
56 type Client struct {
57 config *Config
58 *graphql.Client
59 }
60
61
62 func (c Client) Query(ctx context.Context, query interface{}, variables map[string]interface{}) error {
63 return c.Client.Query(ctx, query, variables)
64 }
65
66
67 func (c Client) Mutate(ctx context.Context, mutation interface{}, variables map[string]interface{}) error {
68 return c.Client.Mutate(ctx, mutation, variables)
69 }
70
71
72 func New(config *Config) (*Client, error) {
73 if config == nil {
74 return nil, errors.New("config is required")
75 }
76
77 if !config.Valid() {
78 return nil, errors.New("invalid config")
79 }
80
81 var client *http.Client
82 var err error
83 if config.hasValidTopToken() {
84 client = getTotpClient(config)
85 } else if config.hasValidBearerToken() {
86 client = getBearerClient(config)
87 } else if client, err = getUserPasswordClient(config); err != nil {
88 return nil, err
89 }
90
91 return &Client{
92 config: config,
93 Client: graphql.NewClient(config.Endpoint, client),
94 }, nil
95 }
96
97 func getUserPasswordClient(config *Config) (*http.Client, error) {
98 token, err := getBearerToken(config.Endpoint, config.Username, config.Password, config.Organization)
99 if err != nil {
100 return nil, err
101 }
102 config.BearerToken = token
103 return getBearerClient(config), nil
104 }
105
106 func getBearerClient(config *Config) *http.Client {
107 headers := make(map[string]string)
108 headers[client.Authorization] = fmt.Sprintf("%s %s", client.BearerToken, config.BearerToken)
109 headers[client.EdgeVersion] = config.Version
110 return client.NewTokenClient(headers)
111 }
112
113 func getTotpClient(config *Config) *http.Client {
114 headers := make(map[string]string)
115 headers[client.Authorization] = fmt.Sprintf("%s %s", client.TotpToken, config.TotpToken)
116 headers[client.EdgeVersion] = config.Version
117 return client.NewTokenClient(headers)
118 }
119
120
121 func getBearerToken(url, username, password, organization string) (string, error) {
122 client := graphql.NewClient(url, nil)
123
124 var mutation struct {
125 Login struct {
126 Token graphql.String
127 } `graphql:"login(username: $username, password: $password, organization: $organization)"`
128 }
129
130 variables := map[string]interface{}{
131 "username": graphql.String(username),
132 "password": graphql.String(password),
133 "organization": graphql.String(organization),
134 }
135
136 err := client.Mutate(context.Background(), &mutation, variables)
137 return string(mutation.Login.Token), err
138 }
139
View as plain text