Skip to content

Commit b08fce1

Browse files
authored
Merge pull request #11 from ConductorOne/goldschmidt/keep-alive
Add keep alive http function
2 parents 532b7d6 + 5f0d272 commit b08fce1

File tree

2 files changed

+103
-7
lines changed

2 files changed

+103
-7
lines changed

pkg/connector/connector.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ func New(ctx context.Context, username string, password string, instanceURL stri
6969

7070
client := jamf.NewClient(
7171
uhttp.NewBaseHttpClient(httpClient),
72+
username,
73+
password,
7274
"",
7375
instanceURL,
7476
)

pkg/jamf/client.go

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ import (
77
"io"
88
"net/http"
99
liburl "net/url"
10+
"time"
1011

1112
"github.com/conductorone/baton-sdk/pkg/uhttp"
13+
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
14+
"go.uber.org/zap"
15+
"google.golang.org/grpc/codes"
16+
"google.golang.org/grpc/status"
1217
)
1318

1419
const (
@@ -22,27 +27,38 @@ const (
2227
userGroupsUrlPath = "/JSSResource/usergroups"
2328
userUrlPath = "/JSSResource/users/id/%d"
2429
usersUrlPath = "/JSSResource/users"
30+
keepAliveUrlPath = "/api/v1/auth/keep-alive"
2531
)
2632

2733
type Client struct {
28-
wrapper *uhttp.BaseHttpClient
29-
token string
30-
instanceURL string
34+
wrapper *uhttp.BaseHttpClient
35+
token string
36+
instanceURL string
37+
lastKeepAlive time.Time
38+
39+
userName string
40+
password string
3141
}
3242

3343
func NewClient(
3444
wrapper *uhttp.BaseHttpClient,
45+
userName string,
46+
password string,
3547
token string,
3648
instanceURL string,
3749
) *Client {
3850
return &Client{
39-
wrapper: wrapper,
40-
token: token,
41-
instanceURL: instanceURL,
51+
wrapper: wrapper,
52+
token: token,
53+
instanceURL: instanceURL,
54+
lastKeepAlive: time.Now(),
55+
userName: userName,
56+
password: password,
4257
}
4358
}
4459

4560
func (c *Client) SetBearerToken(token string) {
61+
c.lastKeepAlive = time.Now()
4662
c.token = token
4763
}
4864

@@ -54,12 +70,66 @@ func (c *Client) getUrl(path string) (*liburl.URL, error) {
5470
return liburl.Parse(urlString)
5571
}
5672

73+
func (c *Client) keepAliveToken(
74+
ctx context.Context,
75+
) error {
76+
l := ctxzap.Extract(ctx)
77+
78+
if c.token == "" {
79+
return fmt.Errorf("token is empty")
80+
}
81+
82+
if time.Since(c.lastKeepAlive) < 5*time.Minute {
83+
return nil
84+
}
85+
86+
l.Debug("Refreshing token")
87+
88+
url, err := c.getUrl(keepAliveUrlPath)
89+
if err != nil {
90+
return err
91+
}
92+
93+
request, err := c.wrapper.NewRequest(
94+
ctx,
95+
http.MethodPost,
96+
url,
97+
uhttp.WithAcceptJSONHeader(),
98+
uhttp.WithContentTypeJSONHeader(),
99+
uhttp.WithHeader(
100+
"Authorization",
101+
fmt.Sprintf("Bearer %s", c.token),
102+
),
103+
)
104+
if err != nil {
105+
return err
106+
}
107+
108+
var target TokenResponse
109+
response, err := c.wrapper.Do(request, uhttp.WithJSONResponse(&target))
110+
if err != nil {
111+
return err
112+
}
113+
err = response.Body.Close()
114+
if err != nil {
115+
return err
116+
}
117+
118+
c.token = target.Token
119+
c.lastKeepAlive = time.Now()
120+
121+
return nil
122+
}
123+
57124
// CreateBearerToken creates bearer token needed to use the Jamf API.
58125
func (c *Client) CreateBearerToken(
59126
ctx context.Context,
60127
username string,
61128
password string,
62129
) (string, error) {
130+
l := ctxzap.Extract(ctx)
131+
132+
l.Debug("Creating bearer token")
63133
url, err := c.getUrl(tokenUrlPath)
64134
if err != nil {
65135
return "", err
@@ -289,11 +359,22 @@ func (c *Client) GetAccounts(ctx context.Context) ([]*UserAccount, []*Group, err
289359
return userAccounts, groups, nil
290360
}
291361

362+
// doRequest performs an authenticated request to the Jamf API.
292363
func (c *Client) doRequest(
293364
ctx context.Context,
294365
url *liburl.URL,
295366
target interface{},
296367
) error {
368+
l := ctxzap.Extract(ctx)
369+
370+
err := c.keepAliveToken(ctx)
371+
if err != nil {
372+
return err
373+
}
374+
375+
firstTry := true
376+
377+
GotoRetry:
297378
request, err := c.wrapper.NewRequest(
298379
ctx,
299380
http.MethodGet,
@@ -307,9 +388,22 @@ func (c *Client) doRequest(
307388
if err != nil {
308389
return err
309390
}
310-
311391
response, err := c.wrapper.Do(request)
312392
if err != nil {
393+
l.Error("failed to perform request", zap.Error(err))
394+
if status.Code(err) == codes.Unauthenticated && firstTry {
395+
l.Debug("retrying request with new token")
396+
token, err := c.CreateBearerToken(ctx, c.userName, c.password)
397+
if err != nil {
398+
return err
399+
}
400+
401+
c.SetBearerToken(token)
402+
firstTry = false
403+
404+
l.Debug("retrying request with new token")
405+
goto GotoRetry
406+
}
313407
return err
314408
}
315409

0 commit comments

Comments
 (0)