From 6d271f7c3db0981d1b7faa4dfa397d543f830781 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2026 15:53:29 +0000 Subject: [PATCH 1/2] chore(deps): bump github.com/stackitcloud/stackit-sdk-go/services/serviceaccount Bumps [github.com/stackitcloud/stackit-sdk-go/services/serviceaccount](https://github.com/stackitcloud/stackit-sdk-go) from 0.20.1 to 0.21.1. - [Release notes](https://github.com/stackitcloud/stackit-sdk-go/releases) - [Changelog](https://github.com/stackitcloud/stackit-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/stackitcloud/stackit-sdk-go/compare/core/v0.20.1...core/v0.21.1) --- updated-dependencies: - dependency-name: github.com/stackitcloud/stackit-sdk-go/services/serviceaccount dependency-version: 0.21.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 62dbc6d93..504eb729d 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.19.1 github.com/stackitcloud/stackit-sdk-go/services/serverbackup v1.7.3 github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.5.6 - github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.20.1 + github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.21.1 github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.7.2 github.com/stackitcloud/stackit-sdk-go/services/ske v1.21.2 github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.18.1 diff --git a/go.sum b/go.sum index cc872ddb8..a9dead2fa 100644 --- a/go.sum +++ b/go.sum @@ -664,8 +664,8 @@ github.com/stackitcloud/stackit-sdk-go/services/serverbackup v1.7.3 h1:wqAj7RP4G github.com/stackitcloud/stackit-sdk-go/services/serverbackup v1.7.3/go.mod h1:VkF3N0fTP8zOztdeKEG0uoMuMp3larEqNB22fnvlA5M= github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.5.6 h1:/3IynjsBBkdH4JbqT+B4gscadIsZZ028+NFlxJTj5xQ= github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.5.6/go.mod h1:z17c+hqIi6S5Op9Uk0FmxaeZIoN6and2AAV7P1viAqQ= -github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.20.1 h1:zVVA/Puge2PK3G8lrGGolnQzxYwwsNUgfMyyRUUQc44= -github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.20.1/go.mod h1:xJNa86o89czK2o60+s46vz8+NSKDTbQduFGFaztJnsA= +github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.21.1 h1:GJn6tlJscVpdovN2lekb9R5dOZ1ynM9+gIVhEVsRCE0= +github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.21.1/go.mod h1:f5RySI1PHkYlv2JWIfbTOBARwf/JR+iAs/qeoGiXAvU= github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.7.2 h1:ne+TBZTWUzIg47X5vRbeyPfJCA1tafIN3R20ADBIu2Q= github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.7.2/go.mod h1:gmOQuuRATNTmHTuwCsJ6ko1AwHThYpvEkmD2uOYEJCQ= github.com/stackitcloud/stackit-sdk-go/services/sfs v0.11.3 h1:u3IgZPwcD6yncKPK+PYqPKKaRW4EqfH8x4ARa53DlXI= From 4aaa3d5fdd434badd52c2a91912913d71452fb61 Mon Sep 17 00:00:00 2001 From: Carlo Goetz Date: Tue, 13 Oct 2026 16:14:24 +0200 Subject: [PATCH 2/2] chore(auth): remove non functional service-account token cmds STACKITCLI-472 --- docs/stackit_service-account.md | 1 - docs/stackit_service-account_token.md | 36 ---- docs/stackit_service-account_token_create.md | 47 ----- docs/stackit_service-account_token_list.md | 50 ----- docs/stackit_service-account_token_revoke.md | 43 ---- .../cmd/service-account/service_account.go | 2 - .../service-account/token/create/create.go | 146 ------------- .../token/create/create_test.go | 191 ----------------- .../cmd/service-account/token/list/list.go | 159 -------------- .../service-account/token/list/list_test.go | 199 ------------------ .../service-account/token/revoke/revoke.go | 120 ----------- .../token/revoke/revoke_test.go | 184 ---------------- internal/cmd/service-account/token/token.go | 30 --- 13 files changed, 1208 deletions(-) delete mode 100644 docs/stackit_service-account_token.md delete mode 100644 docs/stackit_service-account_token_create.md delete mode 100644 docs/stackit_service-account_token_list.md delete mode 100644 docs/stackit_service-account_token_revoke.md delete mode 100644 internal/cmd/service-account/token/create/create.go delete mode 100644 internal/cmd/service-account/token/create/create_test.go delete mode 100644 internal/cmd/service-account/token/list/list.go delete mode 100644 internal/cmd/service-account/token/list/list_test.go delete mode 100644 internal/cmd/service-account/token/revoke/revoke.go delete mode 100644 internal/cmd/service-account/token/revoke/revoke_test.go delete mode 100644 internal/cmd/service-account/token/token.go diff --git a/docs/stackit_service-account.md b/docs/stackit_service-account.md index 2d5d4d37d..002ffecca 100644 --- a/docs/stackit_service-account.md +++ b/docs/stackit_service-account.md @@ -35,5 +35,4 @@ stackit service-account [flags] * [stackit service-account get-jwks](./stackit_service-account_get-jwks.md) - Shows the JWKS for a service account * [stackit service-account key](./stackit_service-account_key.md) - Provides functionality for service account keys * [stackit service-account list](./stackit_service-account_list.md) - Lists all service accounts -* [stackit service-account token](./stackit_service-account_token.md) - Provides functionality for service account tokens diff --git a/docs/stackit_service-account_token.md b/docs/stackit_service-account_token.md deleted file mode 100644 index fcf521ca1..000000000 --- a/docs/stackit_service-account_token.md +++ /dev/null @@ -1,36 +0,0 @@ -## stackit service-account token - -Provides functionality for service account tokens - -### Synopsis - -Provides functionality for service account tokens. - -``` -stackit service-account token [flags] -``` - -### Options - -``` - -h, --help Help for "stackit service-account token" -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, (one of: [json, pretty, none, yaml]) - -p, --project-id string Project ID - --region string Target region for region-specific requests - --verbosity string Verbosity of the CLI, (one of: [debug, info, warning, error]) (default "info") -``` - -### SEE ALSO - -* [stackit service-account](./stackit_service-account.md) - Provides functionality for service accounts -* [stackit service-account token create](./stackit_service-account_token_create.md) - Creates an access token for a service account -* [stackit service-account token list](./stackit_service-account_token_list.md) - Lists access tokens of a service account -* [stackit service-account token revoke](./stackit_service-account_token_revoke.md) - Revokes an access token of a service account - diff --git a/docs/stackit_service-account_token_create.md b/docs/stackit_service-account_token_create.md deleted file mode 100644 index affb2f963..000000000 --- a/docs/stackit_service-account_token_create.md +++ /dev/null @@ -1,47 +0,0 @@ -## stackit service-account token create - -Creates an access token for a service account - -### Synopsis - -Creates an access token for a service account. -The access token can be then used for API calls (where enabled). -The token is only displayed upon creation, and it will not be recoverable later. - -``` -stackit service-account token create [flags] -``` - -### Examples - -``` - Create an access token for the service account with email "my-service-account-1234567@sa.stackit.cloud" with a default time to live - $ stackit service-account token create --email my-service-account-1234567@sa.stackit.cloud - - Create an access token for the service account with email "my-service-account-1234567@sa.stackit.cloud" with a time to live of 10 days - $ stackit service-account token create --email my-service-account-1234567@sa.stackit.cloud --ttl-days 10 -``` - -### Options - -``` - -e, --email string Service account email - -h, --help Help for "stackit service-account token create" - --ttl-days int32 How long (in days) the new access token is valid (default 90) -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, (one of: [json, pretty, none, yaml]) - -p, --project-id string Project ID - --region string Target region for region-specific requests - --verbosity string Verbosity of the CLI, (one of: [debug, info, warning, error]) (default "info") -``` - -### SEE ALSO - -* [stackit service-account token](./stackit_service-account_token.md) - Provides functionality for service account tokens - diff --git a/docs/stackit_service-account_token_list.md b/docs/stackit_service-account_token_list.md deleted file mode 100644 index 46cbb4c18..000000000 --- a/docs/stackit_service-account_token_list.md +++ /dev/null @@ -1,50 +0,0 @@ -## stackit service-account token list - -Lists access tokens of a service account - -### Synopsis - -Lists access tokens of a service account. -Only the metadata about the access tokens is shown, and not the tokens themselves. -Access tokens (including revoked tokens) are returned until they are expired. - -``` -stackit service-account token list [flags] -``` - -### Examples - -``` - List all access tokens of the service account with email "my-service-account-1234567@sa.stackit.cloud" - $ stackit service-account token list --email my-service-account-1234567@sa.stackit.cloud - - List all access tokens of the service account with email "my-service-account-1234567@sa.stackit.cloud" in JSON format - $ stackit service-account token list --email my-service-account-1234567@sa.stackit.cloud --output-format json - - List up to 10 access tokens of the service account with email "my-service-account-1234567@sa.stackit.cloud" - $ stackit service-account token list --email my-service-account-1234567@sa.stackit.cloud --limit 10 -``` - -### Options - -``` - -e, --email string Service account email - -h, --help Help for "stackit service-account token list" - --limit int Maximum number of entries to list -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, (one of: [json, pretty, none, yaml]) - -p, --project-id string Project ID - --region string Target region for region-specific requests - --verbosity string Verbosity of the CLI, (one of: [debug, info, warning, error]) (default "info") -``` - -### SEE ALSO - -* [stackit service-account token](./stackit_service-account_token.md) - Provides functionality for service account tokens - diff --git a/docs/stackit_service-account_token_revoke.md b/docs/stackit_service-account_token_revoke.md deleted file mode 100644 index a226c1c53..000000000 --- a/docs/stackit_service-account_token_revoke.md +++ /dev/null @@ -1,43 +0,0 @@ -## stackit service-account token revoke - -Revokes an access token of a service account - -### Synopsis - -Revokes an access token of a service account. -The access token is instantly revoked, any following calls with the token will be unauthorized. -The token metadata is still stored until the expiration time. - -``` -stackit service-account token revoke TOKEN_ID [flags] -``` - -### Examples - -``` - Revoke an access token with ID "xxx" of the service account with email "my-service-account-1234567@sa.stackit.cloud" - $ stackit service-account token revoke xxx --email my-service-account-1234567@sa.stackit.cloud -``` - -### Options - -``` - -e, --email string Service account email - -h, --help Help for "stackit service-account token revoke" -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, (one of: [json, pretty, none, yaml]) - -p, --project-id string Project ID - --region string Target region for region-specific requests - --verbosity string Verbosity of the CLI, (one of: [debug, info, warning, error]) (default "info") -``` - -### SEE ALSO - -* [stackit service-account token](./stackit_service-account_token.md) - Provides functionality for service account tokens - diff --git a/internal/cmd/service-account/service_account.go b/internal/cmd/service-account/service_account.go index 856be2fba..d8c341276 100644 --- a/internal/cmd/service-account/service_account.go +++ b/internal/cmd/service-account/service_account.go @@ -6,7 +6,6 @@ import ( getjwks "github.com/stackitcloud/stackit-cli/internal/cmd/service-account/get-jwks" "github.com/stackitcloud/stackit-cli/internal/cmd/service-account/key" "github.com/stackitcloud/stackit-cli/internal/cmd/service-account/list" - "github.com/stackitcloud/stackit-cli/internal/cmd/service-account/token" "github.com/stackitcloud/stackit-cli/internal/pkg/args" "github.com/stackitcloud/stackit-cli/internal/pkg/types" "github.com/stackitcloud/stackit-cli/internal/pkg/utils" @@ -33,5 +32,4 @@ func addSubcommands(cmd *cobra.Command, params *types.CmdParams) { cmd.AddCommand(getjwks.NewCmd(params)) cmd.AddCommand(key.NewCmd(params)) - cmd.AddCommand(token.NewCmd(params)) } diff --git a/internal/cmd/service-account/token/create/create.go b/internal/cmd/service-account/token/create/create.go deleted file mode 100644 index 4059c1711..000000000 --- a/internal/cmd/service-account/token/create/create.go +++ /dev/null @@ -1,146 +0,0 @@ -package create - -import ( - "context" - "fmt" - - "github.com/stackitcloud/stackit-cli/internal/pkg/types" - - "github.com/spf13/cobra" - serviceaccount "github.com/stackitcloud/stackit-sdk-go/services/serviceaccount/v2api" - - "github.com/stackitcloud/stackit-cli/internal/pkg/args" - "github.com/stackitcloud/stackit-cli/internal/pkg/errors" - "github.com/stackitcloud/stackit-cli/internal/pkg/examples" - "github.com/stackitcloud/stackit-cli/internal/pkg/flags" - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - "github.com/stackitcloud/stackit-cli/internal/pkg/services/service-account/client" -) - -const ( - serviceAccountEmailFlag = "email" - ttlDaysFlag = "ttl-days" - - defaultTTLDays = 90 -) - -type inputModel struct { - *globalflags.GlobalFlagModel - - ServiceAccountEmail string - TTLDays int32 -} - -func NewCmd(params *types.CmdParams) *cobra.Command { - cmd := &cobra.Command{ - Use: "create", - Short: "Creates an access token for a service account", - Long: fmt.Sprintf("%s\n%s\n%s", - "Creates an access token for a service account.", - "The access token can be then used for API calls (where enabled).", - "The token is only displayed upon creation, and it will not be recoverable later.", - ), - Args: args.NoArgs, - Example: examples.Build( - examples.NewExample( - `Create an access token for the service account with email "my-service-account-1234567@sa.stackit.cloud" with a default time to live`, - "$ stackit service-account token create --email my-service-account-1234567@sa.stackit.cloud"), - examples.NewExample( - `Create an access token for the service account with email "my-service-account-1234567@sa.stackit.cloud" with a time to live of 10 days`, - "$ stackit service-account token create --email my-service-account-1234567@sa.stackit.cloud --ttl-days 10"), - ), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - model, err := parseInput(params.Printer, cmd, args) - if err != nil { - return err - } - - // Configure API client - apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) - if err != nil { - return err - } - - prompt := fmt.Sprintf("Are you sure you want to create an access token for service account %s?", model.ServiceAccountEmail) - err = params.Printer.PromptForConfirmation(prompt) - if err != nil { - return err - } - - // Call API - req := buildRequest(ctx, model, apiClient) - token, err := req.Execute() - if err != nil { - return fmt.Errorf("create access token: %w", err) - } - - return outputResult(params.Printer, model.OutputFormat, model.ServiceAccountEmail, token) - }, - } - - configureFlags(cmd) - return cmd -} - -func configureFlags(cmd *cobra.Command) { - cmd.Flags().StringP(serviceAccountEmailFlag, "e", "", "Service account email") - cmd.Flags().Int32(ttlDaysFlag, defaultTTLDays, "How long (in days) the new access token is valid") - - err := flags.MarkFlagsRequired(cmd, serviceAccountEmailFlag) - cobra.CheckErr(err) -} - -func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { - globalFlags := globalflags.Parse(p, cmd) - if globalFlags.ProjectId == "" { - return nil, &errors.ProjectIdError{} - } - - email := flags.FlagToStringValue(p, cmd, serviceAccountEmailFlag) - if email == "" { - return nil, &errors.FlagValidationError{ - Flag: serviceAccountEmailFlag, - Details: "can't be empty", - } - } - - ttlDays := flags.FlagWithDefaultToInt32Value(p, cmd, ttlDaysFlag) - if ttlDays < 1 { - return nil, &errors.FlagValidationError{ - Flag: serviceAccountEmailFlag, - Details: "time to live should be at least 1 day", - } - } - - model := inputModel{ - GlobalFlagModel: globalFlags, - ServiceAccountEmail: email, - TTLDays: ttlDays, - } - - p.DebugInputModel(model) - return &model, nil -} - -func buildRequest(ctx context.Context, model *inputModel, apiClient *serviceaccount.APIClient) serviceaccount.ApiCreateAccessTokenRequest { - req := apiClient.DefaultAPI.CreateAccessToken(ctx, model.ProjectId, model.ServiceAccountEmail) - req = req.CreateAccessTokenPayload(serviceaccount.CreateAccessTokenPayload{ - TtlDays: model.TTLDays, - }) - return req -} - -func outputResult(p *print.Printer, outputFormat, serviceAccountEmail string, token *serviceaccount.AccessToken) error { - return p.OutputResult(outputFormat, token, func() error { - if token == nil { - return fmt.Errorf("token is nil") - } - - p.Outputf("Created access token for service account %s. Token ID: %s\n\n", serviceAccountEmail, token.Id) - p.Outputf("Valid until: %s\n", token.ValidUntil) - p.Outputf("Token: %s\n", token.Token) - return nil - }) -} diff --git a/internal/cmd/service-account/token/create/create_test.go b/internal/cmd/service-account/token/create/create_test.go deleted file mode 100644 index 74b781630..000000000 --- a/internal/cmd/service-account/token/create/create_test.go +++ /dev/null @@ -1,191 +0,0 @@ -package create - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/google/uuid" - serviceaccount "github.com/stackitcloud/stackit-sdk-go/services/serviceaccount/v2api" - - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/testparams" - "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" -) - -var projectIdFlag = globalflags.ProjectIdFlag - -type testCtxKey struct{} - -var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") -var testClient = &serviceaccount.APIClient{DefaultAPI: &serviceaccount.DefaultAPIService{}} -var testProjectId = uuid.NewString() -var testServiceAccountEmail = "my-service-account-1234567@sa.stackit.cloud" -var testTTLDaysString = "90" -var testTTLDays = int32(90) - -func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { - flagValues := map[string]string{ - projectIdFlag: testProjectId, - serviceAccountEmailFlag: testServiceAccountEmail, - ttlDaysFlag: testTTLDaysString, - } - for _, mod := range mods { - mod(flagValues) - } - return flagValues -} - -func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { - model := &inputModel{ - GlobalFlagModel: &globalflags.GlobalFlagModel{ - ProjectId: testProjectId, - Verbosity: globalflags.VerbosityDefault, - }, - ServiceAccountEmail: testServiceAccountEmail, - TTLDays: testTTLDays, - } - for _, mod := range mods { - mod(model) - } - return model -} - -func fixtureRequest(mods ...func(request *serviceaccount.ApiCreateAccessTokenRequest)) serviceaccount.ApiCreateAccessTokenRequest { - request := testClient.DefaultAPI.CreateAccessToken(testCtx, testProjectId, testServiceAccountEmail) - request = request.CreateAccessTokenPayload(serviceaccount.CreateAccessTokenPayload{ - TtlDays: testTTLDays, - }) - for _, mod := range mods { - mod(&request) - } - return request -} - -func TestParseInput(t *testing.T) { - tests := []struct { - description string - argValues []string - flagValues map[string]string - isValid bool - expectedModel *inputModel - }{ - { - description: "base", - flagValues: fixtureFlagValues(), - isValid: true, - expectedModel: fixtureInputModel(), - }, - { - description: "no values", - flagValues: map[string]string{}, - isValid: false, - }, - { - description: "project id missing", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, projectIdFlag) - }), - isValid: false, - }, - { - description: "project id invalid 1", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[projectIdFlag] = "" - }), - isValid: false, - }, - { - description: "project id invalid 2", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[projectIdFlag] = "invalid-uuid" - }), - isValid: false, - }, - { - description: "service account email missing", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, serviceAccountEmailFlag) - }), - isValid: false, - }, - { - description: "ttl days invalid", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[ttlDaysFlag] = "invalid" - }), - isValid: false, - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) - }) - } -} - -func TestBuildRequest(t *testing.T) { - tests := []struct { - description string - model *inputModel - isValid bool - expectedRequest serviceaccount.ApiCreateAccessTokenRequest - }{ - { - description: "base", - model: fixtureInputModel(), - isValid: true, - expectedRequest: fixtureRequest(), - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - request := buildRequest(testCtx, tt.model, testClient) - - diff := cmp.Diff(request, tt.expectedRequest, - cmp.AllowUnexported(tt.expectedRequest), - cmpopts.EquateComparable(testCtx, serviceaccount.DefaultAPIService{}), - ) - if diff != "" { - t.Fatalf("Data does not match: %s", diff) - } - }) - } -} - -func TestOutputResult(t *testing.T) { - type args struct { - outputFormat string - serviceAccountEmail string - token *serviceaccount.AccessToken - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "empty", - args: args{}, - wantErr: true, - }, - { - name: "empty token", - args: args{ - token: &serviceaccount.AccessToken{}, - }, - wantErr: false, - }, - } - params := testparams.NewTestParams() - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := outputResult(params.Printer, tt.args.outputFormat, tt.args.serviceAccountEmail, tt.args.token); (err != nil) != tt.wantErr { - t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/internal/cmd/service-account/token/list/list.go b/internal/cmd/service-account/token/list/list.go deleted file mode 100644 index 1195cdf86..000000000 --- a/internal/cmd/service-account/token/list/list.go +++ /dev/null @@ -1,159 +0,0 @@ -package list - -import ( - "context" - "fmt" - - "github.com/stackitcloud/stackit-cli/internal/pkg/types" - - "github.com/spf13/cobra" - serviceaccount "github.com/stackitcloud/stackit-sdk-go/services/serviceaccount/v2api" - - "github.com/stackitcloud/stackit-cli/internal/pkg/args" - "github.com/stackitcloud/stackit-cli/internal/pkg/errors" - "github.com/stackitcloud/stackit-cli/internal/pkg/examples" - "github.com/stackitcloud/stackit-cli/internal/pkg/flags" - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - "github.com/stackitcloud/stackit-cli/internal/pkg/services/service-account/client" - "github.com/stackitcloud/stackit-cli/internal/pkg/tables" -) - -const ( - limitFlag = "limit" - serviceAccountEmailFlag = "email" -) - -type inputModel struct { - *globalflags.GlobalFlagModel - - ServiceAccountEmail string - Limit *int64 -} - -func NewCmd(params *types.CmdParams) *cobra.Command { - cmd := &cobra.Command{ - Use: "list", - Short: "Lists access tokens of a service account", - Long: fmt.Sprintf("%s\n%s\n%s", - "Lists access tokens of a service account.", - "Only the metadata about the access tokens is shown, and not the tokens themselves.", - "Access tokens (including revoked tokens) are returned until they are expired.", - ), - Args: args.NoArgs, - Example: examples.Build( - examples.NewExample( - `List all access tokens of the service account with email "my-service-account-1234567@sa.stackit.cloud"`, - "$ stackit service-account token list --email my-service-account-1234567@sa.stackit.cloud"), - examples.NewExample( - `List all access tokens of the service account with email "my-service-account-1234567@sa.stackit.cloud" in JSON format`, - "$ stackit service-account token list --email my-service-account-1234567@sa.stackit.cloud --output-format json"), - examples.NewExample( - `List up to 10 access tokens of the service account with email "my-service-account-1234567@sa.stackit.cloud"`, - "$ stackit service-account token list --email my-service-account-1234567@sa.stackit.cloud --limit 10"), - ), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - model, err := parseInput(params.Printer, cmd, args) - if err != nil { - return err - } - - // Configure API client - apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) - if err != nil { - return err - } - - // Call API - req := buildRequest(ctx, model, apiClient) - resp, err := req.Execute() - if err != nil { - return fmt.Errorf("list tokens metadata: %w", err) - } - tokensMetadata := resp.Items - - // Truncate output - if model.Limit != nil && len(tokensMetadata) > int(*model.Limit) { - tokensMetadata = tokensMetadata[:*model.Limit] - } - - return outputResult(params.Printer, model.OutputFormat, model.ServiceAccountEmail, tokensMetadata) - }, - } - - configureFlags(cmd) - return cmd -} - -func configureFlags(cmd *cobra.Command) { - cmd.Flags().StringP(serviceAccountEmailFlag, "e", "", "Service account email") - cmd.Flags().Int64(limitFlag, 0, "Maximum number of entries to list") - - err := flags.MarkFlagsRequired(cmd, serviceAccountEmailFlag) - cobra.CheckErr(err) -} - -func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { - globalFlags := globalflags.Parse(p, cmd) - if globalFlags.ProjectId == "" { - return nil, &errors.ProjectIdError{} - } - - email := flags.FlagToStringValue(p, cmd, serviceAccountEmailFlag) - if email == "" { - return nil, &errors.FlagValidationError{ - Flag: serviceAccountEmailFlag, - Details: "can't be empty.", - } - } - - limit := flags.FlagToInt64Pointer(p, cmd, limitFlag) - if limit != nil && *limit < 1 { - return nil, &errors.FlagValidationError{ - Flag: limitFlag, - Details: "must be greater than 0", - } - } - - model := inputModel{ - GlobalFlagModel: globalFlags, - ServiceAccountEmail: email, - Limit: limit, - } - - p.DebugInputModel(model) - return &model, nil -} - -func buildRequest(ctx context.Context, model *inputModel, apiClient *serviceaccount.APIClient) serviceaccount.ApiListAccessTokensRequest { - req := apiClient.DefaultAPI.ListAccessTokens(ctx, model.ProjectId, model.ServiceAccountEmail) - return req -} - -func outputResult(p *print.Printer, outputFormat, serviceAccountEmail string, tokensMetadata []serviceaccount.AccessTokenMetadata) error { - return p.OutputResult(outputFormat, tokensMetadata, func() error { - if len(tokensMetadata) == 0 { - p.Outputf("No tokens found for service account with email %q\n", serviceAccountEmail) - return nil - } - - table := tables.NewTable() - table.SetHeader("ID", "ACTIVE", "CREATED_AT", "VALID_UNTIL") - for i := range tokensMetadata { - t := tokensMetadata[i] - table.AddRow( - t.Id, - t.Active, - t.CreatedAt, - t.ValidUntil, - ) - } - err := table.Display(p) - if err != nil { - return fmt.Errorf("render table: %w", err) - } - - return nil - }) -} diff --git a/internal/cmd/service-account/token/list/list_test.go b/internal/cmd/service-account/token/list/list_test.go deleted file mode 100644 index 09ea02c02..000000000 --- a/internal/cmd/service-account/token/list/list_test.go +++ /dev/null @@ -1,199 +0,0 @@ -package list - -import ( - "context" - "testing" - - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/testparams" - "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" - "github.com/stackitcloud/stackit-cli/internal/pkg/utils" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/google/uuid" - serviceaccount "github.com/stackitcloud/stackit-sdk-go/services/serviceaccount/v2api" -) - -var projectIdFlag = globalflags.ProjectIdFlag - -type testCtxKey struct{} - -var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") -var testClient = &serviceaccount.APIClient{DefaultAPI: &serviceaccount.DefaultAPIService{}} -var testProjectId = uuid.NewString() -var testServiceAccountEmail = "my-service-account-1234567@sa.stackit.cloud" - -func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { - flagValues := map[string]string{ - projectIdFlag: testProjectId, - serviceAccountEmailFlag: testServiceAccountEmail, - limitFlag: "10", - } - for _, mod := range mods { - mod(flagValues) - } - return flagValues -} - -func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { - model := &inputModel{ - GlobalFlagModel: &globalflags.GlobalFlagModel{ - ProjectId: testProjectId, - Verbosity: globalflags.VerbosityDefault, - }, - ServiceAccountEmail: testServiceAccountEmail, - Limit: utils.Ptr(int64(10)), - } - for _, mod := range mods { - mod(model) - } - return model -} - -func fixtureRequest(mods ...func(request *serviceaccount.ApiListAccessTokensRequest)) serviceaccount.ApiListAccessTokensRequest { - request := testClient.DefaultAPI.ListAccessTokens(testCtx, testProjectId, testServiceAccountEmail) - for _, mod := range mods { - mod(&request) - } - return request -} - -func TestParseInput(t *testing.T) { - tests := []struct { - description string - argValues []string - flagValues map[string]string - isValid bool - expectedModel *inputModel - }{ - { - description: "base", - flagValues: fixtureFlagValues(), - isValid: true, - expectedModel: fixtureInputModel(), - }, - { - description: "no values", - flagValues: map[string]string{}, - isValid: false, - }, - { - description: "project id missing", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, projectIdFlag) - }), - isValid: false, - }, - { - description: "project id invalid 1", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[projectIdFlag] = "" - }), - isValid: false, - }, - { - description: "project id invalid 2", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[projectIdFlag] = "invalid-uuid" - }), - isValid: false, - }, - { - description: "service account email missing", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, serviceAccountEmailFlag) - }), - isValid: false, - }, - { - description: "limit invalid", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[limitFlag] = "invalid" - }), - isValid: false, - }, - { - description: "limit invalid 2", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[limitFlag] = "0" - }), - isValid: false, - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) - }) - } -} - -func TestBuildRequest(t *testing.T) { - tests := []struct { - description string - model *inputModel - expectedRequest serviceaccount.ApiListAccessTokensRequest - }{ - { - description: "base", - model: fixtureInputModel(), - expectedRequest: fixtureRequest(), - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - request := buildRequest(testCtx, tt.model, testClient) - - diff := cmp.Diff(request, tt.expectedRequest, - cmp.AllowUnexported(tt.expectedRequest), - cmpopts.EquateComparable(testCtx, serviceaccount.DefaultAPIService{}), - ) - if diff != "" { - t.Fatalf("Data does not match: %s", diff) - } - }) - } -} - -func TestOutputResult(t *testing.T) { - type args struct { - outputFormat string - serviceAccountEmail string - tokensMetadata []serviceaccount.AccessTokenMetadata - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "empty", - args: args{}, - wantErr: false, - }, - { - name: "empty tokens metadata slice", - args: args{ - tokensMetadata: []serviceaccount.AccessTokenMetadata{}, - }, - wantErr: false, - }, - { - name: "empty token metadata in tokens metadata slice", - args: args{ - tokensMetadata: []serviceaccount.AccessTokenMetadata{{}}, - }, - wantErr: false, - }, - } - params := testparams.NewTestParams() - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := outputResult(params.Printer, tt.args.outputFormat, tt.args.serviceAccountEmail, tt.args.tokensMetadata); (err != nil) != tt.wantErr { - t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} diff --git a/internal/cmd/service-account/token/revoke/revoke.go b/internal/cmd/service-account/token/revoke/revoke.go deleted file mode 100644 index b9e531084..000000000 --- a/internal/cmd/service-account/token/revoke/revoke.go +++ /dev/null @@ -1,120 +0,0 @@ -package revoke - -import ( - "context" - "fmt" - - "github.com/stackitcloud/stackit-cli/internal/pkg/types" - - "github.com/stackitcloud/stackit-cli/internal/pkg/args" - "github.com/stackitcloud/stackit-cli/internal/pkg/errors" - "github.com/stackitcloud/stackit-cli/internal/pkg/examples" - "github.com/stackitcloud/stackit-cli/internal/pkg/flags" - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - "github.com/stackitcloud/stackit-cli/internal/pkg/services/service-account/client" - "github.com/stackitcloud/stackit-cli/internal/pkg/utils" - - "github.com/spf13/cobra" - serviceaccount "github.com/stackitcloud/stackit-sdk-go/services/serviceaccount/v2api" -) - -const ( - serviceAccountEmailFlag = "email" - tokenIdArg = "TOKEN_ID" -) - -type inputModel struct { - *globalflags.GlobalFlagModel - - ServiceAccountEmail string - TokenId string -} - -func NewCmd(params *types.CmdParams) *cobra.Command { - cmd := &cobra.Command{ - Use: fmt.Sprintf("revoke %s", tokenIdArg), - Short: "Revokes an access token of a service account", - Long: fmt.Sprintf("%s\n%s\n%s", - "Revokes an access token of a service account.", - "The access token is instantly revoked, any following calls with the token will be unauthorized.", - "The token metadata is still stored until the expiration time.", - ), - Args: args.SingleArg(tokenIdArg, utils.ValidateUUID), - Example: examples.Build( - examples.NewExample( - `Revoke an access token with ID "xxx" of the service account with email "my-service-account-1234567@sa.stackit.cloud"`, - "$ stackit service-account token revoke xxx --email my-service-account-1234567@sa.stackit.cloud"), - ), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - model, err := parseInput(params.Printer, cmd, args) - if err != nil { - return err - } - - // Configure API client - apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) - if err != nil { - return err - } - - prompt := fmt.Sprintf("Are you sure you want to revoke the access token with ID %q?", model.TokenId) - err = params.Printer.PromptForConfirmation(prompt) - if err != nil { - return err - } - - // Call API - req := buildRequest(ctx, model, apiClient) - err = req.Execute() - if err != nil { - return fmt.Errorf("revoke access token: %w", err) - } - - params.Printer.Info("Revoked access token with ID %q\n", model.TokenId) - return nil - }, - } - - configureFlags(cmd) - return cmd -} - -func configureFlags(cmd *cobra.Command) { - cmd.Flags().StringP(serviceAccountEmailFlag, "e", "", "Service account email") - - err := flags.MarkFlagsRequired(cmd, serviceAccountEmailFlag) - cobra.CheckErr(err) -} - -func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { - tokenId := inputArgs[0] - - globalFlags := globalflags.Parse(p, cmd) - if globalFlags.ProjectId == "" { - return nil, &errors.ProjectIdError{} - } - - email := flags.FlagToStringValue(p, cmd, serviceAccountEmailFlag) - if email == "" { - return nil, &errors.FlagValidationError{ - Flag: serviceAccountEmailFlag, - Details: "can't be empty", - } - } - - model := inputModel{ - GlobalFlagModel: globalFlags, - ServiceAccountEmail: email, - TokenId: tokenId, - } - - p.DebugInputModel(model) - return &model, nil -} - -func buildRequest(ctx context.Context, model *inputModel, apiClient *serviceaccount.APIClient) serviceaccount.ApiDeleteAccessTokenRequest { - req := apiClient.DefaultAPI.DeleteAccessToken(ctx, model.ProjectId, model.ServiceAccountEmail, model.TokenId) - return req -} diff --git a/internal/cmd/service-account/token/revoke/revoke_test.go b/internal/cmd/service-account/token/revoke/revoke_test.go deleted file mode 100644 index aa680efaf..000000000 --- a/internal/cmd/service-account/token/revoke/revoke_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package revoke - -import ( - "context" - "testing" - - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/google/uuid" - serviceaccount "github.com/stackitcloud/stackit-sdk-go/services/serviceaccount/v2api" -) - -var projectIdFlag = globalflags.ProjectIdFlag - -type testCtxKey struct{} - -var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") -var testClient = &serviceaccount.APIClient{DefaultAPI: &serviceaccount.DefaultAPIService{}} -var testProjectId = uuid.NewString() -var testServiceAccountEmail = "my-service-account-1234567@sa.stackit.cloud" -var testTokenId = uuid.NewString() - -func fixtureArgValues(mods ...func(argValues []string)) []string { - argValues := []string{ - testTokenId, - } - for _, mod := range mods { - mod(argValues) - } - return argValues -} - -func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { - flagValues := map[string]string{ - projectIdFlag: testProjectId, - serviceAccountEmailFlag: testServiceAccountEmail, - } - for _, mod := range mods { - mod(flagValues) - } - return flagValues -} - -func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { - model := &inputModel{ - GlobalFlagModel: &globalflags.GlobalFlagModel{ - ProjectId: testProjectId, - Verbosity: globalflags.VerbosityDefault, - }, - ServiceAccountEmail: testServiceAccountEmail, - TokenId: testTokenId, - } - for _, mod := range mods { - mod(model) - } - return model -} - -func fixtureRequest(mods ...func(request *serviceaccount.ApiDeleteAccessTokenRequest)) serviceaccount.ApiDeleteAccessTokenRequest { - request := testClient.DefaultAPI.DeleteAccessToken(testCtx, testProjectId, testServiceAccountEmail, testTokenId) - for _, mod := range mods { - mod(&request) - } - return request -} - -func TestParseInput(t *testing.T) { - tests := []struct { - description string - argValues []string - flagValues map[string]string - isValid bool - expectedModel *inputModel - }{ - { - description: "base", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(), - isValid: true, - expectedModel: fixtureInputModel(), - }, - { - description: "no values", - argValues: []string{}, - flagValues: map[string]string{}, - isValid: false, - }, - { - description: "no arg values", - argValues: []string{}, - flagValues: fixtureFlagValues(), - isValid: false, - }, - { - description: "no flag values", - argValues: fixtureArgValues(), - flagValues: map[string]string{}, - isValid: false, - }, - { - description: "project id missing", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, projectIdFlag) - }), - isValid: false, - }, - { - description: "project id invalid 1", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[projectIdFlag] = "" - }), - isValid: false, - }, - { - description: "project id invalid 2", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[projectIdFlag] = "invalid-uuid" - }), - isValid: false, - }, - { - description: "service account email missing", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, serviceAccountEmailFlag) - }), - isValid: false, - }, - { - description: "token id invalid 1", - argValues: []string{""}, - flagValues: fixtureFlagValues(), - isValid: false, - }, - { - description: "token id invalid 2", - argValues: []string{"invalid-uuid"}, - flagValues: fixtureFlagValues(), - isValid: false, - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) - }) - } -} - -func TestBuildRequest(t *testing.T) { - tests := []struct { - description string - model *inputModel - isValid bool - expectedRequest serviceaccount.ApiDeleteAccessTokenRequest - }{ - { - description: "base", - model: fixtureInputModel(), - isValid: true, - expectedRequest: fixtureRequest(), - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - request := buildRequest(testCtx, tt.model, testClient) - - diff := cmp.Diff(request, tt.expectedRequest, - cmp.AllowUnexported(tt.expectedRequest), - cmpopts.EquateComparable(testCtx, serviceaccount.DefaultAPIService{}), - ) - if diff != "" { - t.Fatalf("Data does not match: %s", diff) - } - }) - } -} diff --git a/internal/cmd/service-account/token/token.go b/internal/cmd/service-account/token/token.go deleted file mode 100644 index 5faeff394..000000000 --- a/internal/cmd/service-account/token/token.go +++ /dev/null @@ -1,30 +0,0 @@ -package token - -import ( - "github.com/stackitcloud/stackit-cli/internal/cmd/service-account/token/create" - "github.com/stackitcloud/stackit-cli/internal/cmd/service-account/token/list" - "github.com/stackitcloud/stackit-cli/internal/cmd/service-account/token/revoke" - "github.com/stackitcloud/stackit-cli/internal/pkg/args" - "github.com/stackitcloud/stackit-cli/internal/pkg/types" - "github.com/stackitcloud/stackit-cli/internal/pkg/utils" - - "github.com/spf13/cobra" -) - -func NewCmd(params *types.CmdParams) *cobra.Command { - cmd := &cobra.Command{ - Use: "token", - Short: "Provides functionality for service account tokens", - Long: "Provides functionality for service account tokens.", - Args: args.NoArgs, - Run: utils.CmdHelp, - } - addSubcommands(cmd, params) - return cmd -} - -func addSubcommands(cmd *cobra.Command, params *types.CmdParams) { - cmd.AddCommand(create.NewCmd(params)) - cmd.AddCommand(list.NewCmd(params)) - cmd.AddCommand(revoke.NewCmd(params)) -}