44package okta
55
66import (
7+ "bytes"
78 "context"
9+ "encoding/json"
810 "fmt"
11+ "io"
912 "net/http"
1013 "net/url"
1114 "strings"
1215 "time"
1316
17+ gocache "github.com/patrickmn/go-cache"
18+
1419 oktaold "github.com/chrismalek/oktasdk-go/okta"
1520 "github.com/hashicorp/go-cleanhttp"
1621 "github.com/hashicorp/vault/sdk/framework"
1722 "github.com/hashicorp/vault/sdk/helper/tokenutil"
1823 "github.com/hashicorp/vault/sdk/logical"
19- oktanew "github.com/okta/okta-sdk-golang/v2 /okta"
24+ oktanew "github.com/okta/okta-sdk-golang/v5 /okta"
2025)
2126
2227const (
@@ -290,36 +295,133 @@ func (b *backend) pathConfigExistenceCheck(ctx context.Context, req *logical.Req
290295}
291296
292297type oktaShim interface {
293- Client () (* oktanew.Client , context.Context )
298+ Client () (* oktanew.APIClient , context.Context )
294299 NewRequest (method string , url string , body interface {}) (* http.Request , error )
295300 Do (req * http.Request , v interface {}) (interface {}, error )
296301}
297302
298303type oktaShimNew struct {
299- client * oktanew.Client
304+ cfg * oktanew.Configuration
305+ client * oktanew.APIClient
300306 ctx context.Context
307+ cache * gocache.Cache // cache used to hold authorization values created for NewRequests
301308}
302309
303- func (new * oktaShimNew ) Client () (* oktanew.Client , context.Context ) {
310+ func (new * oktaShimNew ) Client () (* oktanew.APIClient , context.Context ) {
304311 return new .client , new .ctx
305312}
306313
307314func (new * oktaShimNew ) NewRequest (method string , url string , body interface {}) (* http.Request , error ) {
308315 if ! strings .HasPrefix (url , "/" ) {
309316 url = "/api/v1/" + url
310317 }
311- return new .client .GetRequestExecutor ().NewRequest (method , url , body )
318+
319+ // reimplementation of RequestExecutor.NewRequest() in v2 of okta-golang-sdk
320+ var buff io.ReadWriter
321+ if body != nil {
322+ switch v := body .(type ) {
323+ case []byte :
324+ buff = bytes .NewBuffer (v )
325+ case * bytes.Buffer :
326+ buff = v
327+ default :
328+ buff = & bytes.Buffer {}
329+ // need to create an encoder specifically to disable html escaping
330+ encoder := json .NewEncoder (buff )
331+ encoder .SetEscapeHTML (false )
332+ err := encoder .Encode (body )
333+ if err != nil {
334+ return nil , err
335+ }
336+ }
337+ }
338+
339+ url = new .cfg .Okta .Client .OrgUrl + url
340+ req , err := http .NewRequest (method , url , buff )
341+ if err != nil {
342+ return nil , err
343+ }
344+
345+ // construct an authorization header for the request using our okta config
346+ var auth oktanew.Authorization
347+ // I think the only usage of the shim is in credential/okta/backend.go, and in that case, the
348+ // AuthorizationMode is only ever SSWS (since OktaClient() below never overrides the default authorization
349+ // mode. This function will faithfully replicate the old RequestExecutor code, though.
350+ switch new .cfg .Okta .Client .AuthorizationMode {
351+ case "SSWS" :
352+ auth = oktanew .NewSSWSAuth (new .cfg .Okta .Client .Token , req )
353+ case "Bearer" :
354+ auth = oktanew .NewBearerAuth (new .cfg .Okta .Client .Token , req )
355+ case "PrivateKey" :
356+ auth = oktanew .NewPrivateKeyAuth (oktanew.PrivateKeyAuthConfig {
357+ TokenCache : new .cache ,
358+ HttpClient : new .cfg .HTTPClient ,
359+ PrivateKeySigner : new .cfg .PrivateKeySigner ,
360+ PrivateKey : new .cfg .Okta .Client .PrivateKey ,
361+ PrivateKeyId : new .cfg .Okta .Client .PrivateKeyId ,
362+ ClientId : new .cfg .Okta .Client .ClientId ,
363+ OrgURL : new .cfg .Okta .Client .OrgUrl ,
364+ Scopes : new .cfg .Okta .Client .Scopes ,
365+ MaxRetries : new .cfg .Okta .Client .RateLimit .MaxRetries ,
366+ MaxBackoff : new .cfg .Okta .Client .RateLimit .MaxBackoff ,
367+ Req : req ,
368+ })
369+ case "JWT" :
370+ auth = oktanew .NewJWTAuth (oktanew.JWTAuthConfig {
371+ TokenCache : new .cache ,
372+ HttpClient : new .cfg .HTTPClient ,
373+ OrgURL : new .cfg .Okta .Client .OrgUrl ,
374+ Scopes : new .cfg .Okta .Client .Scopes ,
375+ ClientAssertion : new .cfg .Okta .Client .ClientAssertion ,
376+ MaxRetries : new .cfg .Okta .Client .RateLimit .MaxRetries ,
377+ MaxBackoff : new .cfg .Okta .Client .RateLimit .MaxBackoff ,
378+ Req : req ,
379+ })
380+ default :
381+ return nil , fmt .Errorf ("unknown authorization mode %v" , new .cfg .Okta .Client .AuthorizationMode )
382+ }
383+
384+ // Authorize adds a header based on the contents of the Authorization struct
385+ err = auth .Authorize ("POST" , url )
386+ if err != nil {
387+ return nil , err
388+ }
389+
390+ req .Header .Add ("Accept" , "application/json" )
391+
392+ if body != nil {
393+ req .Header .Set ("Content-Type" , "application/json" )
394+ }
395+
396+ return req , nil
312397}
313398
314399func (new * oktaShimNew ) Do (req * http.Request , v interface {}) (interface {}, error ) {
315- return new .client .GetRequestExecutor ().Do (new .ctx , req , v )
400+ resp , err := new .cfg .HTTPClient .Do (req )
401+ if err != nil {
402+ return nil , err
403+ }
404+
405+ if resp .Body == nil {
406+ return nil , nil
407+ }
408+ defer resp .Body .Close ()
409+
410+ bt , err := io .ReadAll (resp .Body )
411+ err = json .Unmarshal (bt , v )
412+ if err != nil {
413+ return nil , err
414+ }
415+
416+ // as far as i can tell, we only use the first return to check if it is nil, and assume that means an error happened.
417+ return resp , nil
316418}
317419
318420type oktaShimOld struct {
319421 client * oktaold.Client
320422}
321423
322- func (new * oktaShimOld ) Client () (* oktanew.Client , context.Context ) {
424+ func (new * oktaShimOld ) Client () (* oktanew.APIClient , context.Context ) {
323425 return nil , nil
324426}
325427
@@ -331,7 +433,25 @@ func (new *oktaShimOld) Do(req *http.Request, v interface{}) (interface{}, error
331433 return new .client .Do (req , v )
332434}
333435
334- // OktaClient creates a basic okta client connection
436+ func (c * ConfigEntry ) OktaConfiguration (ctx context.Context ) (* oktanew.Configuration , error ) {
437+ baseURL := defaultBaseURL
438+ if c .Production != nil {
439+ if ! * c .Production {
440+ baseURL = previewBaseURL
441+ }
442+ }
443+ if c .BaseURL != "" {
444+ baseURL = c .BaseURL
445+ }
446+
447+ cfg , err := oktanew .NewConfiguration (oktanew .WithOrgUrl ("https://" + c .Org + "." + baseURL ), oktanew .WithToken (c .Token ))
448+ if err != nil {
449+ return nil , err
450+ }
451+ return cfg , nil
452+ }
453+
454+ // OktaClient returns an OktaShim, based on the presence of a token in the ConfigEntry.
335455func (c * ConfigEntry ) OktaClient (ctx context.Context ) (oktaShim , error ) {
336456 baseURL := defaultBaseURL
337457 if c .Production != nil {
@@ -344,13 +464,18 @@ func (c *ConfigEntry) OktaClient(ctx context.Context) (oktaShim, error) {
344464 }
345465
346466 if c .Token != "" {
347- ctx , client , err := oktanew .NewClient ( ctx ,
467+ cfg , err := oktanew .NewConfiguration (
348468 oktanew .WithOrgUrl ("https://" + c .Org + "." + baseURL ),
349469 oktanew .WithToken (c .Token ))
350470 if err != nil {
351471 return nil , err
352472 }
353- return & oktaShimNew {client , ctx }, nil
473+ return & oktaShimNew {
474+ cfg : cfg ,
475+ client : oktanew .NewAPIClient (cfg ),
476+ ctx : ctx ,
477+ cache : gocache .New (gocache .DefaultExpiration , 1 * time .Second ),
478+ }, nil
354479 }
355480 client , err := oktaold .NewClientWithDomain (cleanhttp .DefaultClient (), c .Org , baseURL , "" )
356481 if err != nil {
0 commit comments