Skip to content

Commit 57866b4

Browse files
kpcraigtvoran
authored andcommitted
Okta Dep Update (#28121)
Update okta to use v5 sdk instead of v2 --------- Co-authored-by: Theron Voran <tvoran@users.noreply.github.com>
1 parent 4304ca2 commit 57866b4

File tree

7 files changed

+222
-70
lines changed

7 files changed

+222
-70
lines changed

builtin/credential/okta/backend.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/hashicorp/vault/sdk/framework"
1414
"github.com/hashicorp/vault/sdk/helper/cidrutil"
1515
"github.com/hashicorp/vault/sdk/logical"
16-
"github.com/okta/okta-sdk-golang/v2/okta"
16+
"github.com/okta/okta-sdk-golang/v5/okta"
1717
"github.com/patrickmn/go-cache"
1818
)
1919

@@ -118,6 +118,7 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username, pas
118118
StateToken string `json:"stateToken"`
119119
}
120120

121+
// The okta-sdk-golang API says to construct your own requests for auth, and the Request Executor is gone, so
121122
authReq, err := shim.NewRequest("POST", "authn", map[string]interface{}{
122123
"username": username,
123124
"password": password,
@@ -129,9 +130,6 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username, pas
129130
var result authResult
130131
rsp, err := shim.Do(authReq, &result)
131132
if err != nil {
132-
if oe, ok := err.(*okta.Error); ok {
133-
return nil, logical.ErrorResponse("Okta auth failed: %v (code=%v)", err, oe.ErrorCode), nil, nil
134-
}
135133
return nil, logical.ErrorResponse(fmt.Sprintf("Okta auth failed: %v", err)), nil, nil
136134
}
137135
if rsp == nil {
@@ -370,23 +368,23 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username, pas
370368
return policies, oktaResponse, allGroups, nil
371369
}
372370

373-
func (b *backend) getOktaGroups(ctx context.Context, client *okta.Client, user *okta.User) ([]string, error) {
374-
groups, resp, err := client.User.ListUserGroups(ctx, user.Id)
371+
func (b *backend) getOktaGroups(ctx context.Context, client *okta.APIClient, user *okta.User) ([]string, error) {
372+
groups, resp, err := client.UserAPI.ListUserGroups(ctx, user.GetId()).Execute()
375373
if err != nil {
376374
return nil, err
377375
}
378376
oktaGroups := make([]string, 0, len(groups))
379377
for _, group := range groups {
380-
oktaGroups = append(oktaGroups, group.Profile.Name)
378+
oktaGroups = append(oktaGroups, group.Profile.GetName())
381379
}
382380
for resp.HasNextPage() {
383381
var nextGroups []*okta.Group
384-
resp, err = resp.Next(ctx, &nextGroups)
382+
resp, err = resp.Next(&nextGroups)
385383
if err != nil {
386384
return nil, err
387385
}
388386
for _, group := range nextGroups {
389-
oktaGroups = append(oktaGroups, group.Profile.Name)
387+
oktaGroups = append(oktaGroups, group.Profile.GetName())
390388
}
391389
}
392390
if b.Logger().IsDebug() {

builtin/credential/okta/backend_test.go

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ import (
1717
"github.com/hashicorp/vault/sdk/helper/logging"
1818
"github.com/hashicorp/vault/sdk/helper/policyutil"
1919
"github.com/hashicorp/vault/sdk/logical"
20-
"github.com/okta/okta-sdk-golang/v2/okta"
21-
"github.com/okta/okta-sdk-golang/v2/okta/query"
20+
"github.com/okta/okta-sdk-golang/v5/okta"
2221
"github.com/stretchr/testify/require"
2322
)
2423

@@ -115,15 +114,15 @@ func TestBackend_Config(t *testing.T) {
115114

116115
func createOktaGroups(t *testing.T, username string, token string, org string) []string {
117116
orgURL := "https://" + org + "." + previewBaseURL
118-
ctx, client, err := okta.NewClient(context.Background(), okta.WithOrgUrl(orgURL), okta.WithToken(token))
117+
cfg, err := okta.NewConfiguration(okta.WithOrgUrl(orgURL), okta.WithToken(token))
119118
require.Nil(t, err)
119+
client := okta.NewAPIClient(cfg)
120+
ctx := context.Background()
120121

121-
users, _, err := client.User.ListUsers(ctx, &query.Params{
122-
Q: username,
123-
})
122+
users, _, err := client.UserAPI.ListUsers(ctx).Q(username).Execute()
124123
require.Nil(t, err)
125124
require.Len(t, users, 1)
126-
userID := users[0].Id
125+
userID := users[0].GetId()
127126
var groupIDs []string
128127

129128
// Verify that login's call to list the groups of the user logging in will page
@@ -133,38 +132,37 @@ func createOktaGroups(t *testing.T, username string, token string, org string) [
133132
// only 200 results are returned for most orgs."
134133
for i := 0; i < 201; i++ {
135134
name := fmt.Sprintf("TestGroup%d", i)
136-
groups, _, err := client.Group.ListGroups(ctx, &query.Params{
137-
Q: name,
138-
})
135+
groups, _, err := client.GroupAPI.ListGroups(ctx).Q(name).Execute()
139136
require.Nil(t, err)
140137

141138
var groupID string
142139
if len(groups) == 0 {
143-
group, _, err := client.Group.CreateGroup(ctx, okta.Group{
140+
group, _, err := client.GroupAPI.CreateGroup(ctx).Group(okta.Group{
144141
Profile: &okta.GroupProfile{
145-
Name: fmt.Sprintf("TestGroup%d", i),
142+
Name: okta.PtrString(fmt.Sprintf("TestGroup%d", i)),
146143
},
147-
})
144+
}).Execute()
148145
require.Nil(t, err)
149-
groupID = group.Id
146+
groupID = group.GetId()
150147
} else {
151-
groupID = groups[0].Id
148+
groupID = groups[0].GetId()
152149
}
153150
groupIDs = append(groupIDs, groupID)
154151

155-
_, err = client.Group.AddUserToGroup(ctx, groupID, userID)
152+
_, err = client.GroupAPI.AssignUserToGroup(ctx, groupID, userID).Execute()
156153
require.Nil(t, err)
157154
}
158155
return groupIDs
159156
}
160157

161158
func deleteOktaGroups(t *testing.T, token string, org string, groupIDs []string) {
162159
orgURL := "https://" + org + "." + previewBaseURL
163-
ctx, client, err := okta.NewClient(context.Background(), okta.WithOrgUrl(orgURL), okta.WithToken(token))
160+
cfg, err := okta.NewConfiguration(okta.WithOrgUrl(orgURL), okta.WithToken(token))
164161
require.Nil(t, err)
162+
client := okta.NewAPIClient(cfg)
165163

166164
for _, groupID := range groupIDs {
167-
_, err := client.Group.DeleteGroup(ctx, groupID)
165+
_, err := client.GroupAPI.DeleteGroup(context.Background(), groupID).Execute()
168166
require.Nil(t, err)
169167
}
170168
}

builtin/credential/okta/path_config.go

Lines changed: 135 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,24 @@
44
package okta
55

66
import (
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

2227
const (
@@ -290,36 +295,133 @@ func (b *backend) pathConfigExistenceCheck(ctx context.Context, req *logical.Req
290295
}
291296

292297
type 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

298303
type 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

307314
func (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

314399
func (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

318420
type 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.
335455
func (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 {

changelog/28121.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:improvement
2+
auth/okta: update to okta sdk v5
3+
```

go.mod

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ require (
177177
github.com/mitchellh/reflectwalk v1.0.2
178178
github.com/ncw/swift v1.0.47
179179
github.com/oklog/run v1.1.0
180-
github.com/okta/okta-sdk-golang/v2 v2.20.0
180+
github.com/okta/okta-sdk-golang/v5 v5.0.2
181181
github.com/oracle/oci-go-sdk v24.3.0+incompatible
182182
github.com/ory/dockertest v3.3.5+incompatible
183183
github.com/ory/dockertest/v3 v3.10.0
@@ -230,11 +230,18 @@ require (
230230
require (
231231
cel.dev/expr v0.15.0 // indirect
232232
cloud.google.com/go/longrunning v0.6.0 // indirect
233+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
233234
github.com/fsnotify/fsnotify v1.6.0 // indirect
234235
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
235236
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
236237
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
237238
github.com/hashicorp/go-secure-stdlib/httputil v0.1.0 // indirect
239+
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
240+
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
241+
github.com/lestrrat-go/httpcc v1.0.1 // indirect
242+
github.com/lestrrat-go/iter v1.0.2 // indirect
243+
github.com/lestrrat-go/jwx v1.2.29 // indirect
244+
github.com/lestrrat-go/option v1.0.1 // indirect
238245
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
239246
github.com/moby/docker-image-spec v1.3.1 // indirect
240247
github.com/moby/sys/userns v0.1.0 // indirect

0 commit comments

Comments
 (0)