cscli machines add: don't overwrite existing credential file (#2625)

* cscli machines add: don't overwrite existing credential file
* keep old behavior with --force
Now --force is used both to override the replacement of and existing machine,
and an existing credentials file. To retain the old behavior, the
existence of the file is only checked for the default configuration, not
if explicitly specified.
This commit is contained in:
mmetc 2023-12-04 22:59:52 +01:00 committed by GitHub
parent f8755be9cd
commit a5ab73d458
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 41 additions and 25 deletions

View file

@ -110,7 +110,7 @@ func NewCapiRegisterCmd() *cobra.Command {
if err != nil { if err != nil {
return fmt.Errorf("write api credentials in '%s' failed: %w", dumpFile, err) return fmt.Errorf("write api credentials in '%s' failed: %w", dumpFile, err)
} }
log.Printf("Central API credentials dumped to '%s'", dumpFile) log.Printf("Central API credentials written to '%s'", dumpFile)
} else { } else {
fmt.Printf("%s\n", string(apiConfigDump)) fmt.Printf("%s\n", string(apiConfigDump))
} }

View file

@ -183,7 +183,7 @@ func restoreConfigFromDirectory(dirPath string, oldBackup bool) error {
if csConfig.API.Server.OnlineClient != nil && csConfig.API.Server.OnlineClient.CredentialsFilePath != "" { if csConfig.API.Server.OnlineClient != nil && csConfig.API.Server.OnlineClient.CredentialsFilePath != "" {
apiConfigDumpFile = csConfig.API.Server.OnlineClient.CredentialsFilePath apiConfigDumpFile = csConfig.API.Server.OnlineClient.CredentialsFilePath
} }
err = os.WriteFile(apiConfigDumpFile, apiConfigDump, 0o644) err = os.WriteFile(apiConfigDumpFile, apiConfigDump, 0o600)
if err != nil { if err != nil {
return fmt.Errorf("write api credentials in '%s' failed: %s", apiConfigDumpFile, err) return fmt.Errorf("write api credentials in '%s' failed: %s", apiConfigDumpFile, err)
} }

View file

@ -150,11 +150,11 @@ func runLapiRegister(cmd *cobra.Command, args []string) error {
log.Fatalf("unable to marshal api credentials: %s", err) log.Fatalf("unable to marshal api credentials: %s", err)
} }
if dumpFile != "" { if dumpFile != "" {
err = os.WriteFile(dumpFile, apiConfigDump, 0644) err = os.WriteFile(dumpFile, apiConfigDump, 0o600)
if err != nil { if err != nil {
log.Fatalf("write api credentials in '%s' failed: %s", dumpFile, err) log.Fatalf("write api credentials in '%s' failed: %s", dumpFile, err)
} }
log.Printf("Local API credentials dumped to '%s'", dumpFile) log.Printf("Local API credentials written to '%s'", dumpFile)
} else { } else {
fmt.Printf("%s\n", string(apiConfigDump)) fmt.Printf("%s\n", string(apiConfigDump))
} }

View file

@ -43,6 +43,7 @@ func generatePassword(length int) string {
charsetLength := len(charset) charsetLength := len(charset)
buf := make([]byte, length) buf := make([]byte, length)
for i := 0; i < length; i++ { for i := 0; i < length; i++ {
rInt, err := saferand.Int(saferand.Reader, big.NewInt(int64(charsetLength))) rInt, err := saferand.Int(saferand.Reader, big.NewInt(int64(charsetLength)))
if err != nil { if err != nil {
@ -190,7 +191,6 @@ cscli machines add MyTestMachine --password MyPassword
} }
func runMachinesAdd(cmd *cobra.Command, args []string) error { func runMachinesAdd(cmd *cobra.Command, args []string) error {
var dumpFile string
var err error var err error
flags := cmd.Flags() flags := cmd.Flags()
@ -200,7 +200,7 @@ func runMachinesAdd(cmd *cobra.Command, args []string) error {
return err return err
} }
outputFile, err := flags.GetString("file") dumpFile, err := flags.GetString("file")
if err != nil { if err != nil {
return err return err
} }
@ -220,7 +220,7 @@ func runMachinesAdd(cmd *cobra.Command, args []string) error {
return err return err
} }
forceAdd, err := flags.GetBool("force") force, err := flags.GetBool("force")
if err != nil { if err != nil {
return err return err
} }
@ -242,17 +242,28 @@ func runMachinesAdd(cmd *cobra.Command, args []string) error {
} }
/*check if file already exists*/ /*check if file already exists*/
if outputFile != "" { if dumpFile == "" && csConfig.API.Client != nil && csConfig.API.Client.CredentialsFilePath != "" {
dumpFile = outputFile credFile := csConfig.API.Client.CredentialsFilePath
} else if csConfig.API.Client != nil && csConfig.API.Client.CredentialsFilePath != "" { // use the default only if the file does not exist
dumpFile = csConfig.API.Client.CredentialsFilePath _, err := os.Stat(credFile)
switch {
case os.IsNotExist(err) || force:
dumpFile = csConfig.API.Client.CredentialsFilePath
case err != nil:
return fmt.Errorf("unable to stat '%s': %s", credFile, err)
default:
return fmt.Errorf(`credentials file '%s' already exists: please remove it, use "--force" or specify a different file with "-f" ("-f -" for standard output)`, credFile)
}
}
if dumpFile == "" {
return fmt.Errorf(`please specify a file to dump credentials to, with -f ("-f -" for standard output)`)
} }
// create a password if it's not specified by user // create a password if it's not specified by user
if machinePassword == "" && !interactive { if machinePassword == "" && !interactive {
if !autoAdd { if !autoAdd {
printHelp(cmd) return fmt.Errorf("please specify a password with --password or use --auto")
return nil
} }
machinePassword = generatePassword(passwordLength) machinePassword = generatePassword(passwordLength)
} else if machinePassword == "" && interactive { } else if machinePassword == "" && interactive {
@ -262,7 +273,7 @@ func runMachinesAdd(cmd *cobra.Command, args []string) error {
survey.AskOne(qs, &machinePassword) survey.AskOne(qs, &machinePassword)
} }
password := strfmt.Password(machinePassword) password := strfmt.Password(machinePassword)
_, err = dbClient.CreateMachine(&machineID, &password, "", true, forceAdd, types.PasswordAuthType) _, err = dbClient.CreateMachine(&machineID, &password, "", true, force, types.PasswordAuthType)
if err != nil { if err != nil {
return fmt.Errorf("unable to create machine: %s", err) return fmt.Errorf("unable to create machine: %s", err)
} }
@ -291,7 +302,7 @@ func runMachinesAdd(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return fmt.Errorf("write api credentials in '%s' failed: %s", dumpFile, err) return fmt.Errorf("write api credentials in '%s' failed: %s", dumpFile, err)
} }
log.Printf("API credentials dumped to '%s'", dumpFile) log.Printf("API credentials written to '%s'", dumpFile)
} else { } else {
fmt.Printf("%s\n", string(apiConfigDump)) fmt.Printf("%s\n", string(apiConfigDump))
} }

View file

@ -23,20 +23,24 @@ teardown() {
#---------- #----------
@test "can list machines as regular user" {
rune -0 cscli machines list
}
@test "we have exactly one machine" { @test "we have exactly one machine" {
rune -0 cscli machines list -o json rune -0 cscli machines list -o json
rune -0 jq -c '[. | length, .[0].machineId[0:32], .[0].isValidated]' <(output) rune -0 jq -c '[. | length, .[0].machineId[0:32], .[0].isValidated]' <(output)
assert_output '[1,"githubciXXXXXXXXXXXXXXXXXXXXXXXX",true]' assert_output '[1,"githubciXXXXXXXXXXXXXXXXXXXXXXXX",true]'
} }
@test "don't overwrite local credentials by default" {
rune -1 cscli machines add local -a -o json
rune -0 jq -r '.msg' <(stderr)
assert_output --partial 'already exists: please remove it, use "--force" or specify a different file with "-f"'
rune -0 cscli machines add local -a --force
assert_stderr --partial "Machine 'local' successfully added to the local API"
}
@test "add a new machine and delete it" { @test "add a new machine and delete it" {
rune -0 cscli machines add -a -f /dev/null CiTestMachine -o human rune -0 cscli machines add -a -f /dev/null CiTestMachine -o human
assert_stderr --partial "Machine 'CiTestMachine' successfully added to the local API" assert_stderr --partial "Machine 'CiTestMachine' successfully added to the local API"
assert_stderr --partial "API credentials dumped to '/dev/null'" assert_stderr --partial "API credentials written to '/dev/null'"
# we now have two machines # we now have two machines
rune -0 cscli machines list -o json rune -0 cscli machines list -o json
@ -56,7 +60,7 @@ teardown() {
@test "register, validate and then remove a machine" { @test "register, validate and then remove a machine" {
rune -0 cscli lapi register --machine CiTestMachineRegister -f /dev/null -o human rune -0 cscli lapi register --machine CiTestMachineRegister -f /dev/null -o human
assert_stderr --partial "Successfully registered to Local API (LAPI)" assert_stderr --partial "Successfully registered to Local API (LAPI)"
assert_stderr --partial "Local API credentials dumped to '/dev/null'" assert_stderr --partial "Local API credentials written to '/dev/null'"
# the machine is not validated yet # the machine is not validated yet
rune -0 cscli machines list -o json rune -0 cscli machines list -o json

View file

@ -61,12 +61,12 @@ make_init_data() {
./instance-db config-yaml ./instance-db config-yaml
./instance-db setup ./instance-db setup
./bin/preload-hub-items
# when installed packages are always using sqlite, so no need to regenerate # when installed packages are always using sqlite, so no need to regenerate
# local credz for sqlite # local credz for sqlite
./bin/preload-hub-items [[ "${DB_BACKEND}" == "sqlite" ]] || ${CSCLI} machines add githubciXXXXXXXXXXXXXXXXXXXXXXXX --auto --force
[[ "${DB_BACKEND}" == "sqlite" ]] || ${CSCLI} machines add --auto
mkdir -p "$LOCAL_INIT_DIR" mkdir -p "$LOCAL_INIT_DIR"

View file

@ -115,11 +115,12 @@ make_init_data() {
./instance-db config-yaml ./instance-db config-yaml
./instance-db setup ./instance-db setup
"$CSCLI" --warning machines add githubciXXXXXXXXXXXXXXXXXXXXXXXX --auto
"$CSCLI" --warning hub update "$CSCLI" --warning hub update
./bin/preload-hub-items ./bin/preload-hub-items
"$CSCLI" --warning machines add githubciXXXXXXXXXXXXXXXXXXXXXXXX --auto --force
mkdir -p "$LOCAL_INIT_DIR" mkdir -p "$LOCAL_INIT_DIR"
./instance-db dump "${LOCAL_INIT_DIR}/database" ./instance-db dump "${LOCAL_INIT_DIR}/database"