-
Notifications
You must be signed in to change notification settings - Fork 2
Make syncing secrets an opt in resource type. #203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,7 @@ import ( | |
|
|
||
| type apiTokenResourceType struct { | ||
| resourceType *v2.ResourceType | ||
| clientV5 *oktav5.APIClient | ||
| connector *Okta | ||
| } | ||
|
|
||
| func (o *apiTokenResourceType) Entitlements(ctx context.Context, resource *v2.Resource, attrs resource.SyncOpAttrs) ([]*v2.Entitlement, *resource.SyncOpResults, error) { | ||
|
|
@@ -37,6 +37,10 @@ func (o *apiTokenResourceType) List( | |
| resourceID *v2.ResourceId, | ||
| attrs resource.SyncOpAttrs, | ||
| ) ([]*v2.Resource, *resource.SyncOpResults, error) { | ||
| if !o.connector.shouldFetchApiTokens() { | ||
| return nil, &resource.SyncOpResults{}, nil | ||
| } | ||
|
|
||
| token := &attrs.PageToken | ||
| bag, prevSerializedResp, err := parsePageToken(token.Token, &v2.ResourceId{ResourceType: resourceTypeApiToken.Id}) | ||
| if err != nil { | ||
|
|
@@ -47,7 +51,7 @@ func (o *apiTokenResourceType) List( | |
| var resp *oktav5.APIResponse | ||
|
|
||
| if prevSerializedResp == "" { | ||
| apiTokens, resp, err = o.clientV5.ApiTokenAPI.ListApiTokens(ctx).Execute() | ||
| apiTokens, resp, err = o.connector.clientV5.ApiTokenAPI.ListApiTokens(ctx).Execute() | ||
| if err != nil { | ||
| return nil, nil, fmt.Errorf("okta-connector-v5: failed to list api tokens: %w", err) | ||
| } | ||
|
Comment on lines
+54
to
57
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: Now that |
||
|
|
@@ -57,7 +61,7 @@ func (o *apiTokenResourceType) List( | |
| return nil, nil, fmt.Errorf("okta-connector-v5: failed to deserialize page token: %w", err) | ||
| } | ||
|
|
||
| localOktaAPIResponse := oktav5.NewAPIResponse(prevResp.Response, o.clientV5, nil) | ||
| localOktaAPIResponse := oktav5.NewAPIResponse(prevResp.Response, o.connector.clientV5, nil) | ||
| if localOktaAPIResponse.HasNextPage() { | ||
| resp, err = localOktaAPIResponse.Next(&apiTokens) | ||
| if err != nil { | ||
|
|
@@ -93,20 +97,24 @@ func (o *apiTokenResourceType) List( | |
| return ret, &resource.SyncOpResults{NextPageToken: nextPageToken, Annotations: annos}, nil | ||
| } | ||
|
|
||
| func apiTokenBuilder(clientV5 *oktav5.APIClient) *apiTokenResourceType { | ||
| func apiTokenBuilder(connector *Okta) *apiTokenResourceType { | ||
| return &apiTokenResourceType{ | ||
| resourceType: resourceTypeApiToken, | ||
| clientV5: clientV5, | ||
| connector: connector, | ||
| } | ||
| } | ||
|
|
||
| func (o *apiTokenResourceType) Get(ctx context.Context, resourceId *v2.ResourceId, parentResourceId *v2.ResourceId) (*v2.Resource, annotations.Annotations, error) { | ||
| if !o.connector.shouldFetchApiTokens() { | ||
| return nil, nil, nil | ||
| } | ||
|
Comment on lines
+108
to
+110
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: Returning |
||
|
|
||
| l := ctxzap.Extract(ctx) | ||
| l.Debug("getting api token", zap.String("api_token_id", resourceId.Resource)) | ||
|
|
||
| var annos annotations.Annotations | ||
|
|
||
| apiToken, resp, err := o.clientV5.ApiTokenAPI.GetApiToken(ctx, resourceId.Resource).Execute() | ||
| apiToken, resp, err := o.connector.clientV5.ApiTokenAPI.GetApiToken(ctx, resourceId.Resource).Execute() | ||
| if err != nil { | ||
| return nil, nil, fmt.Errorf("okta-connector-v5: failed to get api token: %w", err) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| package connector | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/conductorone/baton-sdk/pkg/cli" | ||
| ) | ||
|
|
||
| func TestShouldFetchApiTokens(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| syncSecrets bool | ||
| opts *cli.ConnectorOpts | ||
| want bool | ||
| }{ | ||
| { | ||
| name: "deprecated flag still fetches", | ||
| syncSecrets: true, | ||
| opts: &cli.ConnectorOpts{}, | ||
| want: true, | ||
| }, | ||
| { | ||
| name: "unfiltered sync does not fetch", | ||
| syncSecrets: false, | ||
| opts: &cli.ConnectorOpts{}, | ||
| want: false, | ||
| }, | ||
| { | ||
| name: "explicit opt-in fetches", | ||
| syncSecrets: false, | ||
| opts: &cli.ConnectorOpts{SyncResourceTypeIDs: []string{resourceTypeApiToken.Id}}, | ||
| want: true, | ||
| }, | ||
| { | ||
| name: "explicit filter without api-token does not fetch", | ||
| syncSecrets: false, | ||
| opts: &cli.ConnectorOpts{SyncResourceTypeIDs: []string{resourceTypeUser.Id}}, | ||
| want: false, | ||
| }, | ||
| { | ||
| name: "flag wins over filter that omits api-token", | ||
| syncSecrets: true, | ||
| opts: &cli.ConnectorOpts{SyncResourceTypeIDs: []string{resourceTypeUser.Id}}, | ||
| want: true, | ||
| }, | ||
| { | ||
| name: "nil opts (capabilities) reports fetch", | ||
| syncSecrets: false, | ||
| opts: nil, | ||
| want: true, | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| t.Parallel() | ||
| o := &Okta{SyncSecrets: tt.syncSecrets, opts: tt.opts} | ||
| if got := o.shouldFetchApiTokens(); got != tt.want { | ||
| t.Fatalf("shouldFetchApiTokens() = %v, want %v", got, tt.want) | ||
| } | ||
| }) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -120,7 +120,7 @@ var ( | |
| Id: "api-token", | ||
| DisplayName: "API Token", | ||
| Traits: []v2.ResourceType_Trait{v2.ResourceType_TRAIT_SECRET}, | ||
| Annotations: v1AnnotationsForResourceType("api-token", true, capabilityPermissions("okta.apiTokens.read")), | ||
| Annotations: v1AnnotationsForResourceType("api-token", true, capabilityPermissions("okta.apiTokens.read"), &v2.OptInRequired{}), | ||
|
ggreer marked this conversation as resolved.
|
||
| } | ||
| resourceTypeDevice = &v2.ResourceType{ | ||
| Id: "device", | ||
|
|
@@ -152,6 +152,8 @@ func shouldSyncResourceType(opts *cli.ConnectorOpts, resourceTypeID string) bool | |
| return opts.WillSyncResourceType(resourceTypeID) | ||
| } | ||
|
|
||
| var _ connectorbuilder.ConnectorBuilderV2 = (*Okta)(nil) | ||
|
|
||
| func (o *Okta) ResourceSyncers(ctx context.Context) []connectorbuilder.ResourceSyncerV2 { | ||
| resourceSyncer := []connectorbuilder.ResourceSyncerV2{ | ||
| roleBuilder(o.client, o), | ||
|
|
@@ -176,36 +178,23 @@ func (o *Okta) ResourceSyncers(ctx context.Context) []connectorbuilder.ResourceS | |
| ) | ||
| } | ||
|
|
||
| if o.SyncSecrets { | ||
| resourceSyncer = append(resourceSyncer, apiTokenBuilder(o.clientV5)) | ||
| } | ||
| resourceSyncer = append(resourceSyncer, apiTokenBuilder(o)) | ||
|
|
||
| return resourceSyncer | ||
| } | ||
|
|
||
| func (c *Okta) ListResourceTypes(ctx context.Context, request *v2.ResourceTypesServiceListResourceTypesRequest) (*v2.ResourceTypesServiceListResourceTypesResponse, error) { | ||
| resourceTypes := []*v2.ResourceType{ | ||
| resourceTypeUser, | ||
| resourceTypeGroup, | ||
| resourceTypeRole, | ||
| resourceTypeApp, | ||
| } | ||
|
|
||
| if c.SyncCustomRoles { | ||
| resourceTypes = append(resourceTypes, resourceTypeCustomRole, resourceTypeResourceSets, resourceTypeResourceSetsBindings) | ||
| } | ||
|
|
||
| if c.SyncSecrets { | ||
| resourceTypes = append(resourceTypes, resourceTypeApiToken) | ||
| // shouldFetchApiTokens reports whether API token resources should be fetched from Okta. | ||
| // The api-token type is always advertised (OptInRequired). Fetching happens when the | ||
| // deprecated --sync-secrets flag is set, or when the sync filter explicitly includes | ||
| // api-token (C1 resource-type opt-in). A full unfiltered CLI sync does not fetch tokens. | ||
| func (o *Okta) shouldFetchApiTokens() bool { | ||
| if o.SyncSecrets { | ||
| return true | ||
| } | ||
|
|
||
| if shouldSyncResourceType(c.opts, resourceTypeDevice.Id) { | ||
| resourceTypes = append(resourceTypes, resourceTypeDevice) | ||
| if o.opts == nil { | ||
| return true | ||
| } | ||
|
|
||
| return &v2.ResourceTypesServiceListResourceTypesResponse{ | ||
| List: resourceTypes, | ||
| }, nil | ||
| return o.opts.SyncFilterIsExplicit() && o.opts.WillSyncResourceType(resourceTypeApiToken.Id) | ||
| } | ||
|
Comment on lines
+190
to
198
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: This predicate and the scope decision in
Comment on lines
+190
to
198
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: this gate reads |
||
|
|
||
| func (c *Okta) Metadata(ctx context.Context) (*v2.ConnectorMetadata, error) { | ||
|
|
@@ -450,7 +439,7 @@ func New(ctx context.Context, cc *cfg.Okta, opts *cli.ConnectorOpts) (connectorb | |
| case cfg.PrivateKeyGroup: | ||
| scopes = append(scopes, provisioningScopes...) | ||
|
|
||
| if cc.SyncSecrets { | ||
| if cc.SyncSecrets || (opts != nil && opts.SyncFilterIsExplicit() && opts.WillSyncResourceType(resourceTypeApiToken.Id)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: this predicate duplicates |
||
| scopes = append(scopes, "okta.apiTokens.read") | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Suggestion: the Capabilities section (lines 15-29) still presents Secrets - API tokens as an unconditional sync row, and only Devices carries the
**Devices is opt-in.**callout. Since API tokens are now opt-in too, add a matching note next to the table (and consider line 95, "This connector can sync secrets…") so the capabilities section isn't stale relative to this change.