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 38d7d1abdb..82d1604eb6 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 36277ac374..b55a49c63b 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 733a7b93de..537a146cb8 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 35767cd3f1..3d050cd973 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 bc63218729..73a04200da 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"` @@ -163,6 +167,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_secrets.go b/cli/packages/cmd/dynamic_secrets.go new file mode 100644 index 0000000000..8c699386d3 --- /dev/null +++ b/cli/packages/cmd/dynamic_secrets.go @@ -0,0 +1,571 @@ +/* +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("Lease ID: %s\n", leaseDetails.Id) + 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 renew "`, + 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 0000000000..dbb5886246 --- /dev/null +++ b/cli/packages/visualize/dynamic_secret_leases.go @@ -0,0 +1,39 @@ +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{} + const timeformat = "02-Jan-2006 03:04:05 PM" + for _, el := range dynamicSecretLeases { + rows = append(rows, []string{el.Id, el.ExpireAt.Local().Format(timeformat), el.CreatedAt.Local().Format(timeformat)}) + } + + 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 365cbeac41..7fbd24fb8b 100644 --- a/cli/packages/visualize/visualize.go +++ b/cli/packages/visualize/visualize.go @@ -94,6 +94,33 @@ func getLongestValues(rows [][3]string) (longestSecretName, longestSecretType in return } +func GenericTable(headers []string, rows [][]string) { + 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) { diff --git a/docs/cli/commands/dynamic-secrets.mdx b/docs/cli/commands/dynamic-secrets.mdx new file mode 100644 index 0000000000..c345c3e2d2 --- /dev/null +++ b/docs/cli/commands/dynamic-secrets.mdx @@ -0,0 +1,295 @@ +--- +title: "infisical dynamic-secrets" +description: "Perform dynamic secret operations directly with the CLI" +--- + +``` +infisical dynamic-secrets +``` + +## Description + +Dynamic secrets are unique secrets generated on demand based on the provided configuration settings. For more details, refer to [dynamics secrets section](/documentation/platform/dynamic-secrets/overview). + +This command enables you to perform list, lease, renew lease, and revoke lease operations on dynamic secrets within your Infisical project. + +### Sub-commands + + + Use this command to print out all of the dynamic secrets in your project. + +```bash +$ infisical dynamic-secrets +``` + +### Environment variables + + + Used to fetch dynamic secrets via a [machine identity](/documentation/platform/identities/machine-identities) instead of logged-in credentials. Simply, export this variable in the terminal before running this command. + +```bash +# Example +export INFISICAL_TOKEN=$(infisical login --method=universal-auth --client-id= --client-secret= --silent --plain) # --plain flag will output only the token, so it can be fed to an environment variable. --silent will disable any update messages. +``` + + + + + Used to disable the check for new CLI versions. This can improve the time it takes to run this command. Recommended for production environments. + +To use, simply export this variable in the terminal before running this command. + +```bash +# Example +export INFISICAL_DISABLE_UPDATE_CHECK=true +``` + + + +### Flags + + + The project ID to fetch dynamic secrets from. This is required when using a machine identity to authenticate. + +```bash +# Example +infisical dynamic-secrets --projectId= +``` + + + + + The authenticated token to fetch dynamic secrets from. This is required when using a machine identity to authenticate. + +```bash +# Example +infisical dynamic-secrets --token= +``` + + + + + Used to select the environment name on which actions should be taken. Default + value: `dev` + + + + Use to select the project folder on which dynamic secrets will be accessed. + +```bash +# Example +infisical dynamic-secrets --path="/" --env=dev +``` + + + + + This command is used to create a new lease for a dynamic secret. + +```bash +$ infisical dynamic-secrets lease create +``` + +### Flags + + + Used to select the environment name on which actions should be taken. Default + value: `dev` + + + + The `--plain` flag will output dynamic secret lease credentials values without formatting, one per line. + Default value: `false` + +```bash +# Example +infisical dynamic-secrets lease create dynamic-secret-postgres --plain +``` + + + + + The `--path` flag indicates which project folder dynamic secrets will be injected from. + +```bash +# Example +infisical dynamic-secrets lease create --path="/" --env=dev +``` + + + + + The project ID of the dynamic secrets to lease from. This is required when using a machine identity to authenticate. + +```bash +# Example +infisical dynamic-secrets lease create --projectId= +``` + + + + + The authenticated token to create dynamic secret leases. This is required when using a machine identity to authenticate. + +```bash +# Example +infisical dynamic-secrets lease create --token= +``` + + + + + The lease lifetime. If not provided, the default TTL of the dynamic secret root credential will be used. + +```bash +# Example +infisical dynamic-secrets lease create --ttl= +``` + + + + + + This command is used to list leases for a dynamic secret. + +```bash +$ infisical dynamic-secrets lease list +``` + +### Flags + + + Used to select the environment name on which actions should be taken. Default + value: `dev` + + + + The `--path` flag indicates which project folder dynamic secrets will be injected from. + +```bash +# Example +infisical dynamic-secrets lease list --path="/" --env=dev +``` + + + + + The project ID of the dynamic secrets to list leases from. This is required when using a machine identity to authenticate. + +```bash +# Example +infisical dynamic-secrets lease list --projectId= +``` + + + + + The authenticated token to list dynamic secret leases. This is required when using a machine identity to authenticate. + +```bash +# Example +infisical dynamic-secrets lease list --token= +``` + + + + + + This command is used to renew a lease before it expires. + +```bash +$ infisical dynamic-secrets lease renew +``` + +### Flags + + + Used to select the environment name on which actions should be taken. Default + value: `dev` + + + + The `--path` flag indicates which project folder dynamic secrets will be renewed from. + +```bash +# Example +infisical dynamic-secrets lease renew --path="/" --env=dev +``` + + + + + The project ID of the dynamic secret's lease from. This is required when using a machine identity to authenticate. + +```bash +# Example +infisical dynamic-secrets lease renew --projectId= +``` + + + + + The authenticated token to create dynamic secret leases. This is required when using a machine identity to authenticate. + +```bash +# Example +infisical dynamic-secrets lease renew --token= +``` + + + + + The lease lifetime. If not provided, the default TTL of the dynamic secret root credential will be used. + +```bash +# Example +infisical dynamic-secrets lease renew --ttl= +``` + + + + + + This command is used to delete a lease. + +```bash +$ infisical dynamic-secrets lease delete +``` + +### Flags + + + Used to select the environment name on which actions should be taken. Default + value: `dev` + + + + The `--path` flag indicates which project folder dynamic secrets will be deleted from. + +```bash +# Example +infisical dynamic-secrets lease delete --path="/" --env=dev +``` + + + + + The project ID of the dynamic secret's lease from. This is required when using a machine identity to authenticate. + +```bash +# Example +infisical dynamic-secrets lease delete --projectId= +``` + + + + + The authenticated token to delete dynamic secret leases. This is required when using a machine identity to authenticate. + +```bash +# Example +infisical dynamic-secrets lease delete --token= +``` + + + diff --git a/docs/mint.json b/docs/mint.json index 19f957847e..59aa590541 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -316,6 +316,7 @@ "cli/commands/init", "cli/commands/run", "cli/commands/secrets", + "cli/commands/dynamic-secrets", "cli/commands/export", "cli/commands/token", "cli/commands/service-token",