diff --git a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts index 38d7d1abd..82d1604eb 100644 --- a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts +++ b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts @@ -112,7 +112,7 @@ export const dynamicSecretLeaseServiceFactory = ({ }) ) as object; - const selectedTTL = ttl ?? dynamicSecretCfg.defaultTTL; + const selectedTTL = ttl || dynamicSecretCfg.defaultTTL; const { maxTTL } = dynamicSecretCfg; const expireAt = new Date(new Date().getTime() + ms(selectedTTL)); if (maxTTL) { @@ -187,7 +187,7 @@ export const dynamicSecretLeaseServiceFactory = ({ }) ) as object; - const selectedTTL = ttl ?? dynamicSecretCfg.defaultTTL; + const selectedTTL = ttl || dynamicSecretCfg.defaultTTL; const { maxTTL } = dynamicSecretCfg; const expireAt = new Date(dynamicSecretLease.expireAt.getTime() + ms(selectedTTL)); if (maxTTL) { diff --git a/cli/go.mod b/cli/go.mod index 36277ac37..b55a49c63 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -10,7 +10,7 @@ require ( github.com/fatih/semgroup v1.2.0 github.com/gitleaks/go-gitdiff v0.8.0 github.com/h2non/filetype v1.1.3 - github.com/infisical/go-sdk v0.3.8 + github.com/infisical/go-sdk v0.4.3 github.com/mattn/go-isatty v0.0.20 github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a github.com/muesli/mango-cobra v1.2.0 diff --git a/cli/go.sum b/cli/go.sum index 733a7b93d..537a146cb 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -265,8 +265,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/infisical/go-sdk v0.3.8 h1:0dGOhF3cwt0q5QzpnUs4lxwBiEza+DQYOyvEn7AfrM0= -github.com/infisical/go-sdk v0.3.8/go.mod h1:HHW7DgUqoolyQIUw/9HdpkZ3bDLwWyZ0HEtYiVaDKQw= +github.com/infisical/go-sdk v0.4.3 h1:O5ZJ2eCBAZDE9PIAfBPq9Utb2CgQKrhmj9R0oFTRu4U= +github.com/infisical/go-sdk v0.4.3/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M= github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go index 35767cd3f..3d050cd97 100644 --- a/cli/packages/api/api.go +++ b/cli/packages/api/api.go @@ -205,6 +205,25 @@ func CallGetAllWorkSpacesUserBelongsTo(httpClient *resty.Client) (GetWorkSpacesR return workSpacesResponse, nil } +func CallGetProjectById(httpClient *resty.Client, id string) (Project, error) { + var projectResponse GetProjectByIdResponse + response, err := httpClient. + R(). + SetResult(&projectResponse). + SetHeader("User-Agent", USER_AGENT). + Get(fmt.Sprintf("%v/v1/workspace/%s", config.INFISICAL_URL, id)) + + if err != nil { + return Project{}, err + } + + if response.IsError() { + return Project{}, fmt.Errorf("CallGetProjectById: Unsuccessful response: [response=%v]", response) + } + + return projectResponse.Project, nil +} + func CallIsAuthenticated(httpClient *resty.Client) bool { var workSpacesResponse GetWorkSpacesResponse response, err := httpClient. diff --git a/cli/packages/api/model.go b/cli/packages/api/model.go index adf7a814d..0ccd7af3a 100644 --- a/cli/packages/api/model.go +++ b/cli/packages/api/model.go @@ -128,6 +128,10 @@ type GetWorkSpacesResponse struct { } `json:"workspaces"` } +type GetProjectByIdResponse struct { + Project Project `json:"workspace"` +} + type GetOrganizationsResponse struct { Organizations []struct { ID string `json:"id"` @@ -162,6 +166,12 @@ type Secret struct { PlainTextKey string `json:"plainTextKey"` } +type Project struct { + ID string `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` +} + type RawSecret struct { SecretKey string `json:"secretKey,omitempty"` SecretValue string `json:"secretValue,omitempty"` diff --git a/cli/packages/cmd/dynamic_secret.go b/cli/packages/cmd/dynamic_secret.go new file mode 100644 index 000000000..befc35632 --- /dev/null +++ b/cli/packages/cmd/dynamic_secret.go @@ -0,0 +1,570 @@ +/* +Copyright (c) 2023 Infisical Inc. +*/ +package cmd + +import ( + "context" + "fmt" + + "github.com/Infisical/infisical-merge/packages/api" + "github.com/Infisical/infisical-merge/packages/config" + "github.com/Infisical/infisical-merge/packages/visualize" + + // "github.com/Infisical/infisical-merge/packages/models" + "github.com/Infisical/infisical-merge/packages/util" + // "github.com/Infisical/infisical-merge/packages/visualize" + "github.com/go-resty/resty/v2" + "github.com/posthog/posthog-go" + "github.com/spf13/cobra" + + infisicalSdk "github.com/infisical/go-sdk" + infisicalSdkModels "github.com/infisical/go-sdk/packages/models" +) + +var dynamicSecretCmd = &cobra.Command{ + Example: `infisical dynamic-secrets`, + Short: "Used to list dynamic secrets", + Use: "dynamic-secrets", + DisableFlagsInUseLine: true, + Args: cobra.NoArgs, + Run: getDynamicSecretList, +} + +func getDynamicSecretList(cmd *cobra.Command, args []string) { + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvFromWorkspaceFile() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } + } + + token, err := util.GetInfisicalToken(cmd) + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + projectId, err := cmd.Flags().GetString("projectId") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + secretsPath, err := cmd.Flags().GetString("path") + if err != nil { + util.HandleError(err, "Unable to parse path flag") + } + + var infisicalToken string + httpClient := resty.New() + + if projectId == "" { + workspaceFile, err := util.GetWorkSpaceFromFile() + if err != nil { + util.HandleError(err, "Unable to get local project details") + } + projectId = workspaceFile.WorkspaceId + } + + if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { + infisicalToken = token.Token + } else { + util.RequireLogin() + util.RequireLocalWorkspaceFile() + + loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails() + if err != nil { + util.HandleError(err, "Unable to authenticate") + } + + if loggedInUserDetails.LoginExpired { + util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again") + } + infisicalToken = loggedInUserDetails.UserCredentials.JTWToken + } + + httpClient.SetAuthToken(infisicalToken) + + infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ + SiteUrl: config.INFISICAL_URL, + UserAgent: api.USER_AGENT, + AutoTokenRefresh: false, + }) + infisicalClient.Auth().SetAccessToken(infisicalToken) + + projectDetails, err := api.CallGetProjectById(httpClient, projectId) + if err != nil { + util.HandleError(err, "To fetch project details") + } + + dynamicSecretRootCredentials, err := infisicalClient.DynamicSecrets().List(infisicalSdk.ListDynamicSecretsRootCredentialsOptions{ + ProjectSlug: projectDetails.Slug, + SecretPath: secretsPath, + EnvironmentSlug: environmentName, + }) + + if err != nil { + util.HandleError(err, "To fetch dynamic secret root credentials details") + } + + visualize.PrintAllDynamicRootCredentials(dynamicSecretRootCredentials) + Telemetry.CaptureEvent("cli-command:dynamic-secrets", posthog.NewProperties().Set("count", len(dynamicSecretRootCredentials)).Set("version", util.CLI_VERSION)) +} + +var dynamicSecretLeaseCmd = &cobra.Command{ + Example: `lease`, + Short: "Manage leases for dynamic secrets", + Use: "lease", + DisableFlagsInUseLine: true, +} + +var dynamicSecretLeaseCreateCmd = &cobra.Command{ + Example: `lease create "`, + Short: "Used to lease dynamic secret by name", + Use: "create [dynamic-secret]", + DisableFlagsInUseLine: true, + Args: cobra.ExactArgs(1), + Run: createDynamicSecretLeaseByName, +} + +func createDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { + dynamicSecretRootCredentialName := args[0] + + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvFromWorkspaceFile() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } + } + + token, err := util.GetInfisicalToken(cmd) + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + projectId, err := cmd.Flags().GetString("projectId") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + ttl, err := cmd.Flags().GetString("ttl") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + secretsPath, err := cmd.Flags().GetString("path") + if err != nil { + util.HandleError(err, "Unable to parse path flag") + } + + plainOutput, err := cmd.Flags().GetBool("plain") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + var infisicalToken string + httpClient := resty.New() + + if projectId == "" { + workspaceFile, err := util.GetWorkSpaceFromFile() + if err != nil { + util.HandleError(err, "Unable to get local project details") + } + projectId = workspaceFile.WorkspaceId + } + + if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { + infisicalToken = token.Token + } else { + util.RequireLogin() + util.RequireLocalWorkspaceFile() + + loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails() + if err != nil { + util.HandleError(err, "Unable to authenticate") + } + + if loggedInUserDetails.LoginExpired { + util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again") + } + infisicalToken = loggedInUserDetails.UserCredentials.JTWToken + } + + httpClient.SetAuthToken(infisicalToken) + + infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ + SiteUrl: config.INFISICAL_URL, + UserAgent: api.USER_AGENT, + AutoTokenRefresh: false, + }) + infisicalClient.Auth().SetAccessToken(infisicalToken) + + projectDetails, err := api.CallGetProjectById(httpClient, projectId) + if err != nil { + util.HandleError(err, "To fetch project details") + } + + dynamicSecretRootCredential, err := infisicalClient.DynamicSecrets().GetByName(infisicalSdk.GetDynamicSecretRootCredentialByNameOptions{ + DynamicSecretName: dynamicSecretRootCredentialName, + ProjectSlug: projectDetails.Slug, + SecretPath: secretsPath, + EnvironmentSlug: environmentName, + }) + + if err != nil { + util.HandleError(err, "To fetch dynamic secret root credentials details") + } + + leaseCredentials, _, leaseDetails, err := infisicalClient.DynamicSecrets().Leases().Create(infisicalSdk.CreateDynamicSecretLeaseOptions{ + DynamicSecretName: dynamicSecretRootCredential.Name, + ProjectSlug: projectDetails.Slug, + TTL: ttl, + SecretPath: secretsPath, + EnvironmentSlug: environmentName, + }) + if err != nil { + util.HandleError(err, "To lease dynamic secret") + } + + if plainOutput { + for key, value := range leaseCredentials { + if cred, ok := value.(string); ok { + fmt.Printf("%s=%s\n", key, cred) + } + } + } else { + fmt.Println("Dynamic Secret Leasing") + fmt.Printf("Name: %s\n", dynamicSecretRootCredential.Name) + fmt.Printf("Provider: %s\n", dynamicSecretRootCredential.Type) + fmt.Printf("Expire At: %s\n", leaseDetails.ExpireAt.Local().Format("02-Jan-2006 03:04:05 PM")) + visualize.PrintAllDyamicSecretLeaseCredentials(leaseCredentials) + } + + Telemetry.CaptureEvent("cli-command:dynamic-secrets lease", posthog.NewProperties().Set("type", dynamicSecretRootCredential.Type).Set("version", util.CLI_VERSION)) +} + +var dynamicSecretLeaseRenewCmd = &cobra.Command{ + Example: `lease create "`, + Short: "Used to renew dynamic secret lease by name", + Use: "renew [lease-id]", + DisableFlagsInUseLine: true, + Args: cobra.ExactArgs(1), + Run: renewDynamicSecretLeaseByName, +} + +func renewDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { + dynamicSecretLeaseId := args[0] + + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvFromWorkspaceFile() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } + } + + token, err := util.GetInfisicalToken(cmd) + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + projectId, err := cmd.Flags().GetString("projectId") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + ttl, err := cmd.Flags().GetString("ttl") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + secretsPath, err := cmd.Flags().GetString("path") + if err != nil { + util.HandleError(err, "Unable to parse path flag") + } + + var infisicalToken string + httpClient := resty.New() + + if projectId == "" { + workspaceFile, err := util.GetWorkSpaceFromFile() + if err != nil { + util.HandleError(err, "Unable to get local project details") + } + projectId = workspaceFile.WorkspaceId + } + + if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { + infisicalToken = token.Token + } else { + util.RequireLogin() + util.RequireLocalWorkspaceFile() + + loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails() + if err != nil { + util.HandleError(err, "Unable to authenticate") + } + + if loggedInUserDetails.LoginExpired { + util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again") + } + infisicalToken = loggedInUserDetails.UserCredentials.JTWToken + } + + httpClient.SetAuthToken(infisicalToken) + + infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ + SiteUrl: config.INFISICAL_URL, + UserAgent: api.USER_AGENT, + AutoTokenRefresh: false, + }) + infisicalClient.Auth().SetAccessToken(infisicalToken) + + projectDetails, err := api.CallGetProjectById(httpClient, projectId) + if err != nil { + util.HandleError(err, "To fetch project details") + } + + if err != nil { + util.HandleError(err, "To fetch dynamic secret root credentials details") + } + + leaseDetails, err := infisicalClient.DynamicSecrets().Leases().RenewById(infisicalSdk.RenewDynamicSecretLeaseOptions{ + ProjectSlug: projectDetails.Slug, + TTL: ttl, + SecretPath: secretsPath, + EnvironmentSlug: environmentName, + LeaseId: dynamicSecretLeaseId, + }) + if err != nil { + util.HandleError(err, "To renew dynamic secret lease") + } + + fmt.Println("Successfully renewed dynamic secret lease") + visualize.PrintAllDynamicSecretLeases([]infisicalSdkModels.DynamicSecretLease{leaseDetails}) + + Telemetry.CaptureEvent("cli-command:dynamic-secrets lease renew", posthog.NewProperties().Set("version", util.CLI_VERSION)) +} + +var dynamicSecretLeaseRevokeCmd = &cobra.Command{ + Example: `lease delete "`, + Short: "Used to delete dynamic secret lease by name", + Use: "delete [lease-id]", + DisableFlagsInUseLine: true, + Args: cobra.ExactArgs(1), + Run: revokeDynamicSecretLeaseByName, +} + +func revokeDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { + dynamicSecretLeaseId := args[0] + + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvFromWorkspaceFile() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } + } + + token, err := util.GetInfisicalToken(cmd) + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + projectId, err := cmd.Flags().GetString("projectId") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + secretsPath, err := cmd.Flags().GetString("path") + if err != nil { + util.HandleError(err, "Unable to parse path flag") + } + + var infisicalToken string + httpClient := resty.New() + + if projectId == "" { + workspaceFile, err := util.GetWorkSpaceFromFile() + if err != nil { + util.HandleError(err, "Unable to get local project details") + } + projectId = workspaceFile.WorkspaceId + } + + if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { + infisicalToken = token.Token + } else { + util.RequireLogin() + util.RequireLocalWorkspaceFile() + + loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails() + if err != nil { + util.HandleError(err, "Unable to authenticate") + } + + if loggedInUserDetails.LoginExpired { + util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again") + } + infisicalToken = loggedInUserDetails.UserCredentials.JTWToken + } + + httpClient.SetAuthToken(infisicalToken) + + infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ + SiteUrl: config.INFISICAL_URL, + UserAgent: api.USER_AGENT, + AutoTokenRefresh: false, + }) + infisicalClient.Auth().SetAccessToken(infisicalToken) + + projectDetails, err := api.CallGetProjectById(httpClient, projectId) + if err != nil { + util.HandleError(err, "To fetch project details") + } + + if err != nil { + util.HandleError(err, "To fetch dynamic secret root credentials details") + } + + leaseDetails, err := infisicalClient.DynamicSecrets().Leases().DeleteById(infisicalSdk.DeleteDynamicSecretLeaseOptions{ + ProjectSlug: projectDetails.Slug, + SecretPath: secretsPath, + EnvironmentSlug: environmentName, + LeaseId: dynamicSecretLeaseId, + }) + if err != nil { + util.HandleError(err, "To revoke dynamic secret lease") + } + + fmt.Println("Successfully revoked dynamic secret lease") + visualize.PrintAllDynamicSecretLeases([]infisicalSdkModels.DynamicSecretLease{leaseDetails}) + + Telemetry.CaptureEvent("cli-command:dynamic-secrets lease revoke", posthog.NewProperties().Set("version", util.CLI_VERSION)) +} + +var dynamicSecretLeaseListCmd = &cobra.Command{ + Example: `lease list "`, + Short: "Used to list leases of a dynamic secret by name", + Use: "list [dynamic-secret]", + DisableFlagsInUseLine: true, + Args: cobra.ExactArgs(1), + Run: listDynamicSecretLeaseByName, +} + +func listDynamicSecretLeaseByName(cmd *cobra.Command, args []string) { + dynamicSecretRootCredentialName := args[0] + + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvFromWorkspaceFile() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } + } + + token, err := util.GetInfisicalToken(cmd) + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + projectId, err := cmd.Flags().GetString("projectId") + if err != nil { + util.HandleError(err, "Unable to parse flag") + } + + secretsPath, err := cmd.Flags().GetString("path") + if err != nil { + util.HandleError(err, "Unable to parse path flag") + } + + var infisicalToken string + httpClient := resty.New() + + if projectId == "" { + workspaceFile, err := util.GetWorkSpaceFromFile() + if err != nil { + util.HandleError(err, "Unable to get local project details") + } + projectId = workspaceFile.WorkspaceId + } + + if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) { + infisicalToken = token.Token + } else { + util.RequireLogin() + util.RequireLocalWorkspaceFile() + + loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails() + if err != nil { + util.HandleError(err, "Unable to authenticate") + } + + if loggedInUserDetails.LoginExpired { + util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again") + } + infisicalToken = loggedInUserDetails.UserCredentials.JTWToken + } + + httpClient.SetAuthToken(infisicalToken) + + infisicalClient := infisicalSdk.NewInfisicalClient(context.Background(), infisicalSdk.Config{ + SiteUrl: config.INFISICAL_URL, + UserAgent: api.USER_AGENT, + AutoTokenRefresh: false, + }) + infisicalClient.Auth().SetAccessToken(infisicalToken) + + projectDetails, err := api.CallGetProjectById(httpClient, projectId) + if err != nil { + util.HandleError(err, "To fetch project details") + } + + dynamicSecretLeases, err := infisicalClient.DynamicSecrets().Leases().List(infisicalSdk.ListDynamicSecretLeasesOptions{ + DynamicSecretName: dynamicSecretRootCredentialName, + ProjectSlug: projectDetails.Slug, + SecretPath: secretsPath, + EnvironmentSlug: environmentName, + }) + + if err != nil { + util.HandleError(err, "To fetch dynamic secret leases list") + } + + visualize.PrintAllDynamicSecretLeases(dynamicSecretLeases) + Telemetry.CaptureEvent("cli-command:dynamic-secrets lease list", posthog.NewProperties().Set("lease-count", len(dynamicSecretLeases)).Set("version", util.CLI_VERSION)) +} + +func init() { + dynamicSecretLeaseCreateCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") + dynamicSecretLeaseCreateCmd.Flags().String("token", "", "Create dynamic secret leases using machine identity access token") + dynamicSecretLeaseCreateCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") + dynamicSecretLeaseCreateCmd.Flags().String("ttl", "", "The lease lifetime TTL. If not provided the default TTL of dynamic secret will be used.") + dynamicSecretLeaseCreateCmd.Flags().Bool("plain", false, "Print leased credentials without formatting, one per line") + dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseCreateCmd) + + dynamicSecretLeaseListCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") + dynamicSecretLeaseListCmd.Flags().String("token", "", "Fetch dynamic secret leases machine identity access token") + dynamicSecretLeaseListCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") + dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseListCmd) + + dynamicSecretLeaseRenewCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") + dynamicSecretLeaseRenewCmd.Flags().String("token", "", "Renew dynamic secrets machine identity access token") + dynamicSecretLeaseRenewCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") + dynamicSecretLeaseRenewCmd.Flags().String("ttl", "", "The lease lifetime TTL. If not provided the default TTL of dynamic secret will be used.") + dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseRenewCmd) + + dynamicSecretLeaseRevokeCmd.Flags().StringP("path", "p", "/", "The path from where dynamic secret should be leased from") + dynamicSecretLeaseRevokeCmd.Flags().String("token", "", "Delete dynamic secrets using machine identity access token") + dynamicSecretLeaseRevokeCmd.Flags().String("projectId", "", "Manually set the projectId to fetch leased from when using machine identity based auth") + dynamicSecretLeaseCmd.AddCommand(dynamicSecretLeaseRevokeCmd) + + dynamicSecretCmd.AddCommand(dynamicSecretLeaseCmd) + + dynamicSecretCmd.Flags().String("token", "", "Fetch secrets using service token or machine identity access token") + dynamicSecretCmd.Flags().String("projectId", "", "Manually set the projectId to fetch dynamic-secret when using machine identity based auth") + dynamicSecretCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on") + dynamicSecretCmd.Flags().String("path", "/", "get dynamic secret within a folder path") + rootCmd.AddCommand(dynamicSecretCmd) +} diff --git a/cli/packages/visualize/dynamic_secret_leases.go b/cli/packages/visualize/dynamic_secret_leases.go new file mode 100644 index 000000000..157e4e981 --- /dev/null +++ b/cli/packages/visualize/dynamic_secret_leases.go @@ -0,0 +1,38 @@ +package visualize + +import infisicalModels "github.com/infisical/go-sdk/packages/models" + +func PrintAllDyamicSecretLeaseCredentials(leaseCredentials map[string]any) { + rows := [][]string{} + for key, value := range leaseCredentials { + if cred, ok := value.(string); ok { + rows = append(rows, []string{key, cred}) + } + } + + headers := []string{"Key", "Value"} + + GenericTable(headers, rows) +} + +func PrintAllDynamicRootCredentials(dynamicRootCredentials []infisicalModels.DynamicSecret) { + rows := [][]string{} + for _, el := range dynamicRootCredentials { + rows = append(rows, []string{el.Name, el.Type, el.DefaultTTL, el.MaxTTL}) + } + + headers := []string{"Name", "Provider", "Default TTL", "Max TTL"} + + GenericTable(headers, rows) +} + +func PrintAllDynamicSecretLeases(dynamicSecretLeases []infisicalModels.DynamicSecretLease) { + rows := [][]string{} + for _, el := range dynamicSecretLeases { + rows = append(rows, []string{el.Id, el.ExpireAt.Local().Format("02-Jan-2006 03:04:05 PM"), el.CreatedAt.Local().Format("02-Jan-2006 03:04:05 PM")}) + } + + headers := []string{"ID", "Expire At", "Created At"} + + GenericTable(headers, rows) +} diff --git a/cli/packages/visualize/visualize.go b/cli/packages/visualize/visualize.go index 365cbeac4..57a3064e7 100644 --- a/cli/packages/visualize/visualize.go +++ b/cli/packages/visualize/visualize.go @@ -94,6 +94,52 @@ func getLongestValues(rows [][3]string) (longestSecretName, longestSecretType in return } +func GenericTable(headers []string, rows [][]string) { + // if we're not in a terminal or cygwin terminal, don't truncate the secret value + shouldTruncate := isatty.IsTerminal(os.Stdout.Fd()) + + // This will return an error if we're not in a terminal or + // if the terminal is a cygwin terminal like Git Bash. + width, _, err := term.GetSize(int(os.Stdout.Fd())) + if err != nil { + if shouldTruncate { + log.Error().Msgf("error getting terminal size: %s", err) + } else { + log.Debug().Err(err) + } + } + + availableWidth := width - borderWidths + if availableWidth < 0 { + availableWidth = 0 + } + + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.SetStyle(table.StyleLight) + + // t.SetTitle(tableOptions.Title) + t.Style().Options.DrawBorder = true + t.Style().Options.SeparateHeader = true + t.Style().Options.SeparateColumns = true + + tableHeaders := table.Row{} + for _, header := range headers { + tableHeaders = append(tableHeaders, header) + } + + t.AppendHeader(tableHeaders) + for _, row := range rows { + tableRow := table.Row{} + for _, val := range row { + tableRow = append(tableRow, val) + } + t.AppendRow(tableRow) + } + + t.Render() +} + // stringWidth returns the width of a string. // ANSI escape sequences are ignored and double-width characters are handled correctly. func stringWidth(str string) (width int) {