host API support for login and switch commands

This commit is contained in:
quinton11
2023-04-18 12:03:03 +00:00
parent 4c41a7f1cf
commit be2cf54d6e
6 changed files with 201 additions and 23 deletions
+53
View File
@@ -10,9 +10,11 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/url"
"regexp" "regexp"
"github.com/Infisical/infisical-merge/packages/api" "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/crypto"
"github.com/Infisical/infisical-merge/packages/models" "github.com/Infisical/infisical-merge/packages/models"
"github.com/Infisical/infisical-merge/packages/srp" "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() email, password, err := askForLoginCredentials()
if err != nil { if err != nil {
util.HandleError(err, "Unable to parse email and password for authentication") util.HandleError(err, "Unable to parse email and password for authentication")
@@ -266,6 +274,51 @@ func init() {
rootCmd.AddCommand(loginCmd) 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) { func askForLoginCredentials() (email string, password string, err error) {
validateEmail := func(input string) error { validateEmail := func(input string) error {
matched, err := regexp.MatchString("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", input) matched, err := regexp.MatchString("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", input)
+119 -10
View File
@@ -2,7 +2,10 @@ package cmd
import ( import (
"errors" "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/Infisical/infisical-merge/packages/util"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -30,25 +33,43 @@ var switchCmd = &cobra.Command{
//get previous logged in profiles //get previous logged in profiles
loggedInProfiles, err := getLoggedInUsers() loggedInProfiles, err := getLoggedInUsers()
if err != nil { 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 //prompt user
profile, err := LoggedInUsersPrompt(loggedInProfiles) profile, err := LoggedInUsersPrompt(loggedInProfiles)
if err != nil { if err != nil {
util.HandleError(err, "[infisical switch]: Prompt error") util.HandleError(err, "[infisical user switch]: Prompt error")
} }
//write to config file //write to config file
configFile, err := util.GetConfigFile() configFile, err := util.GetConfigFile()
if err != nil { 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 configFile.LoggedInUserEmail = profile
ok := util.Contains(configFile.LoggedInUsersEmail, profile)
//set logged in user domain
ok := util.ConfigContainsEmail(configFile.LoggedInUsers, profile)
if !ok { 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) 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() { func init() {
userCmd.AddCommand(domainCmd)
userCmd.AddCommand(switchCmd) userCmd.AddCommand(switchCmd)
rootCmd.AddCommand(userCmd) rootCmd.AddCommand(userCmd)
} }
@@ -74,12 +159,13 @@ func getLoggedInUsers() ([]string, error) {
//get logged in profiles //get logged in profiles
// //
if configFile.LoggedInUsersEmail == nil { if len(configFile.LoggedInUsers) > 0 {
loggedInProfiles = append(loggedInProfiles, configFile.LoggedInUserEmail) for _, v := range configFile.LoggedInUsers {
} else { loggedInProfiles = append(loggedInProfiles, v.Email)
if len(configFile.LoggedInUsersEmail) > 0 {
loggedInProfiles = append(loggedInProfiles, configFile.LoggedInUsersEmail...)
} }
} else {
loggedInProfiles = append(loggedInProfiles, configFile.LoggedInUserEmail)
} }
return loggedInProfiles, nil return loggedInProfiles, nil
} else { } 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) { func LoggedInUsersPrompt(profiles []string) (string, error) {
prompt := promptui.Select{Label: "Which of your Infisical profiles would you like to use", prompt := promptui.Select{Label: "Which of your Infisical profiles would you like to use",
Items: profiles, Items: profiles,
+7 -1
View File
@@ -13,8 +13,14 @@ type UserCredentials struct {
// The file struct for Infisical config file // The file struct for Infisical config file
type ConfigFile struct { type ConfigFile struct {
LoggedInUserEmail string `json:"loggedInUserEmail"` LoggedInUserEmail string `json:"loggedInUserEmail"`
LoggedInUserDomain string `json:"LoggedInUserDomain,omitempty"`
VaultBackendType keyring.BackendType `json:"vaultBackendType"` 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 { type SingleEnvironmentVariable struct {
+11 -10
View File
@@ -7,6 +7,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/Infisical/infisical-merge/packages/config"
"github.com/Infisical/infisical-merge/packages/models" "github.com/Infisical/infisical-merge/packages/models"
log "github.com/sirupsen/logrus" 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) 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 profiles exists
if len(existingConfigFile.LoggedInUsersEmail) > 0 { loggedInUser := models.LoggedInUser{
ok := Contains(existingConfigFile.LoggedInUsersEmail, userCredentials.Email) Email: userCredentials.Email,
Domain: config.INFISICAL_URL,
}
if len(existingConfigFile.LoggedInUsers) > 0 {
ok := ConfigContainsEmail(existingConfigFile.LoggedInUsers, userCredentials.Email)
if !ok { if !ok {
existingConfigFile.LoggedInUsersEmail = append(existingConfigFile.LoggedInUsersEmail, userCredentials.Email) existingConfigFile.LoggedInUsers = append(existingConfigFile.LoggedInUsers, loggedInUser)
} }
} else { } else {
existingConfigFile.LoggedInUsersEmail = append(existingConfigFile.LoggedInUsersEmail, userCredentials.Email) existingConfigFile.LoggedInUsers = append(existingConfigFile.LoggedInUsers, loggedInUser)
} }
configFile := models.ConfigFile{ configFile := models.ConfigFile{
LoggedInUserEmail: userCredentials.Email, LoggedInUserEmail: userCredentials.Email,
LoggedInUserDomain: config.INFISICAL_URL,
VaultBackendType: existingConfigFile.VaultBackendType, VaultBackendType: existingConfigFile.VaultBackendType,
LoggedInUsersEmail: existingConfigFile.LoggedInUsersEmail, LoggedInUsers: existingConfigFile.LoggedInUsers,
} }
configFileMarshalled, err := json.Marshal(configFile) configFileMarshalled, err := json.Marshal(configFile)
+7
View File
@@ -6,6 +6,7 @@ import (
"github.com/99designs/keyring" "github.com/99designs/keyring"
"github.com/Infisical/infisical-merge/packages/api" "github.com/Infisical/infisical-merge/packages/api"
"github.com/Infisical/infisical-merge/packages/config"
"github.com/Infisical/infisical-merge/packages/models" "github.com/Infisical/infisical-merge/packages/models"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
) )
@@ -87,6 +88,12 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) {
SetAuthToken(userCreds.JTWToken). SetAuthToken(userCreds.JTWToken).
SetHeader("Accept", "application/json") 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) isAuthenticated := api.CallIsAuthenticated(httpClient)
if !isAuthenticated { if !isAuthenticated {
return LoggedInUserDetails{ return LoggedInUserDetails{
+4 -2
View File
@@ -9,6 +9,8 @@ import (
"os/exec" "os/exec"
"path" "path"
"strings" "strings"
"github.com/Infisical/infisical-merge/packages/models"
) )
type DecodedSymmetricEncryptionDetails = struct { type DecodedSymmetricEncryptionDetails = struct {
@@ -61,9 +63,9 @@ func IsSecretTypeValid(s string) bool {
return false return false
} }
func Contains(iter []string, elem string) bool { func ConfigContainsEmail(iter []models.LoggedInUser, elem string) bool {
for _, value := range iter { for _, value := range iter {
if value == elem { if value.Email == elem {
return true return true
} }
} }