feat: a key that can't be used is explained in the picker
The key picker stays open until the link is made, saying "encrypting
with …" meanwhile. A key that can't be read, or doesn't keep to
share.keyRules, is shown in it in red with which rule it misses
("its value has 28 characters; share.keyRules needs 32
(minLength)"), to pick another.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
6 files changed
+73
-16
No files matched your search
+23
-3
@@ -2,6 +2,7 @@ package share
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
@@ -40,11 +41,20 @@ func (r Rules) Check(key []byte) error {
|
||||
}
|
||||
}
|
||||
n := len([]rune(s))
|
||||
if n >= r.MinLength && kinds >= r.MinClasses {
|
||||
var has, needs []string
|
||||
if n < r.MinLength {
|
||||
has = append(has, fmt.Sprintf("%d characters", n))
|
||||
needs = append(needs, fmt.Sprintf("%d (minLength)", r.MinLength))
|
||||
}
|
||||
if kinds < r.MinClasses {
|
||||
has = append(has, fmt.Sprintf("%d %s of character", kinds, map[bool]string{true: "kind", false: "kinds"}[kinds == 1]))
|
||||
needs = append(needs, fmt.Sprintf("%d of lowercase, uppercase, digits and symbols (minClasses)", r.MinClasses))
|
||||
}
|
||||
if len(has) == 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("the key's value is %d characters of %d kinds, and share.keyRules wants at least %d characters of %d kinds (lowercase, uppercase, digits, symbols): use a random one, like openssl rand -base64 32 makes",
|
||||
n, kinds, r.MinLength, r.MinClasses)
|
||||
return fmt.Errorf("its value has %s; share.keyRules needs %s. Use a random one, like openssl rand -base64 32 makes",
|
||||
strings.Join(has, " and "), strings.Join(needs, " and "))
|
||||
}
|
||||
|
||||
func (r Rules) withDefaults() Rules {
|
||||
@@ -57,3 +67,13 @@ func (r Rules) withDefaults() Rules {
|
||||
r.MinLength = max(r.MinLength, MinKey)
|
||||
return r
|
||||
}
|
||||
|
||||
// KeyError is a key that can't be used: it can't be read, or doesn't
|
||||
// keep to the rules.
|
||||
type KeyError struct {
|
||||
Name string // the secret's
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *KeyError) Error() string { return "can't use " + e.Name + ": " + e.Err.Error() }
|
||||
func (e *KeyError) Unwrap() error { return e.Err }
|
||||
@@ -91,13 +91,13 @@ type file struct {
|
||||
}
|
||||
|
||||
// Seal encrypts files with key, the value of h.Key, into a share link,
|
||||
// if key keeps to rules.
|
||||
// if key keeps to rules (*KeyError if not).
|
||||
func Seal(h Header, key []byte, files []File, rules Rules) (ref.Ref, error) {
|
||||
if _, err := ParseKey(h.Key.String()); err != nil {
|
||||
return ref.Ref{}, fmt.Errorf("the key: %w", err)
|
||||
}
|
||||
if err := rules.Check(key); err != nil {
|
||||
return ref.Ref{}, err
|
||||
return ref.Ref{}, &KeyError{Name: h.Key.Name, Err: err}
|
||||
}
|
||||
if len(files) == 0 {
|
||||
return ref.Ref{}, errors.New("no files to share")
|
||||
|
||||
@@ -62,7 +62,7 @@ func TestSealRefuses(t *testing.T) {
|
||||
files []File
|
||||
want string
|
||||
}{
|
||||
"short key": {Header{Key: key}, []byte("short"), ok, "keyRules wants"},
|
||||
"short key": {Header{Key: key}, []byte("short"), ok, "needs 32 (minLength)"},
|
||||
"vault as key": {Header{Key: ref.Ref{Kind: provider.KindAzure, Host: "kv.vault.azure.net"}}, pass, ok, "isn't a secret"},
|
||||
"no files": {Header{Key: key}, pass, nil, "no files"},
|
||||
"not env": {Header{Key: key}, pass, []File{{Path: "main.go", Data: []byte("x")}}, "isn't an env file"},
|
||||
|
||||
Reference in new issue
Block a user