diff --git a/cmd/iam/accesselevations/accesselevations.go b/cmd/iam/accesselevations/accesselevations.go new file mode 100644 index 0000000..fe647c4 --- /dev/null +++ b/cmd/iam/accesselevations/accesselevations.go @@ -0,0 +1,63 @@ +package accesselevations + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/formattime" + "github.com/thalassa-cloud/cli/internal/shared" + clientiam "github.com/thalassa-cloud/client-go/iam" +) + +// AccessElevationsCmd manages temporary IAM access elevation requests. +var AccessElevationsCmd = &cobra.Command{ + Use: "access-elevations", + Aliases: []string{"access-elevation", "elevations", "ae"}, + Short: "Request and review temporary IAM access elevations", + Long: `Request temporary elevation to an IAM policy, and approve, reject, or revoke +requests in the current organisation/project scope. + +Use --mine on list/get to work with your own requests. create and cancel always +act on your requests; approve, reject, and revoke use the approver API.`, +} + +var ( + noHeader bool + showExactTime bool + mineFlag bool +) + +func printAccessElevationDetails(req *clientiam.IamAccessElevationRequest, exactTime bool) { + fmt.Printf("Identity: %s\n", req.Identity) + fmt.Printf("Status: %s\n", req.Status) + fmt.Printf("Reason: %s\n", req.Reason) + fmt.Printf("Expires: %s\n", formattime.FormatTime(req.RequestedExpiresAt.Local(), exactTime)) + fmt.Printf("Created: %s\n", formattime.FormatTime(req.CreatedAt.Local(), exactTime)) + if req.UpdatedAt != nil { + fmt.Printf("Updated: %s\n", formattime.FormatTime(req.UpdatedAt.Local(), exactTime)) + } + if req.Project != nil { + fmt.Printf("Project: %s (%s)\n", req.Project.Name, req.Project.Identity) + } else if req.ProjectId != "" { + fmt.Printf("Project: %s\n", req.ProjectId) + } + if req.IamPolicy != nil { + fmt.Printf("Policy: %s (%s)\n", req.IamPolicy.Name, req.IamPolicy.Identity) + } + if req.Requester != nil { + fmt.Printf("Requester: %s\n", shared.UserPtrDisplay(req.Requester)) + } + if req.ReviewedAt != nil { + fmt.Printf("Reviewed: %s\n", formattime.FormatTime(req.ReviewedAt.Local(), exactTime)) + } + if req.ReviewedBy != nil { + fmt.Printf("Reviewed by: %s\n", shared.UserPtrDisplay(req.ReviewedBy)) + } + if req.ReviewNote != nil && *req.ReviewNote != "" { + fmt.Printf("Review note: %s\n", *req.ReviewNote) + } + if req.IamPolicyBinding != nil { + fmt.Printf("Binding: %s (%s)\n", req.IamPolicyBinding.Name, req.IamPolicyBinding.Identity) + } +} diff --git a/cmd/iam/accesselevations/cancel.go b/cmd/iam/accesselevations/cancel.go new file mode 100644 index 0000000..e70c4f8 --- /dev/null +++ b/cmd/iam/accesselevations/cancel.go @@ -0,0 +1,48 @@ +package accesselevations + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" +) + +var cancelForce bool + +var cancelCmd = &cobra.Command{ + Use: "cancel ", + Short: "Cancel your pending access elevation request", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteIAMMyAccessElevationIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + ok, err := shared.PromptDestructiveUnlessForce(cancelForce, fmt.Sprintf( + "Are you sure you want to cancel this access elevation request?\n Request: %s\n", args[0])) + if err != nil { + return err + } + if !ok { + return nil + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + req, err := client.IAM().CancelAccessElevation(cmd.Context(), args[0]) + if err != nil { + return fmt.Errorf("failed to cancel access elevation: %w", err) + } + + printAccessElevationDetails(req, false) + return nil + }, +} + +func init() { + AccessElevationsCmd.AddCommand(cancelCmd) + cancelCmd.Flags().BoolVar(&cancelForce, shared.ForceKey, false, "Skip the confirmation prompt") +} diff --git a/cmd/iam/accesselevations/create.go b/cmd/iam/accesselevations/create.go new file mode 100644 index 0000000..be5fadd --- /dev/null +++ b/cmd/iam/accesselevations/create.go @@ -0,0 +1,94 @@ +package accesselevations + +import ( + "fmt" + "time" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/iamresolve" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientiam "github.com/thalassa-cloud/client-go/iam" +) + +var ( + createPolicy string + createReason string + createDuration string + createExpiresAt string + createLabels []string + createAnnotations []string +) + +var createCmd = &cobra.Command{ + Use: "create", + Short: "Request temporary elevation to an IAM policy", + Long: `Create an access elevation request for yourself against a policy. + +Exactly one of --duration or --expires-at is required.`, + Example: ` tcloud iam access-elevations create --policy admin --reason "incident response" --duration 2h + tcloud iam access-elevations create --policy pol-xxx --reason "break-glass" --expires-at 2026-10-01T20:00:00Z`, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + if createExpiresAt != "" && createDuration != "" { + return fmt.Errorf("specify at most one of --expires-at or --duration") + } + if createExpiresAt == "" && createDuration == "" { + return fmt.Errorf("specify one of --duration or --expires-at") + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + policy, err := iamresolve.ResolveIamPolicyRef(cmd.Context(), client.IAM(), createPolicy) + if err != nil { + return err + } + + create := clientiam.CreateAccessElevationRequest{ + PolicyIdentity: policy.Identity, + Reason: createReason, + Duration: createDuration, + Labels: shared.KeyValuePairsToMap(createLabels), + Annotations: shared.KeyValuePairsToMap(createAnnotations), + } + if create.Labels == nil { + create.Labels = map[string]string{} + } + if create.Annotations == nil { + create.Annotations = map[string]string{} + } + if createExpiresAt != "" { + t, err := time.Parse(time.RFC3339, createExpiresAt) + if err != nil { + return fmt.Errorf("--expires-at must be RFC3339 (e.g. 2026-12-31T23:59:59Z): %w", err) + } + create.ExpiresAt = &t + } + + req, err := client.IAM().CreateAccessElevation(cmd.Context(), create) + if err != nil { + return fmt.Errorf("failed to create access elevation: %w", err) + } + + printAccessElevationDetails(req, false) + return nil + }, +} + +func init() { + AccessElevationsCmd.AddCommand(createCmd) + createCmd.Flags().StringVar(&createPolicy, "policy", "", "IAM policy identity, slug, or name (required)") + createCmd.Flags().StringVar(&createReason, "reason", "", "Reason for the elevation request (required)") + createCmd.Flags().StringVar(&createDuration, "duration", "", "Relative TTL (e.g. 2h, 24h)") + createCmd.Flags().StringVar(&createExpiresAt, "expires-at", "", "Absolute expiry time (RFC3339)") + createCmd.Flags().StringSliceVar(&createLabels, "labels", nil, "Labels as key=value (repeatable)") + createCmd.Flags().StringSliceVar(&createAnnotations, "annotations", nil, "Annotations as key=value (repeatable)") + _ = createCmd.MarkFlagRequired("policy") + _ = createCmd.MarkFlagRequired("reason") + _ = createCmd.RegisterFlagCompletionFunc("policy", completion.CompleteIAMPolicyIdentityFlag) +} diff --git a/cmd/iam/accesselevations/get.go b/cmd/iam/accesselevations/get.go new file mode 100644 index 0000000..877b792 --- /dev/null +++ b/cmd/iam/accesselevations/get.go @@ -0,0 +1,44 @@ +package accesselevations + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientiam "github.com/thalassa-cloud/client-go/iam" +) + +var getCmd = &cobra.Command{ + Use: "get ", + Aliases: []string{"view", "show", "describe"}, + Short: "Show an access elevation request", + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteIAMAccessElevationIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + var req *clientiam.IamAccessElevationRequest + if mineFlag { + req, err = client.IAM().GetMyAccessElevation(cmd.Context(), args[0]) + } else { + req, err = client.IAM().GetAccessElevation(cmd.Context(), args[0]) + } + if err != nil { + return fmt.Errorf("failed to get access elevation: %w", err) + } + + printAccessElevationDetails(req, showExactTime) + return nil + }, +} + +func init() { + AccessElevationsCmd.AddCommand(getCmd) + getCmd.Flags().BoolVar(&mineFlag, "mine", false, "Fetch via your own requests API") + getCmd.Flags().BoolVar(&showExactTime, "exact-time", false, "Show full timestamps instead of relative time") +} diff --git a/cmd/iam/accesselevations/list.go b/cmd/iam/accesselevations/list.go new file mode 100644 index 0000000..05f131e --- /dev/null +++ b/cmd/iam/accesselevations/list.go @@ -0,0 +1,69 @@ +package accesselevations + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/table" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientiam "github.com/thalassa-cloud/client-go/iam" +) + +var listCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List access elevation requests", + Long: `List access elevation requests visible to approvers in the current scope. + +Use --mine to list only requests you created.`, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + var requests []clientiam.IamAccessElevationRequest + if mineFlag { + requests, err = client.IAM().ListMyAccessElevations(cmd.Context()) + } else { + requests, err = client.IAM().ListAccessElevations(cmd.Context()) + } + if err != nil { + return fmt.Errorf("failed to list access elevations: %w", err) + } + + body := make([][]string, 0, len(requests)) + for _, req := range requests { + policy := "" + if req.IamPolicy != nil { + policy = req.IamPolicy.Name + if policy == "" { + policy = req.IamPolicy.Identity + } + } + requester := shared.UserPtrDisplay(req.Requester) + body = append(body, []string{ + req.Identity, + string(req.Status), + policy, + requester, + req.Reason, + }) + } + if noHeader { + table.Print(nil, body) + } else { + table.Print([]string{"ID", "Status", "Policy", "Requester", "Reason"}, body) + } + return nil + }, +} + +func init() { + AccessElevationsCmd.AddCommand(listCmd) + listCmd.Flags().BoolVar(&noHeader, shared.NoHeaderKey, false, "Do not print table headers") + listCmd.Flags().BoolVar(&mineFlag, "mine", false, "List only requests you created") +} diff --git a/cmd/iam/accesselevations/review.go b/cmd/iam/accesselevations/review.go new file mode 100644 index 0000000..3618d52 --- /dev/null +++ b/cmd/iam/accesselevations/review.go @@ -0,0 +1,92 @@ +package accesselevations + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/thalassa-cloud/cli/internal/completion" + "github.com/thalassa-cloud/cli/internal/shared" + "github.com/thalassa-cloud/cli/internal/thalassaclient" + clientiam "github.com/thalassa-cloud/client-go/iam" +) + +type reviewAction struct { + use string + short string + confirm string + errPrefix string + call func(ctx context.Context, api *clientiam.Client, identity string, review clientiam.ReviewAccessElevationRequest) (*clientiam.IamAccessElevationRequest, error) +} + +func registerReviewCommand(a reviewAction) { + var force bool + var reviewNote string + + cmd := &cobra.Command{ + Use: a.use + " ", + Short: a.short, + Args: cobra.ExactArgs(1), + ValidArgsFunction: completion.CompleteIAMAccessElevationIdentity, + RunE: func(cmd *cobra.Command, args []string) error { + ok, err := shared.PromptDestructiveUnlessForce(force, fmt.Sprintf( + "%s\n Request: %s\n", a.confirm, args[0])) + if err != nil { + return err + } + if !ok { + return nil + } + + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return fmt.Errorf("failed to create client: %w", err) + } + + req, err := a.call(cmd.Context(), client.IAM(), args[0], clientiam.ReviewAccessElevationRequest{ + ReviewNote: reviewNote, + }) + if err != nil { + return fmt.Errorf("%s: %w", a.errPrefix, err) + } + + printAccessElevationDetails(req, false) + return nil + }, + } + + AccessElevationsCmd.AddCommand(cmd) + cmd.Flags().BoolVar(&force, shared.ForceKey, false, "Skip the confirmation prompt") + cmd.Flags().StringVar(&reviewNote, "review-note", "", "Optional note attached to the review") +} + +func init() { + registerReviewCommand(reviewAction{ + use: "approve", + short: "Approve a pending access elevation request", + confirm: "Are you sure you want to approve this access elevation request?", + errPrefix: "failed to approve access elevation", + call: func(ctx context.Context, api *clientiam.Client, identity string, review clientiam.ReviewAccessElevationRequest) (*clientiam.IamAccessElevationRequest, error) { + return api.ApproveAccessElevation(ctx, identity, review) + }, + }) + registerReviewCommand(reviewAction{ + use: "reject", + short: "Reject a pending access elevation request", + confirm: "Are you sure you want to reject this access elevation request?", + errPrefix: "failed to reject access elevation", + call: func(ctx context.Context, api *clientiam.Client, identity string, review clientiam.ReviewAccessElevationRequest) (*clientiam.IamAccessElevationRequest, error) { + return api.RejectAccessElevation(ctx, identity, review) + }, + }) + registerReviewCommand(reviewAction{ + use: "revoke", + short: "Revoke an approved access elevation", + confirm: "Are you sure you want to revoke this access elevation?", + errPrefix: "failed to revoke access elevation", + call: func(ctx context.Context, api *clientiam.Client, identity string, review clientiam.ReviewAccessElevationRequest) (*clientiam.IamAccessElevationRequest, error) { + return api.RevokeAccessElevation(ctx, identity, review) + }, + }) +} diff --git a/cmd/iam/iam.go b/cmd/iam/iam.go index 7984d0b..03e497c 100644 --- a/cmd/iam/iam.go +++ b/cmd/iam/iam.go @@ -3,6 +3,7 @@ package iam import ( "github.com/spf13/cobra" + "github.com/thalassa-cloud/cli/cmd/iam/accesselevations" "github.com/thalassa-cloud/cli/cmd/iam/federatedidentities" "github.com/thalassa-cloud/cli/cmd/iam/federatedidentityproviders" "github.com/thalassa-cloud/cli/cmd/iam/invites" @@ -18,9 +19,9 @@ import ( var IamCmd = &cobra.Command{ Use: "iam", Short: "Identity and access management for your organisation", - Long: `Manage teams, organisation members, custom roles, IAM policies, federated OIDC identities, -and related resources. Commands apply to the organisation selected in your context -(or the --organisation / -O flag).`, + Long: `Manage teams, organisation members, custom roles, IAM policies, access elevations, +federated OIDC identities, and related resources. Commands apply to the organisation +selected in your context (or the --organisation / -O flag).`, } func init() { @@ -28,6 +29,7 @@ func init() { IamCmd.AddCommand(members.MembersCmd) IamCmd.AddCommand(roles.RolesCmd) IamCmd.AddCommand(policies.PoliciesCmd) + IamCmd.AddCommand(accesselevations.AccessElevationsCmd) IamCmd.AddCommand(federatedidentities.FederatedIdentitiesCmd) IamCmd.AddCommand(federatedidentityproviders.FederatedIdentityProvidersCmd) IamCmd.AddCommand(invites.InvitesCmd) diff --git a/docs/tcloud/iam/_index.md b/docs/tcloud/iam/_index.md index 87d5295..beff9f8 100644 --- a/docs/tcloud/iam/_index.md +++ b/docs/tcloud/iam/_index.md @@ -13,9 +13,9 @@ Identity and access management for your organisation ### Synopsis -Manage teams, organisation members, custom roles, IAM policies, federated OIDC identities, -and related resources. Commands apply to the organisation selected in your context -(or the --organisation / -O flag). +Manage teams, organisation members, custom roles, IAM policies, access elevations, +federated OIDC identities, and related resources. Commands apply to the organisation +selected in your context (or the --organisation / -O flag). ### Options @@ -41,6 +41,7 @@ and related resources. Commands apply to the organisation selected in your conte ### SEE ALSO * [tcloud](/docs/tcloud/tcloud/) - A CLI for working with the Thalassa Cloud Platform +* [tcloud iam access-elevations](/docs/tcloud/iam/access-elevations/) - Request and review temporary IAM access elevations * [tcloud iam federated-identities](/docs/tcloud/iam/federated-identities/) - Federated identities (OIDC subject bindings) * [tcloud iam federated-identity-providers](/docs/tcloud/iam/federated-identity-providers/) - Federated OIDC identity providers * [tcloud iam invites](/docs/tcloud/iam/invites/) - Organisation member invitations diff --git a/docs/tcloud/iam/access-elevations/_index.md b/docs/tcloud/iam/access-elevations/_index.md new file mode 100644 index 0000000..21d4140 --- /dev/null +++ b/docs/tcloud/iam/access-elevations/_index.md @@ -0,0 +1,53 @@ +--- +linkTitle: "tcloud iam access-elevations" +title: "iam access-elevations" +slug: tcloud_iam_access-elevations +url: /docs/tcloud/iam/access-elevations/ +weight: 9911 +cascade: + type: docs +--- +## tcloud iam access-elevations + +Request and review temporary IAM access elevations + +### Synopsis + +Request temporary elevation to an IAM policy, and approve, reject, or revoke +requests in the current organisation/project scope. + +Use --mine on list/get to work with your own requests. create and cancel always +act on your requests; approve, reject, and revoke use the approver API. + +### Options + +``` + -h, --help help for access-elevations +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + --ignore-dir-config Ignore directory-local .thalassa defaults + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud iam](/docs/tcloud/tcloud_iam/) - Identity and access management for your organisation +* [tcloud iam access-elevations approve](/docs/tcloud/iam/access-elevations_approve/) - Approve a pending access elevation request +* [tcloud iam access-elevations cancel](/docs/tcloud/iam/access-elevations_cancel/) - Cancel your pending access elevation request +* [tcloud iam access-elevations create](/docs/tcloud/iam/access-elevations_create/) - Request temporary elevation to an IAM policy +* [tcloud iam access-elevations get](/docs/tcloud/iam/access-elevations_get/) - Show an access elevation request +* [tcloud iam access-elevations list](/docs/tcloud/iam/access-elevations_list/) - List access elevation requests +* [tcloud iam access-elevations reject](/docs/tcloud/iam/access-elevations_reject/) - Reject a pending access elevation request +* [tcloud iam access-elevations revoke](/docs/tcloud/iam/access-elevations_revoke/) - Revoke an approved access elevation + diff --git a/docs/tcloud/iam/access-elevations_approve/_index.md b/docs/tcloud/iam/access-elevations_approve/_index.md new file mode 100644 index 0000000..72b4567 --- /dev/null +++ b/docs/tcloud/iam/access-elevations_approve/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud iam access-elevations approve" +title: "iam access-elevations approve" +slug: tcloud_iam_access-elevations_approve +url: /docs/tcloud/iam/access-elevations_approve/ +weight: 9918 +cascade: + type: docs +--- +## tcloud iam access-elevations approve + +Approve a pending access elevation request + +``` +tcloud iam access-elevations approve [flags] +``` + +### Options + +``` + --force Skip the confirmation prompt + -h, --help help for approve + --review-note string Optional note attached to the review +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + --ignore-dir-config Ignore directory-local .thalassa defaults + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud iam access-elevations](/docs/tcloud/iam/access-elevations/) - Request and review temporary IAM access elevations + diff --git a/docs/tcloud/iam/access-elevations_cancel/_index.md b/docs/tcloud/iam/access-elevations_cancel/_index.md new file mode 100644 index 0000000..efb597d --- /dev/null +++ b/docs/tcloud/iam/access-elevations_cancel/_index.md @@ -0,0 +1,43 @@ +--- +linkTitle: "tcloud iam access-elevations cancel" +title: "iam access-elevations cancel" +slug: tcloud_iam_access-elevations_cancel +url: /docs/tcloud/iam/access-elevations_cancel/ +weight: 9917 +cascade: + type: docs +--- +## tcloud iam access-elevations cancel + +Cancel your pending access elevation request + +``` +tcloud iam access-elevations cancel [flags] +``` + +### Options + +``` + --force Skip the confirmation prompt + -h, --help help for cancel +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + --ignore-dir-config Ignore directory-local .thalassa defaults + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud iam access-elevations](/docs/tcloud/iam/access-elevations/) - Request and review temporary IAM access elevations + diff --git a/docs/tcloud/iam/access-elevations_create/_index.md b/docs/tcloud/iam/access-elevations_create/_index.md new file mode 100644 index 0000000..897d9eb --- /dev/null +++ b/docs/tcloud/iam/access-elevations_create/_index.md @@ -0,0 +1,61 @@ +--- +linkTitle: "tcloud iam access-elevations create" +title: "iam access-elevations create" +slug: tcloud_iam_access-elevations_create +url: /docs/tcloud/iam/access-elevations_create/ +weight: 9916 +cascade: + type: docs +--- +## tcloud iam access-elevations create + +Request temporary elevation to an IAM policy + +### Synopsis + +Create an access elevation request for yourself against a policy. + +Exactly one of --duration or --expires-at is required. + +``` +tcloud iam access-elevations create [flags] +``` + +### Examples + +``` + tcloud iam access-elevations create --policy admin --reason "incident response" --duration 2h + tcloud iam access-elevations create --policy pol-xxx --reason "break-glass" --expires-at 2026-10-01T20:00:00Z +``` + +### Options + +``` + --annotations strings Annotations as key=value (repeatable) + --duration string Relative TTL (e.g. 2h, 24h) + --expires-at string Absolute expiry time (RFC3339) + -h, --help help for create + --labels strings Labels as key=value (repeatable) + --policy string IAM policy identity, slug, or name (required) + --reason string Reason for the elevation request (required) +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + --ignore-dir-config Ignore directory-local .thalassa defaults + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud iam access-elevations](/docs/tcloud/iam/access-elevations/) - Request and review temporary IAM access elevations + diff --git a/docs/tcloud/iam/access-elevations_get/_index.md b/docs/tcloud/iam/access-elevations_get/_index.md new file mode 100644 index 0000000..5feb69c --- /dev/null +++ b/docs/tcloud/iam/access-elevations_get/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud iam access-elevations get" +title: "iam access-elevations get" +slug: tcloud_iam_access-elevations_get +url: /docs/tcloud/iam/access-elevations_get/ +weight: 9915 +cascade: + type: docs +--- +## tcloud iam access-elevations get + +Show an access elevation request + +``` +tcloud iam access-elevations get [flags] +``` + +### Options + +``` + --exact-time Show full timestamps instead of relative time + -h, --help help for get + --mine Fetch via your own requests API +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + --ignore-dir-config Ignore directory-local .thalassa defaults + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud iam access-elevations](/docs/tcloud/iam/access-elevations/) - Request and review temporary IAM access elevations + diff --git a/docs/tcloud/iam/access-elevations_list/_index.md b/docs/tcloud/iam/access-elevations_list/_index.md new file mode 100644 index 0000000..49e43e1 --- /dev/null +++ b/docs/tcloud/iam/access-elevations_list/_index.md @@ -0,0 +1,50 @@ +--- +linkTitle: "tcloud iam access-elevations list" +title: "iam access-elevations list" +slug: tcloud_iam_access-elevations_list +url: /docs/tcloud/iam/access-elevations_list/ +weight: 9914 +cascade: + type: docs +--- +## tcloud iam access-elevations list + +List access elevation requests + +### Synopsis + +List access elevation requests visible to approvers in the current scope. + +Use --mine to list only requests you created. + +``` +tcloud iam access-elevations list [flags] +``` + +### Options + +``` + -h, --help help for list + --mine List only requests you created + --no-header Do not print table headers +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + --ignore-dir-config Ignore directory-local .thalassa defaults + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud iam access-elevations](/docs/tcloud/iam/access-elevations/) - Request and review temporary IAM access elevations + diff --git a/docs/tcloud/iam/access-elevations_reject/_index.md b/docs/tcloud/iam/access-elevations_reject/_index.md new file mode 100644 index 0000000..d18604e --- /dev/null +++ b/docs/tcloud/iam/access-elevations_reject/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud iam access-elevations reject" +title: "iam access-elevations reject" +slug: tcloud_iam_access-elevations_reject +url: /docs/tcloud/iam/access-elevations_reject/ +weight: 9913 +cascade: + type: docs +--- +## tcloud iam access-elevations reject + +Reject a pending access elevation request + +``` +tcloud iam access-elevations reject [flags] +``` + +### Options + +``` + --force Skip the confirmation prompt + -h, --help help for reject + --review-note string Optional note attached to the review +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + --ignore-dir-config Ignore directory-local .thalassa defaults + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud iam access-elevations](/docs/tcloud/iam/access-elevations/) - Request and review temporary IAM access elevations + diff --git a/docs/tcloud/iam/access-elevations_revoke/_index.md b/docs/tcloud/iam/access-elevations_revoke/_index.md new file mode 100644 index 0000000..eab6174 --- /dev/null +++ b/docs/tcloud/iam/access-elevations_revoke/_index.md @@ -0,0 +1,44 @@ +--- +linkTitle: "tcloud iam access-elevations revoke" +title: "iam access-elevations revoke" +slug: tcloud_iam_access-elevations_revoke +url: /docs/tcloud/iam/access-elevations_revoke/ +weight: 9912 +cascade: + type: docs +--- +## tcloud iam access-elevations revoke + +Revoke an approved access elevation + +``` +tcloud iam access-elevations revoke [flags] +``` + +### Options + +``` + --force Skip the confirmation prompt + -h, --help help for revoke + --review-note string Optional note attached to the review +``` + +### Options inherited from parent commands + +``` + --access-token string Access Token authentication (overrides context) + --api string API endpoint (overrides context) + --client-id string OIDC client ID for OIDC authentication (overrides context) + --client-secret string OIDC client secret for OIDC authentication (overrides context) + -c, --context string Context name + --debug Debug mode + --ignore-dir-config Ignore directory-local .thalassa defaults + -O, --organisation string Organisation slug or identity (overrides context) + -P, --project string Project identity (overrides context; slug is resolved to identity; use "root" for organisation scope) + --token string Personal access token (overrides context) +``` + +### SEE ALSO + +* [tcloud iam access-elevations](/docs/tcloud/iam/access-elevations/) - Request and review temporary IAM access elevations + diff --git a/internal/completion/iam.go b/internal/completion/iam.go index 0c56f65..a20216b 100644 --- a/internal/completion/iam.go +++ b/internal/completion/iam.go @@ -229,6 +229,65 @@ func CompleteIAMPolicyIdentity(cmd *cobra.Command, args []string, toComplete str return completeIAMPolicyIdentities(cmd) } +// CompleteIAMAccessElevationIdentity completes access elevation identities (approver list, or --mine). +func CompleteIAMAccessElevationIdentity(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + if mine, _ := cmd.Flags().GetBool("mine"); mine { + return completeIAMMyAccessElevationIdentities(cmd) + } + return completeIAMAccessElevationIdentities(cmd) +} + +// CompleteIAMMyAccessElevationIdentity completes the caller's own access elevation identities. +func CompleteIAMMyAccessElevationIdentity(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return completeIAMMyAccessElevationIdentities(cmd) +} + +func completeIAMAccessElevationIdentities(cmd *cobra.Command) ([]string, cobra.ShellCompDirective) { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + requests, err := client.IAM().ListAccessElevations(cmd.Context()) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + out := make([]string, 0, len(requests)) + for _, req := range requests { + desc := string(req.Status) + if req.Reason != "" { + desc += " - " + req.Reason + } + out = append(out, req.Identity+"\t"+desc) + } + return out, cobra.ShellCompDirectiveNoFileComp +} + +func completeIAMMyAccessElevationIdentities(cmd *cobra.Command) ([]string, cobra.ShellCompDirective) { + client, err := thalassaclient.GetThalassaClient() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + requests, err := client.IAM().ListMyAccessElevations(cmd.Context()) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + out := make([]string, 0, len(requests)) + for _, req := range requests { + desc := string(req.Status) + if req.Reason != "" { + desc += " - " + req.Reason + } + out = append(out, req.Identity+"\t"+desc) + } + return out, cobra.ShellCompDirectiveNoFileComp +} + // CompleteIAMPolicyResourceType completes resource types usable in IAM policy rules. func CompleteIAMPolicyResourceType(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { client, err := thalassaclient.GetThalassaClient()