mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Add promptutil to shared/promptuil (#6757)
* Add promptutil to shared/promptutil * fixes * comment
This commit is contained in:
1
go.sum
1
go.sum
@@ -383,6 +383,7 @@ github.com/ianlancetaylor/cgosymbolizer v0.0.0-20200424224625-be1b05b0b279 h1:Ip
|
||||
github.com/ianlancetaylor/cgosymbolizer v0.0.0-20200424224625-be1b05b0b279/go.mod h1:a5aratAVTWyz+nJMmDsN8O4XTfaLfdAsB1ysCmZX5Bw=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80 h1:g/SJtZVYc1cxSB8lgrgqeOlIdi4MhqNNHYRAC8y+g4c=
|
||||
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/flux v0.65.0/go.mod h1:BwN2XG2lMszOoquQaFdPET8FRQfrXiZsWmcMO9rkaVY=
|
||||
|
||||
16
shared/promptutil/BUILD.bazel
Normal file
16
shared/promptutil/BUILD.bazel
Normal file
@@ -0,0 +1,16 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"prompt.go",
|
||||
"validate.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/shared/promptutil",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"@com_github_logrusorgru_aurora//:go_default_library",
|
||||
"@com_github_nbutton23_zxcvbn_go//:go_default_library",
|
||||
"@org_golang_x_crypto//ssh/terminal:go_default_library",
|
||||
],
|
||||
)
|
||||
90
shared/promptutil/prompt.go
Normal file
90
shared/promptutil/prompt.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package promptutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
var au = aurora.NewAurora(true)
|
||||
|
||||
// ValidatePrompt requests the user for text and expects it to fulfil la provided validation function.
|
||||
func ValidatePrompt(promptText string, validateFunc func(string) error) (string, error) {
|
||||
var responseValid bool
|
||||
var response string
|
||||
for !responseValid {
|
||||
fmt.Printf("%s:\n", au.Bold(promptText))
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil && !strings.Contains(err.Error(), "unexpected newline") {
|
||||
return "", err
|
||||
}
|
||||
response = strings.TrimRight(response, "\r\n")
|
||||
if err := validateFunc(response); err != nil {
|
||||
fmt.Printf("Entry not valid: %s\n", au.BrightRed(err))
|
||||
} else {
|
||||
responseValid = true
|
||||
}
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// DefaultPrompt prompts the user for any text and performs no validation. If nothing is entered it returns the default.
|
||||
func DefaultPrompt(promptText string, defaultValue string) (string, error) {
|
||||
var response string
|
||||
fmt.Printf("%s %s:\n", promptText, fmt.Sprintf("(%s: %s)", au.BrightGreen("default"), defaultValue))
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil && !strings.Contains(err.Error(), "unexpected newline") {
|
||||
return "", err
|
||||
}
|
||||
response = strings.TrimRight(response, "\r\n")
|
||||
if response == "" {
|
||||
return defaultValue, nil
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// DefaultAndValidatePrompt prompts the user for any text and expects it to fulfill a validation function. If nothing is entered
|
||||
// the default value is returned.
|
||||
func DefaultAndValidatePrompt(promptText string, defaultValue string, validateFunc func(string) error) (string, error) {
|
||||
var responseValid bool
|
||||
var response string
|
||||
for !responseValid {
|
||||
fmt.Printf("%s %s:\n", promptText, fmt.Sprintf("(%s: %s)", au.BrightGreen("default"), defaultValue))
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil && !strings.Contains(err.Error(), "unexpected newline") {
|
||||
return "", err
|
||||
}
|
||||
response = strings.TrimRight(response, "\r\n")
|
||||
if response == "" {
|
||||
return defaultValue, nil
|
||||
}
|
||||
if err := validateFunc(response); err != nil {
|
||||
fmt.Printf("Entry not valid: %s\n", au.BrightRed(err))
|
||||
}
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// PasswordPrompt prompts the user for a password, that repeatedly requests the password until it qualifies the
|
||||
// passed in validation function.
|
||||
func PasswordPrompt(promptText string, validateFunc func(string) error) (string, error) {
|
||||
var responseValid bool
|
||||
var response string
|
||||
for !responseValid {
|
||||
fmt.Printf("%s:\n", au.Bold(promptText))
|
||||
bytePassword, err := terminal.ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
response = strings.TrimRight(string(bytePassword), "\r\n")
|
||||
if err := validateFunc(response); err != nil {
|
||||
fmt.Printf("\nEntry not valid: %s\n", au.BrightRed(err))
|
||||
} else {
|
||||
responseValid = true
|
||||
}
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
98
shared/promptutil/validate.go
Normal file
98
shared/promptutil/validate.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package promptutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"unicode"
|
||||
|
||||
strongPasswords "github.com/nbutton23/zxcvbn-go"
|
||||
)
|
||||
|
||||
const (
|
||||
// Constants for passwords.
|
||||
minPasswordLength = 8
|
||||
// Min password score of 3 out of 5 based on the https://github.com/nbutton23/zxcvbn-go
|
||||
// library for strong-entropy password computation.
|
||||
minPasswordScore = 3
|
||||
)
|
||||
|
||||
// NotEmpty is a validation function to make sure the input given isn't empty and is valid unicode.
|
||||
func NotEmpty(input string) error {
|
||||
if input == "" {
|
||||
return errors.New("input cannot be empty")
|
||||
}
|
||||
if !IsValidUnicode(input) {
|
||||
return errors.New("not valid unicode")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateNumber makes sure the entered text is a valid number.
|
||||
func ValidateNumber(input string) error {
|
||||
_, err := strconv.Atoi(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConfirmation makes sure the entered text is the user confirming.
|
||||
func ValidateConfirmation(input string) error {
|
||||
if input != "Y" && input != "y" {
|
||||
return errors.New("please confirm the above text")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsValidUnicode checks if an input string is a valid unicode string comprised of only
|
||||
// letters, numbers, punctuation, or symbols.
|
||||
func IsValidUnicode(input string) bool {
|
||||
for _, char := range input {
|
||||
if !(unicode.IsLetter(char) ||
|
||||
unicode.IsNumber(char) ||
|
||||
unicode.IsPunct(char) ||
|
||||
unicode.IsSymbol(char)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ValidatePasswordInput validates a strong password input for new accounts,
|
||||
// including a min length, at least 1 number and at least
|
||||
// 1 special character.
|
||||
func ValidatePasswordInput(input string) error {
|
||||
var (
|
||||
hasMinLen = false
|
||||
hasLetter = false
|
||||
hasNumber = false
|
||||
hasSpecial = false
|
||||
)
|
||||
if len(input) >= minPasswordLength {
|
||||
hasMinLen = true
|
||||
}
|
||||
for _, char := range input {
|
||||
switch {
|
||||
case !(unicode.IsLetter(char) || unicode.IsNumber(char) || unicode.IsPunct(char) || unicode.IsSymbol(char)):
|
||||
return errors.New("password must only contain alphanumeric characters, punctuation, or symbols")
|
||||
case unicode.IsLetter(char):
|
||||
hasLetter = true
|
||||
case unicode.IsNumber(char):
|
||||
hasNumber = true
|
||||
case unicode.IsPunct(char) || unicode.IsSymbol(char):
|
||||
hasSpecial = true
|
||||
}
|
||||
}
|
||||
if !(hasMinLen && hasLetter && hasNumber && hasSpecial) {
|
||||
return errors.New(
|
||||
"password must have more than 8 characters, at least 1 special character, and 1 number",
|
||||
)
|
||||
}
|
||||
strength := strongPasswords.PasswordStrength(input, nil)
|
||||
if strength.Score < minPasswordScore {
|
||||
return errors.New(
|
||||
"password is too easy to guess, try a stronger password",
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user