fix: load uid key from manager config
This commit is contained in:
+54
-6
@@ -7,10 +7,41 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/crypto/xtea"
|
||||
)
|
||||
|
||||
type userID int64
|
||||
|
||||
const defaultUIDKey = "la6YsO+bNX/+XIkOqc5Svw=="
|
||||
|
||||
var (
|
||||
uidCodecMu sync.RWMutex
|
||||
uidCipher *xtea.Cipher
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := SetUIDKey(defaultUIDKey); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func SetUIDKey(encoded string) error {
|
||||
key, err := base64.StdEncoding.DecodeString(strings.TrimSpace(encoded))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid uid key: %w", err)
|
||||
}
|
||||
cipher, err := xtea.NewCipher(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid uid key: %w", err)
|
||||
}
|
||||
uidCodecMu.Lock()
|
||||
uidCipher = cipher
|
||||
uidCodecMu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (id *userID) UnmarshalJSON(raw []byte) error {
|
||||
value, err := parseUserIDJSON(raw)
|
||||
if err != nil {
|
||||
@@ -63,7 +94,7 @@ func parseUserID(value string) (int64, error) {
|
||||
if err != nil || len(decoded) < 8 {
|
||||
return 0, fmt.Errorf("invalid user id")
|
||||
}
|
||||
return int64(binary.LittleEndian.Uint64(decoded[:8])), nil
|
||||
return decryptUID(decoded[:8]), nil
|
||||
}
|
||||
return strconv.ParseInt(value, 10, 64)
|
||||
}
|
||||
@@ -72,11 +103,8 @@ func formatUserID(id int64) string {
|
||||
if id == 0 {
|
||||
return ""
|
||||
}
|
||||
src := make([]byte, 8)
|
||||
dst := make([]byte, base64.URLEncoding.WithPadding(base64.NoPadding).EncodedLen(8))
|
||||
binary.LittleEndian.PutUint64(src, uint64(id))
|
||||
base64.URLEncoding.WithPadding(base64.NoPadding).Encode(dst, src)
|
||||
return "usr" + string(dst)
|
||||
encoded := encryptUID(id)
|
||||
return "usr" + base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(encoded)
|
||||
}
|
||||
|
||||
func parseUserIDList(values []userID) []int64 {
|
||||
@@ -92,3 +120,23 @@ func parseUserIDList(values []userID) []int64 {
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func decryptUID(encoded []byte) int64 {
|
||||
dst := make([]byte, 8)
|
||||
uidCodecMu.RLock()
|
||||
cipher := uidCipher
|
||||
uidCodecMu.RUnlock()
|
||||
cipher.Decrypt(dst, encoded[:8])
|
||||
return int64(binary.LittleEndian.Uint64(dst))
|
||||
}
|
||||
|
||||
func encryptUID(id int64) []byte {
|
||||
src := make([]byte, 8)
|
||||
dst := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(src, uint64(id))
|
||||
uidCodecMu.RLock()
|
||||
cipher := uidCipher
|
||||
uidCodecMu.RUnlock()
|
||||
cipher.Encrypt(dst, src)
|
||||
return dst
|
||||
}
|
||||
|
||||
@@ -22,6 +22,23 @@ func TestUserIDJSONAcceptsIMUserID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserIDCodecMatchesIMServer(t *testing.T) {
|
||||
const (
|
||||
publicID = "usrOj2NWDNdlMU"
|
||||
dbID = int64(1574592702383656960)
|
||||
)
|
||||
got, err := parseUserID(publicID)
|
||||
if err != nil {
|
||||
t.Fatalf("parseUserID: %v", err)
|
||||
}
|
||||
if got != dbID {
|
||||
t.Fatalf("parseUserID(%q)=%d, want %d", publicID, got, dbID)
|
||||
}
|
||||
if formatted := formatUserID(dbID); formatted != publicID {
|
||||
t.Fatalf("formatUserID(%d)=%q, want %q", dbID, formatted, publicID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserIDJSONRejectsNumericID(t *testing.T) {
|
||||
var id userID
|
||||
if err := json.Unmarshal([]byte(`1573862062872858624`), &id); err == nil {
|
||||
|
||||
Reference in New Issue
Block a user