@@ -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
1419const (
@@ -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
2733type 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
3343func 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
4560func (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.
58125func (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.
292363func (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