Skip to content

Commit 2520336

Browse files
Merge branch 'main' into nithish/TF-32131-update-policyset-1
2 parents 6253066 + 10dd303 commit 2520336

10 files changed

+460
-11
lines changed

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
* Improve API error handling to decode both JSON:API error objects and regular JSON errors arrays by @uk1288 [#1304](https://github.com/hashicorp/go-tfe/pull/1304)
55

66
## Enhancements
7-
* Adds the `ProviderType` field to `AdminSAMLSetting` and `AdminSAMLSettingsUpdateOptions` to support the `provider-type` SAML setting.
8-
* Adds `PolicyUpdatePatterns` to `PolicySet`, `PolicySetCreateOptions`, and `PolicySetUpdateOptions` to support `policy-update-patterns` by @nithishravindra
7+
8+
* Adds the `ProviderType` field to `AdminSAMLSetting` and `AdminSAMLSettingsUpdateOptions` to support the `provider-type` SAML setting by @skj-skj [#1303](https://github.com/hashicorp/go-tfe/pull/1303)
9+
* Adds `AdminSCIMSetting` to support managing site-level SCIM settings by @skj-skj [#1307](https://github.com/hashicorp/go-tfe/pull/1307)
10+
* Adds `PolicyUpdatePatterns` to `PolicySet`, `PolicySetCreateOptions`, and `PolicySetUpdateOptions` to support `policy-update-patterns` by @nithishravindra [#1306](https://github.com/hashicorp/go-tfe/pull/1306)
911

1012
# v1.103.0
1113

admin_setting.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type AdminSettings struct {
1515
Twilio TwilioSettings
1616
Customization CustomizationSettings
1717
OIDC OIDCSettings
18+
SCIM SCIMSettings
1819
}
1920

2021
func newAdminSettings(client *Client) *AdminSettings {
@@ -26,5 +27,6 @@ func newAdminSettings(client *Client) *AdminSettings {
2627
Twilio: &adminTwilioSettings{client: client},
2728
Customization: &adminCustomizationSettings{client: client},
2829
OIDC: &adminOIDCSettings{client: client},
30+
SCIM: &adminSCIMSettings{client: client},
2931
}
3032
}

admin_setting_scim.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright IBM Corp. 2018, 2026
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package tfe
5+
6+
import (
7+
"context"
8+
)
9+
10+
// Compile-time proof of interface implementation.
11+
var _ SCIMSettings = (*adminSCIMSettings)(nil)
12+
13+
// SCIMSettings describes all the scim settings related methods that the Terraform
14+
// Enterprise API supports
15+
//
16+
// TFE API docs: https://developer.hashicorp.com/terraform/enterprise/api-docs/admin/settings
17+
type SCIMSettings interface {
18+
// Read scim settings
19+
Read(ctx context.Context) (*AdminSCIMSetting, error)
20+
21+
// Update scim settings
22+
Update(ctx context.Context, options AdminSCIMSettingUpdateOptions) (*AdminSCIMSetting, error)
23+
24+
// Delete scim settings
25+
Delete(ctx context.Context) error
26+
}
27+
28+
// adminSCIMSettings implements SCIMSettings.
29+
type adminSCIMSettings struct {
30+
client *Client
31+
}
32+
33+
// AdminSCIMSetting represents the SCIM setting in Terraform Enterprise
34+
type AdminSCIMSetting struct {
35+
ID string `jsonapi:"primary,scim-settings"`
36+
Enabled bool `jsonapi:"attr,enabled"`
37+
Paused bool `jsonapi:"attr,paused"`
38+
SiteAdminGroupSCIMID string `jsonapi:"attr,site-admin-group-scim-id"`
39+
SiteAdminGroupDisplayName string `jsonapi:"attr,site-admin-group-display-name"`
40+
}
41+
42+
// AdminSCIMSettingUpdateOptions represents the options for updating an admin SCIM setting.
43+
type AdminSCIMSettingUpdateOptions struct {
44+
Enabled *bool `jsonapi:"attr,enabled,omitempty"`
45+
Paused *bool `jsonapi:"attr,paused,omitempty"`
46+
SiteAdminGroupSCIMID *string `jsonapi:"attr,site-admin-group-scim-id,omitempty"`
47+
}
48+
49+
// Read scim setting.
50+
func (a *adminSCIMSettings) Read(ctx context.Context) (*AdminSCIMSetting, error) {
51+
req, err := a.client.NewRequest("GET", "admin/scim-settings", nil)
52+
if err != nil {
53+
return nil, err
54+
}
55+
56+
scim := &AdminSCIMSetting{}
57+
err = req.Do(ctx, scim)
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
return scim, nil
63+
}
64+
65+
// Update scim setting.
66+
func (a *adminSCIMSettings) Update(ctx context.Context, options AdminSCIMSettingUpdateOptions) (*AdminSCIMSetting, error) {
67+
req, err := a.client.NewRequest("PATCH", "admin/scim-settings", &options)
68+
if err != nil {
69+
return nil, err
70+
}
71+
72+
scim := &AdminSCIMSetting{}
73+
err = req.Do(ctx, scim)
74+
if err != nil {
75+
return nil, err
76+
}
77+
return scim, nil
78+
}
79+
80+
// Delete scim setting.
81+
func (a *adminSCIMSettings) Delete(ctx context.Context) error {
82+
req, err := a.client.NewRequest("DELETE", "admin/scim-settings", nil)
83+
if err != nil {
84+
return err
85+
}
86+
87+
return req.Do(ctx, nil)
88+
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Copyright IBM Corp. 2018, 2026
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package tfe
5+
6+
import (
7+
"context"
8+
"testing"
9+
"time"
10+
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestAdminSettings_SCIM_Read(t *testing.T) {
16+
skipUnlessEnterprise(t)
17+
client := testClient(t)
18+
ctx := context.Background()
19+
20+
t.Run("read scim settings with default values", func(t *testing.T) {
21+
scimSettings, err := client.Admin.Settings.SCIM.Read(ctx)
22+
require.NoError(t, err)
23+
24+
assert.Equal(t, "scim", scimSettings.ID)
25+
assert.False(t, scimSettings.Enabled)
26+
assert.False(t, scimSettings.Paused)
27+
assert.Empty(t, scimSettings.SiteAdminGroupSCIMID)
28+
assert.Empty(t, scimSettings.SiteAdminGroupDisplayName)
29+
})
30+
}
31+
32+
func TestAdminSettings_SCIM_Update(t *testing.T) {
33+
skipUnlessEnterprise(t)
34+
client := testClient(t)
35+
ctx := context.Background()
36+
37+
enableSAML(ctx, t, client, true)
38+
defer enableSAML(ctx, t, client, false)
39+
40+
scimClient := client.Admin.Settings.SCIM
41+
42+
t.Run("enable scim settings", func(t *testing.T) {
43+
err := setSAMLProviderType(ctx, t, client, true)
44+
require.NoErrorf(t, err, "failed to set SAML provider type")
45+
defer cleanupSCIMSettings(ctx, t, client)
46+
47+
scimSettings, err := scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{Enabled: Bool(true)})
48+
require.NoError(t, err)
49+
50+
assert.True(t, scimSettings.Enabled)
51+
})
52+
53+
t.Run("pause scim settings", func(t *testing.T) {
54+
err := setSAMLProviderType(ctx, t, client, true)
55+
require.NoErrorf(t, err, "failed to set SAML provider type")
56+
defer cleanupSCIMSettings(ctx, t, client)
57+
58+
_, err = scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{
59+
Enabled: Bool(true),
60+
})
61+
require.NoError(t, err)
62+
63+
testCases := []struct {
64+
name string
65+
paused bool
66+
}{
67+
{"pause scim provisioning", true},
68+
{"unpause scim provisioning", false},
69+
}
70+
71+
for _, tc := range testCases {
72+
t.Run(tc.name, func(t *testing.T) {
73+
_, err := scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{Paused: &tc.paused})
74+
require.NoError(t, err)
75+
scimSettings, err := scimClient.Read(ctx)
76+
require.NoError(t, err)
77+
assert.Equal(t, tc.paused, scimSettings.Paused)
78+
})
79+
}
80+
})
81+
82+
t.Run("update site admin group scim id", func(t *testing.T) {
83+
err := setSAMLProviderType(ctx, t, client, true)
84+
require.NoErrorf(t, err, "failed to set SAML provider type")
85+
defer cleanupSCIMSettings(ctx, t, client)
86+
87+
_, err = scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{Enabled: Bool(true)})
88+
require.NoError(t, err)
89+
90+
scimToken := generateSCIMToken(ctx, t, client)
91+
scimGroupID := createSCIMGroup(ctx, t, client, "foo", scimToken)
92+
93+
testCases := []struct {
94+
name string
95+
scimGroupID string
96+
raiseError bool
97+
}{
98+
{"link scim group to site admin role", scimGroupID, false},
99+
{"trying to link non-existent group - should raise error", "this-group-doesn't-exist", true},
100+
}
101+
102+
for _, tc := range testCases {
103+
t.Run(tc.name, func(t *testing.T) {
104+
_, err := scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{SiteAdminGroupSCIMID: &tc.scimGroupID})
105+
if tc.raiseError {
106+
require.Error(t, err)
107+
return
108+
}
109+
require.NoError(t, err)
110+
scimSettings, err := scimClient.Read(ctx)
111+
require.NoError(t, err)
112+
assert.Equal(t, tc.scimGroupID, scimSettings.SiteAdminGroupSCIMID)
113+
assert.Equal(t, "foo", scimSettings.SiteAdminGroupDisplayName)
114+
})
115+
}
116+
})
117+
}
118+
119+
func TestAdminSettings_SCIM_Delete(t *testing.T) {
120+
skipUnlessEnterprise(t)
121+
client := testClient(t)
122+
ctx := context.Background()
123+
124+
enableSAML(ctx, t, client, true)
125+
defer enableSAML(ctx, t, client, false)
126+
127+
scimClient := client.Admin.Settings.SCIM
128+
129+
t.Run("disable scim settings", func(t *testing.T) {
130+
err := setSAMLProviderType(ctx, t, client, true)
131+
require.NoErrorf(t, err, "failed to set SAML provider type")
132+
defer cleanupSCIMSettings(ctx, t, client)
133+
134+
testCases := []struct {
135+
name string
136+
isScimEnabled bool
137+
}{
138+
{"disable scim provisioning when it's already enabled", true},
139+
{"disable scim provisioning when it's already disabled - should not raise error", false},
140+
}
141+
142+
for _, tc := range testCases {
143+
t.Run(tc.name, func(t *testing.T) {
144+
if tc.isScimEnabled {
145+
_, err := scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{Enabled: Bool(true)})
146+
require.NoError(t, err)
147+
}
148+
149+
err := scimClient.Delete(ctx)
150+
require.NoError(t, err)
151+
152+
scimSettings, err := scimClient.Read(ctx)
153+
require.NoError(t, err)
154+
assert.False(t, scimSettings.Enabled)
155+
})
156+
}
157+
})
158+
}
159+
160+
// cleanup scim settings by disabling scim provisioning and setting saml provider type to unknown.
161+
func cleanupSCIMSettings(ctx context.Context, t *testing.T, client *Client) {
162+
t.Helper()
163+
scimSettings, err := client.Admin.Settings.SCIM.Read(ctx)
164+
if err == nil && scimSettings.Enabled {
165+
err = client.Admin.Settings.SCIM.Delete(ctx)
166+
require.NoErrorf(t, err, "failed to disable SCIM provisioning")
167+
}
168+
169+
err = setSAMLProviderType(ctx, t, client, false)
170+
require.NoErrorf(t, err, "failed to set SAML provider type")
171+
}
172+
173+
// generate a SCIM token for testing
174+
func generateSCIMToken(ctx context.Context, t *testing.T, client *Client) string {
175+
t.Helper()
176+
// TFE requires a minimum of 30 days for SCIM token expiration
177+
expiredAt := time.Now().Add(30 * 24 * time.Hour)
178+
179+
options := struct {
180+
Description *string `jsonapi:"attr,description"`
181+
ExpiredAt *time.Time `jsonapi:"attr,expired-at,iso8601"`
182+
}{
183+
Description: String("test-scim-token"),
184+
ExpiredAt: &expiredAt,
185+
}
186+
req, err := client.NewRequest("POST", "admin/scim-tokens", &options)
187+
require.NoError(t, err)
188+
189+
var res struct {
190+
Token string `jsonapi:"attr,token"`
191+
}
192+
err = req.Do(ctx, &res)
193+
require.NoError(t, err)
194+
195+
return res.Token
196+
}

agent_integration_test.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@ func TestAgentsRead(t *testing.T) {
3333
assert.Equal(t, agent, k)
3434
})
3535

36-
t.Run("when the agent does not exist", func(t *testing.T) {
37-
k, err := client.Agents.Read(ctx, "nonexistent")
38-
assert.Nil(t, k)
39-
assert.Equal(t, err, ErrResourceNotFound)
40-
})
36+
// NOTE!! Commenting out test to unblock PRs pending a fix in the API.
37+
// Please re-enable once the API is fixed.
38+
// t.Run("when the agent does not exist", func(t *testing.T) {
39+
// k, err := client.Agents.Read(ctx, "nonexistent")
40+
// assert.Nil(t, k)
41+
// assert.Equal(t, err, ErrResourceNotFound)
42+
// })
4143

4244
t.Run("without a valid agent ID", func(t *testing.T) {
4345
k, err := client.Agents.Read(ctx, badIdentifier)

generate_mocks.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mockgen -source=admin_setting_customization.go -destination=mocks/admin_setting_
1414
mockgen -source=admin_setting_general.go -destination=mocks/admin_setting_general_mocks.go -package=mocks
1515
mockgen -source=admin_setting_oidc.go -destination=mocks/admin_setting_oidc_mocks.go -package=mocks
1616
mockgen -source=admin_setting_saml.go -destination=mocks/admin_setting_saml_mocks.go -package=mocks
17+
mockgen -source=admin_setting_scim.go -destination=mocks/admin_setting_scim_mocks.go -package=mocks
1718
mockgen -source=admin_setting_smtp.go -destination=mocks/admin_setting_smtp_mocks.go -package=mocks
1819
mockgen -source=admin_setting_twilio.go -destination=mocks/admin_setting_twilio_mocks.go -package=mocks
1920
mockgen -source=admin_terraform_version.go -destination=mocks/admin_terraform_version_mocks.go -package=mocks

0 commit comments

Comments
 (0)