-
Notifications
You must be signed in to change notification settings - Fork 105
Adds scim settings, scim settings integration tests and generated mocks #1307
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
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
a05235f
adds scim settings, scim settings integration tests and generated mocks
skj-skj ae1a424
Merge branch 'main' into skj/scim-settings
skj-skj bbd5243
fixed lint
skj-skj 641bf1e
updated changelog
skj-skj d592471
added enableSAML helper func for test, fixed failed test cases
skj-skj 28a5c36
fixed idp cert can't be blank error in test
skj-skj d0fbc52
added condition to skip test if not running on tfe
skj-skj ee773f8
Update admin_setting_scim.go
skj-skj 5a44984
Update admin_setting_scim.go
skj-skj 53901d0
addressed review comments, fixed typos, updated cleanup function
skj-skj 8dc76d5
addressed review comments, removed redundant error handling
skj-skj 2532f1e
addressed review comment, fixed typo, added comment
skj-skj 2a20405
Update admin_setting_scim.go
skj-skj f50277d
added t.Helper in helper func for scim integration test
skj-skj 4fe1360
Update CHANGELOG.md
skj-skj a9a1d6d
Merge branch 'main' into skj/scim-settings
skj-skj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| // Copyright IBM Corp. 2018, 2026 | ||
| // SPDX-License-Identifier: MPL-2.0 | ||
|
|
||
| package tfe | ||
|
|
||
| import ( | ||
| "context" | ||
| ) | ||
|
|
||
| // Compile-time proof of interface implementation. | ||
| var _ SCIMSettings = (*adminSCIMSettings)(nil) | ||
|
|
||
| // SCIMSettings describes all the scim settings related methods that the Terraform | ||
| // Enterprise API supports | ||
| // | ||
| // TFE API docs: https://developer.hashicorp.com/terraform/enterprise/api-docs/admin/settings | ||
| type SCIMSettings interface { | ||
| // Read scim settings | ||
| Read(ctx context.Context) (*AdminSCIMSetting, error) | ||
|
|
||
| // Update scim settings | ||
| Update(ctx context.Context, options AdminSCIMSettingUpdateOptions) (*AdminSCIMSetting, error) | ||
|
|
||
| // Delete scim settings | ||
| Delete(ctx context.Context) error | ||
| } | ||
|
|
||
| // adminSCIMSettings implements SCIMSettings. | ||
| type adminSCIMSettings struct { | ||
| client *Client | ||
| } | ||
|
|
||
| // AdminSCIMSetting represents the SCIM setting in Terraform Enterprise | ||
| type AdminSCIMSetting struct { | ||
| ID string `jsonapi:"primary,scim-settings"` | ||
| Enabled bool `jsonapi:"attr,enabled"` | ||
| Paused bool `jsonapi:"attr,paused"` | ||
| SiteAdminGroupSCIMID string `jsonapi:"attr,site-admin-group-scim-id"` | ||
| SiteAdminGroupDisplayName string `jsonapi:"attr,site-admin-group-display-name"` | ||
| } | ||
|
|
||
| // AdminSCIMSettingUpdateOptions represents the options for updating an admin SCIM setting. | ||
| type AdminSCIMSettingUpdateOptions struct { | ||
| Enabled *bool `jsonapi:"attr,enabled,omitempty"` | ||
| Paused *bool `jsonapi:"attr,paused,omitempty"` | ||
| SiteAdminGroupSCIMID *string `jsonapi:"attr,site-admin-group-scim-id,omitempty"` | ||
| } | ||
|
skj-skj marked this conversation as resolved.
|
||
|
|
||
| // Read scim setting. | ||
| func (a *adminSCIMSettings) Read(ctx context.Context) (*AdminSCIMSetting, error) { | ||
| req, err := a.client.NewRequest("GET", "admin/scim-settings", nil) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| scim := &AdminSCIMSetting{} | ||
| err = req.Do(ctx, scim) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return scim, nil | ||
| } | ||
|
|
||
| // Update scim setting. | ||
| func (a *adminSCIMSettings) Update(ctx context.Context, options AdminSCIMSettingUpdateOptions) (*AdminSCIMSetting, error) { | ||
| req, err := a.client.NewRequest("PATCH", "admin/scim-settings", &options) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| scim := &AdminSCIMSetting{} | ||
| err = req.Do(ctx, scim) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return scim, nil | ||
| } | ||
|
|
||
| // Delete scim setting. | ||
| func (a *adminSCIMSettings) Delete(ctx context.Context) error { | ||
| req, err := a.client.NewRequest("DELETE", "admin/scim-settings", nil) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return req.Do(ctx, nil) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| // Copyright IBM Corp. 2018, 2026 | ||
| // SPDX-License-Identifier: MPL-2.0 | ||
|
|
||
| package tfe | ||
|
|
||
| import ( | ||
| "context" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestAdminSettings_SCIM_Read(t *testing.T) { | ||
| skipUnlessEnterprise(t) | ||
| client := testClient(t) | ||
| ctx := context.Background() | ||
|
|
||
| t.Run("read scim settings with default values", func(t *testing.T) { | ||
| scimSettings, err := client.Admin.Settings.SCIM.Read(ctx) | ||
| require.NoError(t, err) | ||
|
|
||
| assert.Equal(t, "scim", scimSettings.ID) | ||
| assert.False(t, scimSettings.Enabled) | ||
| assert.False(t, scimSettings.Paused) | ||
| assert.Empty(t, scimSettings.SiteAdminGroupSCIMID) | ||
| assert.Empty(t, scimSettings.SiteAdminGroupDisplayName) | ||
| }) | ||
| } | ||
|
|
||
| func TestAdminSettings_SCIM_Update(t *testing.T) { | ||
| skipUnlessEnterprise(t) | ||
| client := testClient(t) | ||
| ctx := context.Background() | ||
|
|
||
| enableSAML(ctx, t, client, true) | ||
| defer enableSAML(ctx, t, client, false) | ||
|
|
||
| scimClient := client.Admin.Settings.SCIM | ||
|
|
||
| t.Run("enable scim settings", func(t *testing.T) { | ||
| err := setSAMLProviderType(ctx, t, client, true) | ||
| require.NoErrorf(t, err, "failed to set SAML provider type") | ||
| defer cleanupSCIMSettings(ctx, t, client) | ||
|
skj-skj marked this conversation as resolved.
|
||
|
|
||
| scimSettings, err := scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{Enabled: Bool(true)}) | ||
| require.NoError(t, err) | ||
|
|
||
| assert.True(t, scimSettings.Enabled) | ||
| }) | ||
|
|
||
| t.Run("pause scim settings", func(t *testing.T) { | ||
| err := setSAMLProviderType(ctx, t, client, true) | ||
| require.NoErrorf(t, err, "failed to set SAML provider type") | ||
| defer cleanupSCIMSettings(ctx, t, client) | ||
|
skj-skj marked this conversation as resolved.
|
||
|
|
||
| _, err = scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{ | ||
| Enabled: Bool(true), | ||
| }) | ||
| require.NoError(t, err) | ||
|
|
||
| testCases := []struct { | ||
| name string | ||
| paused bool | ||
| }{ | ||
| {"pause scim provisioning", true}, | ||
| {"unpause scim provisioning", false}, | ||
| } | ||
|
|
||
| for _, tc := range testCases { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| _, err := scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{Paused: &tc.paused}) | ||
| require.NoError(t, err) | ||
| scimSettings, err := scimClient.Read(ctx) | ||
| require.NoError(t, err) | ||
| assert.Equal(t, tc.paused, scimSettings.Paused) | ||
| }) | ||
| } | ||
| }) | ||
|
|
||
| t.Run("update site admin group scim id", func(t *testing.T) { | ||
| err := setSAMLProviderType(ctx, t, client, true) | ||
| require.NoErrorf(t, err, "failed to set SAML provider type") | ||
| defer cleanupSCIMSettings(ctx, t, client) | ||
|
skj-skj marked this conversation as resolved.
|
||
|
|
||
| _, err = scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{Enabled: Bool(true)}) | ||
| require.NoError(t, err) | ||
|
|
||
| scimToken := generateSCIMToken(ctx, t, client) | ||
| scimGroupID := createSCIMGroup(ctx, t, client, "foo", scimToken) | ||
|
|
||
| testCases := []struct { | ||
| name string | ||
| scimGroupID string | ||
| raiseError bool | ||
| }{ | ||
| {"link scim group to site admin role", scimGroupID, false}, | ||
| {"trying to link non-existent group - should raise error", "this-group-doesn't-exist", true}, | ||
| } | ||
|
|
||
| for _, tc := range testCases { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| _, err := scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{SiteAdminGroupSCIMID: &tc.scimGroupID}) | ||
| if tc.raiseError { | ||
| require.Error(t, err) | ||
| return | ||
| } | ||
| require.NoError(t, err) | ||
| scimSettings, err := scimClient.Read(ctx) | ||
| require.NoError(t, err) | ||
| assert.Equal(t, tc.scimGroupID, scimSettings.SiteAdminGroupSCIMID) | ||
| assert.Equal(t, "foo", scimSettings.SiteAdminGroupDisplayName) | ||
| }) | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| func TestAdminSettings_SCIM_Delete(t *testing.T) { | ||
| skipUnlessEnterprise(t) | ||
| client := testClient(t) | ||
| ctx := context.Background() | ||
|
|
||
| enableSAML(ctx, t, client, true) | ||
| defer enableSAML(ctx, t, client, false) | ||
|
|
||
| scimClient := client.Admin.Settings.SCIM | ||
|
|
||
| t.Run("disable scim settings", func(t *testing.T) { | ||
| err := setSAMLProviderType(ctx, t, client, true) | ||
| require.NoErrorf(t, err, "failed to set SAML provider type") | ||
| defer cleanupSCIMSettings(ctx, t, client) | ||
|
|
||
| testCases := []struct { | ||
| name string | ||
| isScimEnabled bool | ||
| }{ | ||
| {"disable scim provisioning when it's already enabled", true}, | ||
| {"disable scim provisioning when it's already disabled - should not raise error", false}, | ||
| } | ||
|
|
||
| for _, tc := range testCases { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| if tc.isScimEnabled { | ||
| _, err := scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{Enabled: Bool(true)}) | ||
| require.NoError(t, err) | ||
| } | ||
|
|
||
| err := scimClient.Delete(ctx) | ||
| require.NoError(t, err) | ||
|
|
||
| scimSettings, err := scimClient.Read(ctx) | ||
| require.NoError(t, err) | ||
| assert.False(t, scimSettings.Enabled) | ||
| }) | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| // cleanup scim settings by disabling scim provisioning and setting saml provider type to unknown. | ||
| func cleanupSCIMSettings(ctx context.Context, t *testing.T, client *Client) { | ||
| t.Helper() | ||
| scimSettings, err := client.Admin.Settings.SCIM.Read(ctx) | ||
| if err == nil && scimSettings.Enabled { | ||
| err = client.Admin.Settings.SCIM.Delete(ctx) | ||
| require.NoErrorf(t, err, "failed to disable SCIM provisioning") | ||
|
skj-skj marked this conversation as resolved.
|
||
| } | ||
|
|
||
| err = setSAMLProviderType(ctx, t, client, false) | ||
| require.NoErrorf(t, err, "failed to set SAML provider type") | ||
| } | ||
|
|
||
| // generate a SCIM token for testing | ||
| func generateSCIMToken(ctx context.Context, t *testing.T, client *Client) string { | ||
| t.Helper() | ||
| // TFE requires a minimum of 30 days for SCIM token expiration | ||
| expiredAt := time.Now().Add(30 * 24 * time.Hour) | ||
|
skj-skj marked this conversation as resolved.
|
||
|
|
||
| options := struct { | ||
| Description *string `jsonapi:"attr,description"` | ||
| ExpiredAt *time.Time `jsonapi:"attr,expired-at,iso8601"` | ||
| }{ | ||
| Description: String("test-scim-token"), | ||
| ExpiredAt: &expiredAt, | ||
| } | ||
|
skj-skj marked this conversation as resolved.
|
||
| req, err := client.NewRequest("POST", "admin/scim-tokens", &options) | ||
| require.NoError(t, err) | ||
|
|
||
| var res struct { | ||
| Token string `jsonapi:"attr,token"` | ||
| } | ||
| err = req.Do(ctx, &res) | ||
| require.NoError(t, err) | ||
|
|
||
| return res.Token | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.