From be2cf54d6e138b8d82a5310be941f43310b9823f Mon Sep 17 00:00:00 2001 From: quinton11 Date: Tue, 18 Apr 2023 12:03:03 +0000 Subject: [PATCH] host API support for login and switch commands --- cli/packages/cmd/login.go | 53 +++++++++++++ cli/packages/cmd/user.go | 129 ++++++++++++++++++++++++++++--- cli/packages/models/cli.go | 8 +- cli/packages/util/config.go | 21 ++--- cli/packages/util/credentials.go | 7 ++ cli/packages/util/helper.go | 6 +- 6 files changed, 201 insertions(+), 23 deletions(-) diff --git a/cli/packages/cmd/login.go b/cli/packages/cmd/login.go index ebdb86cd1f..93f5bf5e19 100644 --- a/cli/packages/cmd/login.go +++ b/cli/packages/cmd/login.go @@ -10,9 +10,11 @@ import ( "errors" "fmt" + "net/url" "regexp" "github.com/Infisical/infisical-merge/packages/api" + "github.com/Infisical/infisical-merge/packages/config" "github.com/Infisical/infisical-merge/packages/crypto" "github.com/Infisical/infisical-merge/packages/models" "github.com/Infisical/infisical-merge/packages/srp" @@ -73,6 +75,12 @@ var loginCmd = &cobra.Command{ } // } + //prompt user to select domain between Infisical cloud and self hosting + err = askForDomain() + if err != nil { + util.HandleError(err, "Unable to parse domain url") + } + email, password, err := askForLoginCredentials() if err != nil { util.HandleError(err, "Unable to parse email and password for authentication") @@ -266,6 +274,51 @@ func init() { rootCmd.AddCommand(loginCmd) } +func askForDomain() error { + //query user to choose between Infisical cloud or self hosting + options := []string{"Infisical Cloud", "Self Hosting"} + optionsPrompt := promptui.Select{ + Label: "Select your hosting option", + Items: options, + Size: 2, + } + + idx, _, err := optionsPrompt.Run() + if err != nil { + return err + } + + if idx == 0 { + //cloud option + config.INFISICAL_URL = util.INFISICAL_DEFAULT_API_URL + return nil + } + + urlValidation := func(input string) error { + _, err := url.ParseRequestURI(input) + if err != nil { + return errors.New("this is an invalid url") + } + return nil + } + + //else run prompt to enter domain + domainPrompt := promptui.Prompt{ + Label: "Domain", + Validate: urlValidation, + } + + domain, err := domainPrompt.Run() + if err != nil { + return err + } + + //set api url + config.INFISICAL_URL = domain + //return nil + return nil +} + func askForLoginCredentials() (email string, password string, err error) { validateEmail := func(input string) error { matched, err := regexp.MatchString("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", input) diff --git a/cli/packages/cmd/user.go b/cli/packages/cmd/user.go index 49c2db6b56..93b32eb3fc 100644 --- a/cli/packages/cmd/user.go +++ b/cli/packages/cmd/user.go @@ -2,7 +2,10 @@ package cmd import ( "errors" + "net/url" + "github.com/Infisical/infisical-merge/packages/config" + "github.com/Infisical/infisical-merge/packages/models" "github.com/Infisical/infisical-merge/packages/util" "github.com/manifoldco/promptui" "github.com/spf13/cobra" @@ -30,25 +33,43 @@ var switchCmd = &cobra.Command{ //get previous logged in profiles loggedInProfiles, err := getLoggedInUsers() if err != nil { - util.HandleError(err, "[infisical switch]: Unable to get logged Profiles") + util.HandleError(err, "[infisical user switch]: Unable to get logged Profiles") } //prompt user profile, err := LoggedInUsersPrompt(loggedInProfiles) if err != nil { - util.HandleError(err, "[infisical switch]: Prompt error") + util.HandleError(err, "[infisical user switch]: Prompt error") } //write to config file configFile, err := util.GetConfigFile() if err != nil { - util.HandleError(err, "[infisical switch]: Unable to get config file") + util.HandleError(err, "[infisical user switch]: Unable to get config file") } configFile.LoggedInUserEmail = profile - ok := util.Contains(configFile.LoggedInUsersEmail, profile) + + //set logged in user domain + ok := util.ConfigContainsEmail(configFile.LoggedInUsers, profile) + if !ok { - configFile.LoggedInUsersEmail = append(configFile.LoggedInUsersEmail, profile) + //profile not in loggedInUsers + configFile.LoggedInUsers = append(configFile.LoggedInUsers, models.LoggedInUser{ + Email: profile, + Domain: config.INFISICAL_URL, + }) + //set logged in user domain + configFile.LoggedInUserDomain = config.INFISICAL_URL + + } else { + //exists, set logged in user domain + for _, v := range configFile.LoggedInUsers { + if profile == v.Email { + configFile.LoggedInUserDomain = v.Domain + break + } + } } err = util.WriteConfigFile(&configFile) @@ -58,7 +79,71 @@ var switchCmd = &cobra.Command{ }, } +var domainCmd = &cobra.Command{ + Use: "domain", + Short: "Used to update the domain of an Infisical profile", + DisableFlagsInUseLine: true, + Example: "infisical user domain", + Args: cobra.ExactArgs(0), + PreRun: func(cmd *cobra.Command, args []string) { + util.RequireLogin() + }, + Run: func(cmd *cobra.Command, args []string) { + //prompt for profiles selection + loggedInProfiles, err := getLoggedInUsers() + if err != nil { + util.HandleError(err, "[infisical user domain]: Unable to get logged Profiles") + } + + //prompt user + profile, err := LoggedInUsersPrompt(loggedInProfiles) + if err != nil { + util.HandleError(err, "[infisical user domain]: Prompt error") + } + + //prompt to update domain + domain, err := NewDomainPrompt() + if err != nil { + util.HandleError(err, "[infisical user domain]: Prompt error") + } + + //write to config file + configFile, err := util.GetConfigFile() + if err != nil { + util.HandleError(err, "[infisical user]: Unable to get config file") + } + + //check if profile in logged in profiles + + //if not add new profile loggedInUsers + //else update profile from loggedinUsers list + ok := util.ConfigContainsEmail(configFile.LoggedInUsers, profile) + if !ok { + configFile.LoggedInUsers = append(configFile.LoggedInUsers, models.LoggedInUser{ + Email: profile, + Domain: domain, + }) + } else { + //exists, set logged in user domain + for _, v := range configFile.LoggedInUsers { + if profile == v.Email { + v.Domain = domain + break + } + } + + } + //check if current loggedinuser is selected profile + //if yes set current domain to changed domain + if configFile.LoggedInUserEmail == profile { + configFile.LoggedInUserDomain = domain + } + + }, +} + func init() { + userCmd.AddCommand(domainCmd) userCmd.AddCommand(switchCmd) rootCmd.AddCommand(userCmd) } @@ -74,12 +159,13 @@ func getLoggedInUsers() ([]string, error) { //get logged in profiles // - if configFile.LoggedInUsersEmail == nil { - loggedInProfiles = append(loggedInProfiles, configFile.LoggedInUserEmail) - } else { - if len(configFile.LoggedInUsersEmail) > 0 { - loggedInProfiles = append(loggedInProfiles, configFile.LoggedInUsersEmail...) + if len(configFile.LoggedInUsers) > 0 { + for _, v := range configFile.LoggedInUsers { + loggedInProfiles = append(loggedInProfiles, v.Email) } + } else { + + loggedInProfiles = append(loggedInProfiles, configFile.LoggedInUserEmail) } return loggedInProfiles, nil } else { @@ -88,6 +174,29 @@ func getLoggedInUsers() ([]string, error) { } } +func NewDomainPrompt() (string, error) { + urlValidation := func(input string) error { + _, err := url.ParseRequestURI(input) + if err != nil { + return errors.New("this is an invalid url") + } + return nil + } + + //else run prompt to enter domain + domainPrompt := promptui.Prompt{ + Label: "New Domain", + Validate: urlValidation, + } + + domain, err := domainPrompt.Run() + if err != nil { + return "", err + } + + return domain, nil +} + func LoggedInUsersPrompt(profiles []string) (string, error) { prompt := promptui.Select{Label: "Which of your Infisical profiles would you like to use", Items: profiles, diff --git a/cli/packages/models/cli.go b/cli/packages/models/cli.go index ce5838f434..b9b0ab7b59 100644 --- a/cli/packages/models/cli.go +++ b/cli/packages/models/cli.go @@ -13,8 +13,14 @@ type UserCredentials struct { // The file struct for Infisical config file type ConfigFile struct { LoggedInUserEmail string `json:"loggedInUserEmail"` + LoggedInUserDomain string `json:"LoggedInUserDomain,omitempty"` VaultBackendType keyring.BackendType `json:"vaultBackendType"` - LoggedInUsersEmail []string `json:"loggedInUsersEmail,omitempty"` + LoggedInUsers []LoggedInUser `json:"loggedInUsers,omitempty"` +} + +type LoggedInUser struct { + Email string `json:"email"` + Domain string `json:"domain"` } type SingleEnvironmentVariable struct { diff --git a/cli/packages/util/config.go b/cli/packages/util/config.go index c1b57b67d4..ef039443cf 100644 --- a/cli/packages/util/config.go +++ b/cli/packages/util/config.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" + "github.com/Infisical/infisical-merge/packages/config" "github.com/Infisical/infisical-merge/packages/models" log "github.com/sirupsen/logrus" ) @@ -31,25 +32,25 @@ func WriteInitalConfig(userCredentials *models.UserCredentials) error { return fmt.Errorf("writeInitalConfig: unable to write config file because [err=%s]", err) } - //if empty, initialize - if existingConfigFile.LoggedInUsersEmail == nil { - existingConfigFile.LoggedInUsersEmail = []string{} - } - //if profiles exists - if len(existingConfigFile.LoggedInUsersEmail) > 0 { - ok := Contains(existingConfigFile.LoggedInUsersEmail, userCredentials.Email) + loggedInUser := models.LoggedInUser{ + Email: userCredentials.Email, + Domain: config.INFISICAL_URL, + } + if len(existingConfigFile.LoggedInUsers) > 0 { + ok := ConfigContainsEmail(existingConfigFile.LoggedInUsers, userCredentials.Email) if !ok { - existingConfigFile.LoggedInUsersEmail = append(existingConfigFile.LoggedInUsersEmail, userCredentials.Email) + existingConfigFile.LoggedInUsers = append(existingConfigFile.LoggedInUsers, loggedInUser) } } else { - existingConfigFile.LoggedInUsersEmail = append(existingConfigFile.LoggedInUsersEmail, userCredentials.Email) + existingConfigFile.LoggedInUsers = append(existingConfigFile.LoggedInUsers, loggedInUser) } configFile := models.ConfigFile{ LoggedInUserEmail: userCredentials.Email, + LoggedInUserDomain: config.INFISICAL_URL, VaultBackendType: existingConfigFile.VaultBackendType, - LoggedInUsersEmail: existingConfigFile.LoggedInUsersEmail, + LoggedInUsers: existingConfigFile.LoggedInUsers, } configFileMarshalled, err := json.Marshal(configFile) diff --git a/cli/packages/util/credentials.go b/cli/packages/util/credentials.go index 1766f04c77..b06ed4ef62 100644 --- a/cli/packages/util/credentials.go +++ b/cli/packages/util/credentials.go @@ -6,6 +6,7 @@ import ( "github.com/99designs/keyring" "github.com/Infisical/infisical-merge/packages/api" + "github.com/Infisical/infisical-merge/packages/config" "github.com/Infisical/infisical-merge/packages/models" "github.com/go-resty/resty/v2" ) @@ -87,6 +88,12 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) { SetAuthToken(userCreds.JTWToken). SetHeader("Accept", "application/json") + //configFile.LoggedInUserDomain + //if not empty set as infisical url + if configFile.LoggedInUserDomain != "" { + config.INFISICAL_URL = configFile.LoggedInUserDomain + } + isAuthenticated := api.CallIsAuthenticated(httpClient) if !isAuthenticated { return LoggedInUserDetails{ diff --git a/cli/packages/util/helper.go b/cli/packages/util/helper.go index 336b368df8..46928fbc43 100644 --- a/cli/packages/util/helper.go +++ b/cli/packages/util/helper.go @@ -9,6 +9,8 @@ import ( "os/exec" "path" "strings" + + "github.com/Infisical/infisical-merge/packages/models" ) type DecodedSymmetricEncryptionDetails = struct { @@ -61,9 +63,9 @@ func IsSecretTypeValid(s string) bool { return false } -func Contains(iter []string, elem string) bool { +func ConfigContainsEmail(iter []models.LoggedInUser, elem string) bool { for _, value := range iter { - if value == elem { + if value.Email == elem { return true } }