Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
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 go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,4 @@ require (
sigs.k8s.io/yaml v1.3.0 // indirect
)

replace github.com/confluentinc/ccloud-sdk-go-v2/org => github.com/confluentinc/ccloud-sdk-go-v2-internal/org v0.2.0
Comment thread
ameliadong97 marked this conversation as resolved.
Copy link
Copy Markdown
Member Author

@ameliadong97 Amelia Dong (ameliadong97) May 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to follow the guidance here: https://confluent.slack.com/archives/C04PBPC4H1T/p1776729951342679?thread_ts=1776702872.357189&cid=C04PBPC4H1T

But CI is failing with

go.mod:298:1: replacement are not allowed: github.com/confluentinc/ccloud-sdk-go-v2/org (gomoddirectives)

8 changes: 2 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,8 @@ github.com/compose-spec/compose-go/v2 v2.1.3 h1:bD67uqLuL/XgkAK6ir3xZvNLFPxPScEi
github.com/compose-spec/compose-go/v2 v2.1.3/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
github.com/confluentinc/ccloud-sdk-go-v1-public v0.0.0-20250521223017-0e8f6f971b52 h1:19qEGhkbZa5fopKCe0VPIV+Sasby4Pv10z9ZaktwWso=
github.com/confluentinc/ccloud-sdk-go-v1-public v0.0.0-20250521223017-0e8f6f971b52/go.mod h1:62EMf+5uFEt1BJ2q8WMrUoI9VUSxAbDnmZCGRt/MbA0=
github.com/confluentinc/ccloud-sdk-go-v2-internal/networking-access-point v0.13.0 h1:crG9ZKL8WlY7fX1ooSsODyNKSLhWuOpvThQ/GYlDjK0=
github.com/confluentinc/ccloud-sdk-go-v2-internal/networking-access-point v0.13.0/go.mod h1:94S6dsLwNBa6zumSRQi81/aM750kLu+HJFXd4jcJz7A=
github.com/confluentinc/ccloud-sdk-go-v2-internal/networking-gateway v0.13.0 h1:RDHQh6GcaL6JrZock9jsPQCUiwHNNZBiJOrXt7/v2lw=
github.com/confluentinc/ccloud-sdk-go-v2-internal/networking-gateway v0.13.0/go.mod h1:xr1v1dpdLoFSozNL4Qiv4gNjVlaTgr7mBMVnfjtzdas=
github.com/confluentinc/ccloud-sdk-go-v2-internal/org v0.2.0 h1:M2CN2vnXsgsi+mxS9Mbq8NQdi/dfRE0ooYVFrZ81R3s=
github.com/confluentinc/ccloud-sdk-go-v2-internal/org v0.2.0/go.mod h1:G9+rxKPBSPWLEbeYBGLlvt4DzPjifsYBRkDog8sCcRk=
github.com/confluentinc/ccloud-sdk-go-v2/ai v0.1.0 h1:zSF4OQUJXWH2JeAo9rsq13ibk+JFdzITGR8S7cFMpzw=
github.com/confluentinc/ccloud-sdk-go-v2/ai v0.1.0/go.mod h1:DoxqzzF3JzvJr3fWkvCiOHFlE0GoYpozWxFZ1Ud9ntA=
github.com/confluentinc/ccloud-sdk-go-v2/apikeys v0.4.0 h1:8fWyLwMuy8ec0MVF5Avd54UvbIxhDFhZzanHBVwgxdw=
Expand Down Expand Up @@ -254,8 +252,6 @@ github.com/confluentinc/ccloud-sdk-go-v2/networking-ip v0.2.0 h1:ZHNF2DeqVlNPuKG
github.com/confluentinc/ccloud-sdk-go-v2/networking-ip v0.2.0/go.mod h1:KTShFBZA7WG8LcxlWjJpoZFdWkJ+uOw3dDuwAHs5eKU=
github.com/confluentinc/ccloud-sdk-go-v2/networking-privatelink v0.3.0 h1:mC0E1nKUt57AxMM4Lpdfd+KA/YZwJVwro9ER+dCUFi8=
github.com/confluentinc/ccloud-sdk-go-v2/networking-privatelink v0.3.0/go.mod h1:GIHF2cYOUKx+6ycYokr4i8E4cuNBC22xqvO/IhqZ31U=
github.com/confluentinc/ccloud-sdk-go-v2/org v0.9.0 h1:FtaqHX0kBTK7fCQK+9SJcOso+XpWCWzY2roT3gBQGfw=
github.com/confluentinc/ccloud-sdk-go-v2/org v0.9.0/go.mod h1:X0uaTYPp+mr19W1R/Z1LuB1ePZJZrH7kxnQckDx6zoc=
github.com/confluentinc/ccloud-sdk-go-v2/provider-integration v0.2.0 h1:UN2a+aqYhk95ro+wVLkeB/8W7n+UV2KsE3jNFbbDCSw=
github.com/confluentinc/ccloud-sdk-go-v2/provider-integration v0.2.0/go.mod h1:TzompS9F0G6awN5xMC+nguNG8ULElN5UqX2XOBOIPuM=
github.com/confluentinc/ccloud-sdk-go-v2/service-quota v0.2.0 h1:xVSmwdycExze1E2Jta99CaFuMOlL6k6KExOOSY1hSFg=
Expand Down
1 change: 1 addition & 0 deletions internal/organization/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func New(prerunner pcmd.PreRunner) *cobra.Command {
cmd.AddCommand(c.newDescribeCommand())
cmd.AddCommand(c.newListCommand())
cmd.AddCommand(c.newUpdateCommand())
cmd.AddCommand(c.newScimTokenCommand())

return cmd
}
36 changes: 36 additions & 0 deletions internal/organization/command_scim_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package organization

import (
"time"

"github.com/spf13/cobra"

pcmd "github.com/confluentinc/cli/v4/pkg/cmd"
)

type scimTokenCommand struct {
*pcmd.AuthenticatedCLICommand
}

func (c *command) newScimTokenCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "scim-token",
Short: "Manage SCIM tokens.",
Long: "Manage SCIM tokens for the current organization.",
}

scimCmd := &scimTokenCommand{c.AuthenticatedCLICommand}

cmd.AddCommand(scimCmd.newCreateCommand())
cmd.AddCommand(scimCmd.newListCommand())
cmd.AddCommand(scimCmd.newDeleteCommand())

return cmd
}

func formatTimestamp(ts *time.Time) string {
if ts == nil {
return ""
}
return ts.UTC().Format(time.RFC3339)
}
61 changes: 61 additions & 0 deletions internal/organization/command_scim_token_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package organization

import (
"github.com/spf13/cobra"

orgv2 "github.com/confluentinc/ccloud-sdk-go-v2/org/v2"

pcmd "github.com/confluentinc/cli/v4/pkg/cmd"
"github.com/confluentinc/cli/v4/pkg/output"
)

type scimTokenCreateOut struct {
Id string `human:"ID" serialized:"id"`
Token string `human:"Token" serialized:"token"`
CreatedAt string `human:"Created At" serialized:"created_at"`
ExpiresAt string `human:"Expires At" serialized:"expires_at"`
}

func (c *scimTokenCommand) newCreateCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "Create a SCIM token.",
Long: "Create a SCIM token for the current organization.\n\nSave the token as it is not retrievable later.",
Args: cobra.NoArgs,
RunE: c.create,
}

cmd.Flags().Int32("expire-duration-mins", 0, "Token expiration duration in minutes. Defaults to 6 months if not specified.")
pcmd.AddOutputFlag(cmd)

return cmd
}

func (c *scimTokenCommand) create(cmd *cobra.Command, _ []string) error {
token := orgv2.InlineObject{}

// Only set expiration duration if explicitly provided
if cmd.Flags().Changed("expire-duration-mins") {
expireDurationMins, err := cmd.Flags().GetInt32("expire-duration-mins")
if err != nil {
return err
}
token.ExpireDurationMins = orgv2.PtrInt32(expireDurationMins)
}
Comment thread
ameliadong97 marked this conversation as resolved.

createdToken, err := c.V2Client.CreateScimToken(token)
if err != nil {
return err
}

output.Println(c.Config.EnableColor, "Save the token as it is not retrievable later.")

table := output.NewTable(cmd)
table.Add(&scimTokenCreateOut{
Id: createdToken.GetId(),
Token: createdToken.GetToken(),
CreatedAt: formatTimestamp(createdToken.CreatedAt),
ExpiresAt: formatTimestamp(createdToken.ExpiresAt),
})
return table.Print()
}
35 changes: 35 additions & 0 deletions internal/organization/command_scim_token_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package organization

import (
"github.com/spf13/cobra"

pcmd "github.com/confluentinc/cli/v4/pkg/cmd"
"github.com/confluentinc/cli/v4/pkg/deletion"
"github.com/confluentinc/cli/v4/pkg/resource"
)

func (c *scimTokenCommand) newDeleteCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "delete <id>",
Short: "Delete a SCIM token.",
Long: "Delete a SCIM token for the current organization.",
Args: cobra.ExactArgs(1),
RunE: c.delete,
}

pcmd.AddForceFlag(cmd)

return cmd
}

func (c *scimTokenCommand) delete(cmd *cobra.Command, args []string) error {
if err := deletion.ValidateAndConfirm(cmd, args, func(id string) bool { return true }, resource.ScimToken); err != nil {
return err
}

_, err := deletion.Delete(cmd, args, func(tokenId string) error {
return c.V2Client.DeleteScimToken(tokenId)
}, resource.ScimToken)

return err
}
45 changes: 45 additions & 0 deletions internal/organization/command_scim_token_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package organization

import (
"github.com/spf13/cobra"

pcmd "github.com/confluentinc/cli/v4/pkg/cmd"
"github.com/confluentinc/cli/v4/pkg/output"
)

type scimTokenListOut struct {
Id string `human:"ID" serialized:"id"`
CreatedAt string `human:"Created At" serialized:"created_at"`
ExpiresAt string `human:"Expires At" serialized:"expires_at"`
}

func (c *scimTokenCommand) newListCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List SCIM tokens.",
Long: "List SCIM tokens for the current organization.",
Args: cobra.NoArgs,
RunE: c.list,
}

pcmd.AddOutputFlag(cmd)

return cmd
}

func (c *scimTokenCommand) list(cmd *cobra.Command, _ []string) error {
tokens, err := c.V2Client.ListScimTokens()
if err != nil {
return err
}

list := output.NewList(cmd)
for _, token := range tokens {
list.Add(&scimTokenListOut{
Id: token.GetId(),
CreatedAt: formatTimestamp(token.CreatedAt),
ExpiresAt: formatTimestamp(token.ExpiresAt),
})
}
return list.Print()
}
49 changes: 49 additions & 0 deletions pkg/ccloudv2/scim.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package ccloudv2

import (
"net/http"

orgv2 "github.com/confluentinc/ccloud-sdk-go-v2/org/v2"

"github.com/confluentinc/cli/v4/pkg/errors"
)

// SCIM token API calls

func (c *Client) CreateScimToken(token orgv2.InlineObject) (orgv2.OrgV2ScimToken, error) {
resp, httpResp, err := c.OrgClient.ScimTokensOrgV2Api.CreateOrgV2ScimToken(c.orgApiContext()).InlineObject(token).Execute()
return resp, errors.CatchCCloudV2Error(err, httpResp)
}

func (c *Client) ListScimTokens() ([]orgv2.OrgV2ScimToken, error) {
var list []orgv2.OrgV2ScimToken

done := false
pageToken := ""
for !done {
page, httpResp, err := c.executeListScimTokens(pageToken)
if err != nil {
return nil, errors.CatchCCloudV2Error(err, httpResp)
}
list = append(list, page.GetData()...)

pageToken, done, err = extractNextPageToken(page.GetMetadata().Next)
if err != nil {
return nil, err
}
}
return list, nil
}

func (c *Client) executeListScimTokens(pageToken string) (orgv2.OrgV2ScimTokenList, *http.Response, error) {
req := c.OrgClient.ScimTokensOrgV2Api.ListOrgV2ScimTokens(c.orgApiContext()).PageSize(ccloudV2ListPageSize)
if pageToken != "" {
req = req.PageToken(pageToken)
}
return req.Execute()
}

func (c *Client) DeleteScimToken(id string) error {
httpResp, err := c.OrgClient.ScimTokensOrgV2Api.DeleteOrgV2ScimToken(c.orgApiContext(), id).Execute()
return errors.CatchCCloudV2Error(err, httpResp)
}
1 change: 1 addition & 0 deletions pkg/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const (
SchemaExporter = "schema exporter"
SchemaRegistryCluster = "Schema Registry cluster"
SchemaRegistryConfiguration = "Schema Registry configuration"
ScimToken = "SCIM token"
ServiceAccount = "service account"
SsoGroupMapping = "SSO group mapping"
Tableflow = "tableflow"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Save the token as it is not retrievable later.
+------------+--------------------------------------+
| ID | scim_token-5 |
| Token | scim_secret_token_value_scim_token-5 |
| Created At | 2026-02-12T10:58:00Z |
| Expires At | 2026-03-14T10:58:00Z |
+------------+--------------------------------------+
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Save the token as it is not retrievable later.
{
"id": "scim_token-4",
"token": "scim_secret_token_value_scim_token-4",
"created_at": "2026-02-12T10:57:59Z",
"expires_at": "2026-08-12T10:57:59Z"
}
7 changes: 7 additions & 0 deletions test/fixtures/output/organization/scim-token-create.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Save the token as it is not retrievable later.
+------------+--------------------------------------+
| ID | scim_token-3 |
| Token | scim_secret_token_value_scim_token-3 |
| Created At | 2026-02-12T10:57:58Z |
| Expires At | 2026-08-12T10:57:58Z |
+------------+--------------------------------------+
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Error: failed to delete nonexistent: SCIM token not found
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Are you sure you want to delete SCIM token "scim_token-67890"? (y/n): Deleted SCIM token "scim_token-67890".
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deleted SCIM token "scim_token-12345".
27 changes: 27 additions & 0 deletions test/fixtures/output/organization/scim-token-list-json.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[
{
"id": "scim_token-12345",
"created_at": "2025-01-01T01:00:00Z",
"expires_at": "2026-01-01T01:00:00Z"
},
{
"id": "scim_token-3",
"created_at": "2026-02-12T10:57:58Z",
"expires_at": "2026-08-12T10:57:58Z"
},
{
"id": "scim_token-4",
"created_at": "2026-02-12T10:57:59Z",
"expires_at": "2026-08-12T10:57:59Z"
},
{
"id": "scim_token-5",
"created_at": "2026-02-12T10:58:00Z",
"expires_at": "2026-03-14T10:58:00Z"
},
{
"id": "scim_token-67890",
"created_at": "2025-02-01T01:00:00Z",
"expires_at": "2026-02-01T01:00:00Z"
}
]
7 changes: 7 additions & 0 deletions test/fixtures/output/organization/scim-token-list.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ID | Created At | Expires At
-------------------+----------------------+-----------------------
scim_token-12345 | 2025-01-01T01:00:00Z | 2026-01-01T01:00:00Z
scim_token-3 | 2026-02-12T10:57:58Z | 2026-08-12T10:57:58Z
scim_token-4 | 2026-02-12T10:57:59Z | 2026-08-12T10:57:59Z
scim_token-5 | 2026-02-12T10:58:00Z | 2026-03-14T10:58:00Z
scim_token-67890 | 2025-02-01T01:00:00Z | 2026-02-01T01:00:00Z
19 changes: 19 additions & 0 deletions test/organization_scim_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package test

func (s *CLITestSuite) TestOrganizationSCIM() {
tests := []CLITest{
{args: "organization scim-token create", fixture: "organization/scim-token-create.golden"},
{args: "organization scim-token create -o json", fixture: "organization/scim-token-create-json.golden"},
{args: "organization scim-token create --expire-duration-mins 43200", fixture: "organization/scim-token-create-custom-expiration.golden"},
{args: "organization scim-token list", fixture: "organization/scim-token-list.golden"},
{args: "organization scim-token list -o json", fixture: "organization/scim-token-list-json.golden"},
{args: "organization scim-token delete scim_token-12345 --force", fixture: "organization/scim-token-delete.golden"},
{args: "organization scim-token delete scim_token-67890", input: "y\n", fixture: "organization/scim-token-delete-prompt.golden"},
{args: "organization scim-token delete nonexistent --force", fixture: "organization/scim-token-delete-not-found.golden", exitCode: 1},
}
Comment thread
ameliadong97 marked this conversation as resolved.

for _, test := range tests {
test.login = "cloud"
s.runIntegrationTest(test)
}
}
3 changes: 3 additions & 0 deletions test/test-server/ccloud_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ func handleMe(t *testing.T, isAuditLogEnabled bool) http.HandlerFunc {
Id: 42,
ResourceId: organizationId,
Name: "Confluent",
Sso: &ccloudv1.Sso{
Auth0ConnectionName: "op-12345",
},
}
if isAuditLogEnabled {
org.AuditLog = &ccloudv1.AuditLog{
Expand Down
3 changes: 3 additions & 0 deletions test/test-server/ccloudv2_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,8 @@ func NewV2Router(t *testing.T) *mux.Router {
router.HandleFunc(route.path, route.handler(t))
}

// Register SCIM token v2 routes
RegisterSCIMRoutes(router, t)

return router
}
Loading