github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbpagesconfig/editor.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  	"errors"
     9  	"fmt"
    10  	"os"
    11  	"strings"
    12  
    13  	"github.com/keybase/client/go/kbfs/libpages/config"
    14  	"github.com/keybase/client/go/minterm"
    15  )
    16  
    17  func newKBPConfigEditorWithPrompter(kbpConfigDir string, p prompter) (
    18  	*kbpConfigEditor, error) {
    19  	kbpConfigPath, err := kbpConfigPath(kbpConfigDir)
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  	editor := &kbpConfigEditor{kbpConfigPath: kbpConfigPath, prompter: p}
    24  	f, err := os.Open(kbpConfigPath)
    25  	switch {
    26  	case err == nil:
    27  		var cfg config.Config
    28  		cfg, editor.originalConfigStr, err = readConfigAndClose(f)
    29  		if err != nil {
    30  			return nil, fmt.Errorf(
    31  				"reading config file %s error: %v", kbpConfigPath, err)
    32  		}
    33  		switch cfg.Version() {
    34  		case config.Version1:
    35  			editor.kbpConfig = cfg.(*config.V1)
    36  			needsUpgrade, err := editor.kbpConfig.HasBcryptPasswords()
    37  			if err != nil {
    38  				return nil, err
    39  			}
    40  			if needsUpgrade {
    41  				return nil, errors.New(
    42  					"config has bcrypt password hashes. Please run " +
    43  						"`kbpagesconfig upgrade` to migrate to sha256")
    44  			}
    45  		default:
    46  			return nil, fmt.Errorf(
    47  				"unsupported config version %s", cfg.Version())
    48  		}
    49  	case os.IsNotExist(err):
    50  		editor.kbpConfig = config.DefaultV1()
    51  	default:
    52  		return nil, fmt.Errorf(
    53  			"open file %s error: %v", kbpConfigPath, err)
    54  	}
    55  	return editor, nil
    56  }
    57  
    58  func newKBPConfigEditor(kbpConfigDir string) (*kbpConfigEditor, error) {
    59  	term, err := minterm.New()
    60  	if err != nil {
    61  		return nil, fmt.Errorf("opening terminal error: %s", err)
    62  	}
    63  	return newKBPConfigEditorWithPrompter(kbpConfigDir, term)
    64  }
    65  
    66  func (e *kbpConfigEditor) confirmAndWrite() error {
    67  	if err := e.kbpConfig.Validate(); err != nil {
    68  		return fmt.Errorf("new config would not be valid: %v", err)
    69  	}
    70  	return confirmAndWrite(
    71  		e.originalConfigStr, e.kbpConfig, e.kbpConfigPath, e.prompter)
    72  }
    73  
    74  func (e *kbpConfigEditor) setUser(username string, isAdd bool) error {
    75  	_, userExists := e.kbpConfig.Users[username]
    76  	if userExists && isAdd {
    77  		return fmt.Errorf("user %s already exists", username)
    78  	}
    79  	if !userExists && !isAdd {
    80  		return fmt.Errorf("user %s doesn't exist", username)
    81  	}
    82  	confirmedRandom, err := promptConfirm(e.prompter, fmt.Sprintf(
    83  		"We can generate a random password for %s, or you can enter "+
    84  			"a password. Since we use a fast hash function for password "+
    85  			"hashing, we recommend generating random passwords with enough "+
    86  			"entropy. Would you like to generate a random password now "+
    87  			"(recommended)?", username), true)
    88  	if err != nil {
    89  		return fmt.Errorf("getting confirmation error: %v", err)
    90  	}
    91  	var password string
    92  	if confirmedRandom {
    93  		password, err = generateRandomPassword()
    94  		if err != nil {
    95  			return fmt.Errorf("generating random password error: %v", err)
    96  		}
    97  		confirmed, err := promptConfirm(e.prompter, fmt.Sprintf(
    98  			"Here's the password for %s:\n\n\t%s\n\n"+
    99  				"This is the only time you'll see it, so please write it "+
   100  				"down or give it to %s. Continue?",
   101  			username, password, username), false)
   102  		if err != nil {
   103  			return fmt.Errorf("getting confirmation error: %v", err)
   104  		}
   105  		if !confirmed {
   106  			return errors.New("not confirmed")
   107  		}
   108  	} else {
   109  		input, err := e.prompter.PromptPassword(fmt.Sprintf(
   110  			"enter a password for %s: ", username))
   111  		if err != nil {
   112  			return fmt.Errorf("getting password error: %v", err)
   113  		}
   114  		password = strings.TrimSpace(input)
   115  		if len(password) == 0 {
   116  			return fmt.Errorf("empty password")
   117  		}
   118  	}
   119  	hashed, err := config.GenerateSHA256PasswordHash(password)
   120  	if err != nil {
   121  		return err
   122  	}
   123  	if e.kbpConfig.Users == nil {
   124  		e.kbpConfig.Users = make(map[string]string)
   125  	}
   126  	e.kbpConfig.Users[username] = (hashed)
   127  	return nil
   128  }
   129  
   130  func (e *kbpConfigEditor) removeUser(username string) {
   131  	delete(e.kbpConfig.Users, username)
   132  
   133  }
   134  
   135  func (e *kbpConfigEditor) setFieldSimple(pathStr string, setter func(c *config.PerPathConfigV1)) error {
   136  	if e.kbpConfig.PerPathConfigs == nil {
   137  		e.kbpConfig.PerPathConfigs = make(map[string]config.PerPathConfigV1)
   138  	}
   139  	pathPerPathConfig := e.kbpConfig.PerPathConfigs[pathStr]
   140  	setter(&pathPerPathConfig)
   141  	e.kbpConfig.PerPathConfigs[pathStr] = pathPerPathConfig
   142  	return e.kbpConfig.Validate()
   143  }
   144  
   145  func (e *kbpConfigEditor) setAnonymousPermission(
   146  	permsStr string, pathStr string) error {
   147  	return e.setFieldSimple(pathStr, func(c *config.PerPathConfigV1) {
   148  		c.AnonymousPermissions = permsStr
   149  	})
   150  }
   151  
   152  func (e *kbpConfigEditor) clearPerPathConfig(pathStr string) {
   153  	delete(e.kbpConfig.PerPathConfigs, pathStr)
   154  }
   155  
   156  func (e *kbpConfigEditor) setAdditionalPermission(
   157  	username string, permsStr string, pathStr string) error {
   158  	if e.kbpConfig.PerPathConfigs == nil {
   159  		e.kbpConfig.PerPathConfigs = make(map[string]config.PerPathConfigV1)
   160  	}
   161  	pathPerPathConfig := e.kbpConfig.PerPathConfigs[pathStr]
   162  	if pathPerPathConfig.WhitelistAdditionalPermissions == nil {
   163  		// If permsStr is empty, we'd leave an empty permission entry behind.
   164  		// But that's OK since it doesn't change any behavior, i.e., no
   165  		// additional permission is granted for the user on the path. If user
   166  		// really wants the entry gone, they can use the "remove" command.
   167  		pathPerPathConfig.WhitelistAdditionalPermissions = make(map[string]string)
   168  	}
   169  	pathPerPathConfig.WhitelistAdditionalPermissions[username] = permsStr
   170  	e.kbpConfig.PerPathConfigs[pathStr] = pathPerPathConfig
   171  	return e.kbpConfig.Validate()
   172  }
   173  
   174  func (e *kbpConfigEditor) removeUserPermissionsFromPerPathConfig(
   175  	username string, pathStr string) {
   176  	if e.kbpConfig.PerPathConfigs == nil {
   177  		return
   178  	}
   179  	if e.kbpConfig.PerPathConfigs[pathStr].WhitelistAdditionalPermissions == nil {
   180  		return
   181  	}
   182  	delete(e.kbpConfig.PerPathConfigs[pathStr].WhitelistAdditionalPermissions, username)
   183  }
   184  
   185  func (e *kbpConfigEditor) getUserPermissionsOnPath(
   186  	username string, pathStr string) (read, list bool, err error) {
   187  	read, list, _, _, _, err = e.kbpConfig.GetPermissions(
   188  		pathStr, &username)
   189  	return read, list, err
   190  }
   191  
   192  func (e *kbpConfigEditor) setAccessControlAllowOrigin(
   193  	pathStr string, acao string) error {
   194  	return e.setFieldSimple(pathStr, func(c *config.PerPathConfigV1) {
   195  		c.AccessControlAllowOrigin = acao
   196  	})
   197  }
   198  
   199  func (e *kbpConfigEditor) set403(
   200  	pathStr string, p string) error {
   201  	return e.setFieldSimple(pathStr, func(c *config.PerPathConfigV1) {
   202  		c.Custom403Forbidden = p
   203  	})
   204  }
   205  
   206  func (e *kbpConfigEditor) set404(
   207  	pathStr string, p string) error {
   208  	return e.setFieldSimple(pathStr, func(c *config.PerPathConfigV1) {
   209  		c.Custom404NotFound = p
   210  	})
   211  }