github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/core/auth/credentials.go (about) 1 /* 2 * Copyright (C) 2019 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package auth 19 20 import ( 21 "bufio" 22 "errors" 23 "fmt" 24 "os" 25 "path/filepath" 26 27 "github.com/mysteriumnetwork/node/config" 28 "github.com/rs/zerolog/log" 29 "golang.org/x/crypto/bcrypt" 30 ) 31 32 // CredentialsManager verifies/sets user credentials for web UI. 33 type CredentialsManager struct { 34 fileLocation string 35 } 36 37 const passwordFile = "nodeui-pass" 38 39 var ( 40 // ErrBadCredentials represents an error when validating wrong c. 41 ErrBadCredentials = errors.New("bad credentials") 42 43 passwordNotFound = errors.New("password or password file doesn't exist") 44 ) 45 46 // NewCredentialsManager returns given a password file directory 47 // returns a new credentials manager, which can be used to validate or alter 48 // user credentials. 49 func NewCredentialsManager(dataDir string) *CredentialsManager { 50 return &CredentialsManager{ 51 fileLocation: filepath.Join(dataDir, passwordFile), 52 } 53 } 54 55 // Validate username and password against stored credentials. 56 func (c *CredentialsManager) Validate(username, password string) error { 57 if username != config.FlagTequilapiUsername.Value { 58 return ErrBadCredentials 59 } 60 61 storedHash, err := c.loadOrInitialize() 62 if err != nil { 63 return fmt.Errorf("could not load credentials: %w", err) 64 } 65 66 err = bcrypt.CompareHashAndPassword([]byte(storedHash), []byte(password)) 67 if err != nil { 68 return fmt.Errorf("bad credentials: %w", err) 69 } 70 71 return nil 72 } 73 74 // SetPassword sets a new password for a user. 75 func (c *CredentialsManager) SetPassword(password string) error { 76 passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) 77 if err != nil { 78 return fmt.Errorf("unable to generate password hash: %w", err) 79 } 80 81 return c.setPassword(string(passwordHash)) 82 } 83 84 func (c *CredentialsManager) loadOrInitialize() (string, error) { 85 storedHash, err := c.getPassword() 86 if !errors.Is(err, passwordNotFound) { 87 return storedHash, err 88 } 89 90 log.Info().Err(err).Msg("CredentialsManager not found, initializing to default") 91 92 err = c.SetPassword(config.FlagTequilapiPassword.Value) 93 if err != nil { 94 return "", fmt.Errorf("failed to set initial credentials: %w", err) 95 } 96 97 return c.getPassword() 98 } 99 100 func (c *CredentialsManager) setPassword(passwordHash string) error { 101 f, err := os.OpenFile(c.fileLocation, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) 102 if err != nil { 103 return fmt.Errorf("could not open password file: %w", err) 104 } 105 defer f.Close() 106 107 if _, err := f.WriteString(passwordHash); err != nil { 108 return fmt.Errorf("could not write the new password: %w", err) 109 } 110 111 return f.Sync() 112 } 113 114 func (c *CredentialsManager) getPassword() (string, error) { 115 file, err := os.Open(c.fileLocation) 116 if err != nil { 117 if errors.Is(err, os.ErrNotExist) { 118 return "", passwordNotFound 119 } 120 121 return "", err 122 } 123 defer file.Close() 124 125 scanner := bufio.NewScanner(file) 126 if scanner.Scan() { 127 return scanner.Text(), scanner.Err() 128 } 129 130 return "", passwordNotFound 131 }