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 }