Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
20 changes: 20 additions & 0 deletions test/organization_scim_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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"
test.workflow = true
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