keys: the version check, CheckName and IsHex64 move here from the Delivery domain's kept package, since Delivery and Identity and access share them (ADR 0634 §1, §4; issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 14:40:09 +01:00
parent e67f85b5fe
commit 7ea9572f03
2 changed files with 60 additions and 0 deletions
+39
View File
@@ -5,6 +5,8 @@ package keys
import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"regexp"
"strings"
@@ -15,6 +17,43 @@ const Version = 1
var nameOK = regexp.MustCompile(`^[-/_=a-zA-Z0-9]+$`)
// ErrUnknownVersion is what CheckVersion returns for a value whose version is missing or not Version. Test it with
// errors.Is.
var ErrUnknownVersion = errors.New("keys: unknown value version")
// CheckVersion refuses a value with no version or one this reader does not read.
func CheckVersion(data []byte) error {
var probe struct {
V *int `json:"v"`
}
if err := json.Unmarshal(data, &probe); err != nil {
return fmt.Errorf("keys: value is not JSON: %w", err)
}
if probe.V == nil {
return fmt.Errorf("%w: no version in the value, this reader reads %d", ErrUnknownVersion, Version)
}
if *probe.V != Version {
return fmt.Errorf("%w: the value is version %d, this reader reads %d", ErrUnknownVersion, *probe.V, Version)
}
return nil
}
// CheckName refuses a name that is empty or holds a dot, a wildcard, whitespace or a character a bus key refuses.
func CheckName(kind, name string) error {
if name == "" {
return fmt.Errorf("keys: the %s name is empty", kind)
}
if !nameOK.MatchString(name) {
return fmt.Errorf("keys: the %s name %q holds a dot, a wildcard, whitespace or a character a key refuses", kind, name)
}
return nil
}
var hex64 = regexp.MustCompile(`^[0-9a-f]{64}$`)
// IsHex64 says whether s is 64 lowercase hex digits.
func IsHex64(s string) bool { return hex64.MatchString(s) }
// EscapeName is name as a key part: a character outside letters, digits, `-`, `/` and `_` is written `=` and its
// two lowercase hex digits per byte, and `=` itself is escaped, so any non-empty name keys and a name of allowed
// characters only keys as it is. UnescapeName is its inverse.
+21
View File
@@ -1,6 +1,7 @@
package keys
import (
"errors"
"strings"
"testing"
)
@@ -27,3 +28,23 @@ func TestNamesEscapeIntoKeyPartsAndBack(t *testing.T) {
t.Errorf("Version = %d: a kept value's readers read the one version", Version)
}
}
func TestAKeptValueIsReadOnlyAtItsVersionAndAKeyPartOnlyOfKeyCharacters(t *testing.T) {
for data, ok := range map[string]bool{`{"v":1}`: true, `{"v":2}`: false, `{}`: false} {
err := CheckVersion([]byte(data))
if (err == nil) != ok || (!ok && !errors.Is(err, ErrUnknownVersion)) {
t.Errorf("CheckVersion(%s) = %v", data, err)
}
}
if CheckVersion([]byte(`nope`)) == nil {
t.Error("a value that is no JSON is read")
}
for name, ok := range map[string]bool{"a-b_c/d=": true, "": false, "a.b": false, "a b": false, "a*": false} {
if (CheckName("module", name) == nil) != ok {
t.Errorf("CheckName(%q) = %v", name, CheckName("module", name))
}
}
if !IsHex64(strings.Repeat("a", 64)) || IsHex64(strings.Repeat("A", 64)) || IsHex64("a") {
t.Error("IsHex64 reads a wrong form")
}
}