github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/kbpagesconfig/cmd_upgrade.go (about)

     1  // Copyright 2018 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"os"
    11  	"strings"
    12  
    13  	"github.com/keybase/client/go/kbfs/libpages/config"
    14  	"github.com/keybase/client/go/minterm"
    15  	"github.com/urfave/cli"
    16  )
    17  
    18  func migrateUserToSHA256Hash(p prompter, username string, oldConfig config.Config) (sha256Hash string, err error) {
    19  	for { // loop until user ctrl-C the command
    20  		input, err := p.PromptPassword(fmt.Sprintf(
    21  			"enter the password for %s: ", username))
    22  		if err != nil {
    23  			fmt.Fprintf(os.Stderr, "getting password error: %v\n", err)
    24  			continue
    25  		}
    26  		password := strings.TrimSpace(input)
    27  		if len(password) == 0 {
    28  			fmt.Fprintf(os.Stderr, "empty password\n")
    29  			continue
    30  		}
    31  		if oldConfig.Authenticate(context.Background(), username, password) {
    32  			return config.GenerateSHA256PasswordHash(password)
    33  		}
    34  		fmt.Fprintf(os.Stderr, "password for %s doesn't match what's in the old config file\n", username)
    35  		confirmed, err := promptConfirm(p, fmt.Sprintf(
    36  			"The password you entered for %s doesn't match what's "+
    37  				"currently in the old config file. Still use it?\n",
    38  			username), false)
    39  		if err != nil {
    40  			fmt.Fprintf(os.Stderr, "getting confirmation error: %v\n", err)
    41  			continue
    42  		}
    43  		if confirmed {
    44  			return config.GenerateSHA256PasswordHash(password)
    45  		}
    46  	}
    47  }
    48  
    49  func upgradeToSHA256WithPrompter(kbpConfigDir string, prompter prompter) (err error) {
    50  	kbpConfigPath, err := kbpConfigPath(kbpConfigDir)
    51  	if err != nil {
    52  		return err
    53  	}
    54  	f, err := os.Open(kbpConfigPath)
    55  	switch {
    56  	case err == nil:
    57  	case os.IsNotExist(err):
    58  		return fmt.Errorf("no kbpages config file exists in %s", kbpConfigDir)
    59  	default:
    60  		return fmt.Errorf("open file %s error: %v", kbpConfigPath, err)
    61  	}
    62  
    63  	cfg, originalConfigStr, err := readConfigAndClose(f)
    64  	if err != nil {
    65  		return fmt.Errorf(
    66  			"reading config file %s error: %v", kbpConfigPath, err)
    67  	}
    68  	if cfg.Version() != config.Version1 {
    69  		return fmt.Errorf(
    70  			"unsupported config version %s", cfg.Version())
    71  	}
    72  
    73  	oldConfig := cfg.(*config.V1)
    74  	needsUpgrade, err := oldConfig.HasBcryptPasswords()
    75  	if err != nil {
    76  		return err
    77  	}
    78  	if !needsUpgrade {
    79  		fmt.Printf("Config file %s is already the latest version (%s).\n",
    80  			kbpConfigPath, cfg.Version())
    81  		return nil
    82  	}
    83  
    84  	confirmed, err := promptConfirm(prompter,
    85  		"You are about to migrate some password hashes in your "+
    86  			"kbpages config file from bcrypt to sha256. You will be "+
    87  			"prompted to enter passwords for each user one by one. "+
    88  			"If you don't know the password for any user(s), you may "+
    89  			"enter new passwords for them. Continue?", true)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	if !confirmed {
    94  		return fmt.Errorf("not confirmed")
    95  	}
    96  
    97  	newConfig := config.DefaultV1()
    98  	for p, acl := range oldConfig.ACLs {
    99  		if newConfig.ACLs == nil {
   100  			newConfig.ACLs = make(map[string]config.PerPathConfigV1)
   101  		}
   102  		// shadow copy since oldConfig is one-time use anyway
   103  		newConfig.ACLs[p] = acl
   104  	}
   105  	for p, perPathConfig := range oldConfig.PerPathConfigs {
   106  		if newConfig.PerPathConfigs == nil {
   107  			newConfig.PerPathConfigs = make(map[string]config.PerPathConfigV1)
   108  		}
   109  		// shadow copy since oldConfig is one-time use anyway
   110  		newConfig.PerPathConfigs[p] = perPathConfig
   111  	}
   112  	for user := range oldConfig.Users {
   113  		if newConfig.Users == nil {
   114  			newConfig.Users = make(map[string]string)
   115  		}
   116  		newConfig.Users[user], err = migrateUserToSHA256Hash(
   117  			prompter, user, oldConfig)
   118  		if err != nil {
   119  			return fmt.Errorf("migrating to sha256 error: %v", err)
   120  		}
   121  	}
   122  	return confirmAndWrite(originalConfigStr, newConfig,
   123  		kbpConfigPath, prompter)
   124  }
   125  
   126  func upgradeToSHA256(c *cli.Context) {
   127  	term, err := minterm.New()
   128  	if err != nil {
   129  		fmt.Fprintf(os.Stderr, "opening terminal error: %s\n", err)
   130  		os.Exit(1)
   131  	}
   132  	if err = upgradeToSHA256WithPrompter(c.GlobalString("dir"), term); err != nil {
   133  		fmt.Fprintf(os.Stderr, "upgrading to SHA256 error: %s\n", err)
   134  		os.Exit(1)
   135  	}
   136  }
   137  
   138  var upgradeCmd = cli.Command{
   139  	Name:      "upgrade",
   140  	Usage:     "upgrade config file to the latest version",
   141  	UsageText: "upgrade",
   142  	Action:    upgradeToSHA256,
   143  }