Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
## Enhancements
* 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)
* Adds `AdminSCIMSetting` to support managing site-level SCIM settings by @skj-skj [#1307](https://github.com/hashicorp/go-tfe/pull/1307)
* Adds `AdminSCIMToken` to support managing site-level SCIM tokens by @skj-skj [#1310](https://github.com/hashicorp/go-tfe/pull/1310)

# v1.103.0

Expand Down
13 changes: 11 additions & 2 deletions admin_setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@

package tfe

// SCIMResource groups the SCIM related resources together.
type SCIMResource struct {
SCIMSettings
Tokens AdminSCIMTokens
}
Comment thread
skj-skj marked this conversation as resolved.

// AdminSettings describes all the admin settings related methods that the Terraform Enterprise API supports.
// Note that admin settings are only available in Terraform Enterprise.
//
Expand All @@ -15,7 +21,7 @@ type AdminSettings struct {
Twilio TwilioSettings
Customization CustomizationSettings
OIDC OIDCSettings
SCIM SCIMSettings
SCIM *SCIMResource
}
Comment thread
skj-skj marked this conversation as resolved.

func newAdminSettings(client *Client) *AdminSettings {
Expand All @@ -27,6 +33,9 @@ func newAdminSettings(client *Client) *AdminSettings {
Twilio: &adminTwilioSettings{client: client},
Customization: &adminCustomizationSettings{client: client},
OIDC: &adminOIDCSettings{client: client},
SCIM: &adminSCIMSettings{client: client},
SCIM: &SCIMResource{
SCIMSettings: &adminSCIMSettings{client: client},
Tokens: &adminSCIMTokens{client: client},
},
}
}
32 changes: 4 additions & 28 deletions admin_setting_scim_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package tfe
import (
"context"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -87,8 +86,10 @@ func TestAdminSettings_SCIM_Update(t *testing.T) {
_, err = scimClient.Update(ctx, AdminSCIMSettingUpdateOptions{Enabled: Bool(true)})
require.NoError(t, err)

scimToken := generateSCIMToken(ctx, t, client)
scimGroupID := createSCIMGroup(ctx, t, client, "foo", scimToken)
scimToken, err := scimClient.Tokens.Create(ctx, "scim integration test token")
require.NoError(t, err)
Comment thread
skj-skj marked this conversation as resolved.
Comment thread
skj-skj marked this conversation as resolved.
require.NotEmpty(t, scimToken.Token)
scimGroupID := createSCIMGroup(ctx, t, client, "foo", scimToken.Token)

testCases := []struct {
name string
Expand Down Expand Up @@ -169,28 +170,3 @@ func cleanupSCIMSettings(ctx context.Context, t *testing.T, client *Client) {
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)

options := struct {
Description *string `jsonapi:"attr,description"`
ExpiredAt *time.Time `jsonapi:"attr,expired-at,iso8601"`
}{
Description: String("test-scim-token"),
ExpiredAt: &expiredAt,
}
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
}
135 changes: 135 additions & 0 deletions admin_setting_scim_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright IBM Corp. 2018, 2026
// SPDX-License-Identifier: MPL-2.0

package tfe

Comment thread
skj-skj marked this conversation as resolved.
import (
"context"
"fmt"
"net/url"
"time"
)

var _ AdminSCIMTokens = (*adminSCIMTokens)(nil)

// AdminSCIMTokens describes all the Admin SCIM token related methods that the Terraform
// Enterprise API supports
//
// TFE API docs: https://developer.hashicorp.com/terraform/enterprise/api-docs/admin/scim-tokens
type AdminSCIMTokens interface {
// List all Admin SCIM tokens.
List(ctx context.Context) (*AdminSCIMTokenList, error)

// Create an Admin SCIM token.
Create(ctx context.Context, description string) (*AdminSCIMToken, error)

// Create an Admin SCIM token with options.
CreateWithOptions(ctx context.Context, options AdminSCIMTokenCreateOptions) (*AdminSCIMToken, error)

// Read an Admin SCIM token by its ID.
Read(ctx context.Context, scimTokenID string) (*AdminSCIMToken, error)

// Delete an Admin SCIM token.
Delete(ctx context.Context, scimTokenID string) error
}

// adminSCIMTokens implements AdminSCIMTokens
type adminSCIMTokens struct {
client *Client
}

// AdminSCIMTokenList represents a list of Admin SCIM tokens
type AdminSCIMTokenList struct {
Comment thread
skj-skj marked this conversation as resolved.
Comment thread
skj-skj marked this conversation as resolved.
Items []*AdminSCIMToken
}
Comment thread
skj-skj marked this conversation as resolved.

// AdminSCIMToken represents a Terraform Enterprise Admin SCIM token.
type AdminSCIMToken struct {
ID string `jsonapi:"primary,authentication-tokens"`
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
LastUsedAt time.Time `jsonapi:"attr,last-used-at,iso8601"`
ExpiredAt time.Time `jsonapi:"attr,expired-at,iso8601"`
Description string `jsonapi:"attr,description"`
Token string `jsonapi:"attr,token,omitempty"`
}

// AdminSCIMTokenCreateOptions represents the options for creating an Admin SCIM token
type AdminSCIMTokenCreateOptions struct {
// Required: A human-readable description of the token's purpose
// (for example, Okta SCIM Integration).
Description *string `jsonapi:"attr,description"`

// Optional: Optional ISO-8601 timestamp for token expiration.
// Defaults to 365 days in the future. Must be between 29 and 365 days in the future.
ExpiredAt *time.Time `jsonapi:"attr,expired-at,iso8601,omitempty"`
}

// List all Admin SCIM tokens.
func (a *adminSCIMTokens) List(ctx context.Context) (*AdminSCIMTokenList, error) {
req, err := a.client.NewRequest("GET", AdminSCIMTokensPath, nil)
if err != nil {
return nil, err
}

scimTokens := &AdminSCIMTokenList{}
err = req.Do(ctx, scimTokens)
if err != nil {
return nil, err
}
return scimTokens, nil
}

// Create an Admin SCIM token.
func (a *adminSCIMTokens) Create(ctx context.Context, description string) (*AdminSCIMToken, error) {
return a.CreateWithOptions(ctx, AdminSCIMTokenCreateOptions{
Description: &description,
})
}

// Create an Admin SCIM token with options.
func (a *adminSCIMTokens) CreateWithOptions(ctx context.Context, options AdminSCIMTokenCreateOptions) (*AdminSCIMToken, error) {
if !validString(options.Description) {
return nil, ErrSCIMTokenDescription
}
req, err := a.client.NewRequest("POST", AdminSCIMTokensPath, &options)
if err != nil {
return nil, err
}
scimToken := &AdminSCIMToken{}
err = req.Do(ctx, scimToken)
if err != nil {
return nil, err
}
return scimToken, nil
}

// Read an Admin SCIM token by its ID.
func (a *adminSCIMTokens) Read(ctx context.Context, scimTokenID string) (*AdminSCIMToken, error) {
if !validStringID(&scimTokenID) {
return nil, ErrInvalidTokenID
}
u := fmt.Sprintf(AdminSCIMTokenPath, url.PathEscape(scimTokenID))
req, err := a.client.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
scimToken := &AdminSCIMToken{}
err = req.Do(ctx, scimToken)
if err != nil {
return nil, err
}
return scimToken, nil
}

// Delete an Admin SCIM token.
func (a *adminSCIMTokens) Delete(ctx context.Context, scimTokenID string) error {
if !validStringID(&scimTokenID) {
return ErrInvalidTokenID
}
u := fmt.Sprintf(AuthenticationTokensPath, url.PathEscape(scimTokenID))
Comment thread
skj-skj marked this conversation as resolved.
req, err := a.client.NewRequest("DELETE", u, nil)
if err != nil {
return err
}
return req.Do(ctx, nil)
}
Loading
Loading