feat: user service

This commit is contained in:
Ilia Denisov
2026-04-10 19:05:02 +02:00
committed by GitHub
parent 710bad712e
commit 23ffcb7535
140 changed files with 33418 additions and 952 deletions
@@ -38,6 +38,11 @@ var (
// ErrInvalidEventSignature reports that one gateway event signature is not
// a raw Ed25519 signature for the canonical event signing input.
ErrInvalidEventSignature = errors.New("invalid event signature")
// ErrInvalidResponseSignature reports that one gateway unary response
// signature is not a raw Ed25519 signature for the canonical response
// signing input.
ErrInvalidResponseSignature = errors.New("invalid response signature")
)
// RequestSigningFields stores the canonical public request fields bound into
@@ -85,6 +90,25 @@ type EventSigningFields struct {
PayloadHash []byte
}
// ResponseSigningFields stores the canonical public unary response fields
// bound into one gateway signature input.
type ResponseSigningFields struct {
// ProtocolVersion identifies the gateway transport envelope version.
ProtocolVersion string
// RequestID is the transport correlation identifier echoed by the gateway.
RequestID string
// TimestampMS carries the gateway response timestamp in milliseconds.
TimestampMS int64
// ResultCode stores the stable opaque gateway result code.
ResultCode string
// PayloadHash stores the raw SHA-256 digest of PayloadBytes.
PayloadHash []byte
}
// ComputePayloadHash returns the canonical raw SHA-256 digest for payloadBytes.
func ComputePayloadHash(payloadBytes []byte) []byte {
sum := sha256.Sum256(payloadBytes)
@@ -154,6 +178,28 @@ func BuildEventSigningInput(fields EventSigningFields) []byte {
return buf
}
// BuildResponseSigningInput returns the canonical byte sequence the v1
// gateway unary response signature covers.
func BuildResponseSigningInput(fields ResponseSigningFields) []byte {
size := len("galaxy-response-v1") +
len(fields.ProtocolVersion) +
len(fields.RequestID) +
len(fields.ResultCode) +
len(fields.PayloadHash) +
(5 * binary.MaxVarintLen64) +
8
buf := make([]byte, 0, size)
buf = appendLengthPrefixedString(buf, "galaxy-response-v1")
buf = appendLengthPrefixedString(buf, fields.ProtocolVersion)
buf = appendLengthPrefixedString(buf, fields.RequestID)
buf = binary.BigEndian.AppendUint64(buf, uint64(fields.TimestampMS))
buf = appendLengthPrefixedString(buf, fields.ResultCode)
buf = appendLengthPrefixedBytes(buf, fields.PayloadHash)
return buf
}
// SignRequest returns one raw Ed25519 client signature for the canonical v1
// request signing input.
func SignRequest(privateKey ed25519.PrivateKey, fields RequestSigningFields) []byte {
@@ -173,6 +219,19 @@ func VerifyEventSignature(publicKey ed25519.PublicKey, signature []byte, fields
return nil
}
// VerifyResponseSignature reports whether signature authenticates fields under
// publicKey using the canonical gateway unary-response signing input.
func VerifyResponseSignature(publicKey ed25519.PublicKey, signature []byte, fields ResponseSigningFields) error {
if len(publicKey) != ed25519.PublicKeySize || len(signature) != ed25519.SignatureSize {
return ErrInvalidResponseSignature
}
if !ed25519.Verify(publicKey, BuildResponseSigningInput(fields), signature) {
return ErrInvalidResponseSignature
}
return nil
}
func appendLengthPrefixedString(dst []byte, value string) []byte {
return appendLengthPrefixedBytes(dst, []byte(value))
}
@@ -0,0 +1,61 @@
// Package userv1contract provides public-contract helpers for the
// authenticated gateway v1 User Service self-service message types.
package userv1contract
import (
usermodel "galaxy/model/user"
"galaxy/transcoder"
)
const (
// MessageTypeGetMyAccount is the authenticated gateway message type used to
// read the current self-service account aggregate.
MessageTypeGetMyAccount = usermodel.MessageTypeGetMyAccount
// MessageTypeUpdateMyProfile is the authenticated gateway message type used
// to mutate self-service profile fields.
MessageTypeUpdateMyProfile = usermodel.MessageTypeUpdateMyProfile
// MessageTypeUpdateMySettings is the authenticated gateway message type used
// to mutate self-service settings fields.
MessageTypeUpdateMySettings = usermodel.MessageTypeUpdateMySettings
// ResultCodeOK is the success result code projected by gateway for all
// successful `user.*` authenticated commands.
ResultCodeOK = "ok"
)
// EncodeGetMyAccountRequest returns the FlatBuffers payload for the public
// empty get-account request.
func EncodeGetMyAccountRequest() ([]byte, error) {
return transcoder.GetMyAccountRequestToPayload(&usermodel.GetMyAccountRequest{})
}
// EncodeUpdateMyProfileRequest returns the FlatBuffers payload for one public
// self-service profile mutation request.
func EncodeUpdateMyProfileRequest(raceName string) ([]byte, error) {
return transcoder.UpdateMyProfileRequestToPayload(&usermodel.UpdateMyProfileRequest{
RaceName: raceName,
})
}
// EncodeUpdateMySettingsRequest returns the FlatBuffers payload for one public
// self-service settings mutation request.
func EncodeUpdateMySettingsRequest(preferredLanguage string, timeZone string) ([]byte, error) {
return transcoder.UpdateMySettingsRequestToPayload(&usermodel.UpdateMySettingsRequest{
PreferredLanguage: preferredLanguage,
TimeZone: timeZone,
})
}
// DecodeAccountResponse decodes the public FlatBuffers success payload shared
// by all authenticated `user.*` commands.
func DecodeAccountResponse(payload []byte) (*usermodel.AccountResponse, error) {
return transcoder.PayloadToAccountResponse(payload)
}
// DecodeErrorResponse decodes the public FlatBuffers error payload shared by
// all authenticated `user.*` commands.
func DecodeErrorResponse(payload []byte) (*usermodel.ErrorResponse, error) {
return transcoder.PayloadToErrorResponse(payload)
}