Files
galaxy-game/authsession/internal/service/shared/normalize.go
T
2026-04-08 16:23:07 +02:00

159 lines
4.6 KiB
Go

package shared
import (
"crypto/ed25519"
"encoding/base64"
"fmt"
"strings"
"time"
"galaxy/authsession/internal/domain/common"
"galaxy/authsession/internal/domain/devicesession"
)
// NormalizeString trims surrounding Unicode whitespace from value.
func NormalizeString(value string) string {
return strings.TrimSpace(value)
}
// ParseEmail trims value and validates it against the frozen public e-mail
// contract.
func ParseEmail(value string) (common.Email, error) {
email := common.Email(NormalizeString(value))
if err := email.Validate(); err != nil {
return "", InvalidRequest(err.Error())
}
return email, nil
}
// ParseChallengeID trims value and validates it as one challenge identifier.
func ParseChallengeID(value string) (common.ChallengeID, error) {
challengeID := common.ChallengeID(NormalizeString(value))
if err := challengeID.Validate(); err != nil {
return "", InvalidRequest(err.Error())
}
return challengeID, nil
}
// ParseDeviceSessionID trims value and validates it as one device-session
// identifier.
func ParseDeviceSessionID(value string) (common.DeviceSessionID, error) {
deviceSessionID := common.DeviceSessionID(NormalizeString(value))
if err := deviceSessionID.Validate(); err != nil {
return "", InvalidRequest(err.Error())
}
return deviceSessionID, nil
}
// ParseUserID trims value and validates it as one user identifier.
func ParseUserID(value string) (common.UserID, error) {
userID := common.UserID(NormalizeString(value))
if err := userID.Validate(); err != nil {
return "", InvalidRequest(err.Error())
}
return userID, nil
}
// ParseRequiredCode trims value and validates it as a required non-empty
// confirmation code.
func ParseRequiredCode(value string) (string, error) {
code := NormalizeString(value)
if code == "" {
return "", InvalidRequest("code must not be empty")
}
return code, nil
}
// ParseClientPublicKey trims value and validates it as the standard
// base64-encoded raw 32-byte Ed25519 public key expected by the public auth
// contract.
func ParseClientPublicKey(value string) (common.ClientPublicKey, error) {
normalized := NormalizeString(value)
if normalized == "" {
return common.ClientPublicKey{}, InvalidClientPublicKey()
}
decoded, err := base64.StdEncoding.Strict().DecodeString(normalized)
if err != nil || len(decoded) != ed25519.PublicKeySize {
return common.ClientPublicKey{}, InvalidClientPublicKey()
}
key, err := common.NewClientPublicKey(ed25519.PublicKey(decoded))
if err != nil {
return common.ClientPublicKey{}, InvalidClientPublicKey()
}
return key, nil
}
// ParseRevokeReasonCode trims value and validates it as one machine-readable
// revoke reason code.
func ParseRevokeReasonCode(value string) (common.RevokeReasonCode, error) {
code := common.RevokeReasonCode(NormalizeString(value))
if err := code.Validate(); err != nil {
return "", InvalidRequest(err.Error())
}
return code, nil
}
// ParseRevokeActorType trims value and validates it as one machine-readable
// revoke actor type.
func ParseRevokeActorType(value string) (common.RevokeActorType, error) {
actorType := common.RevokeActorType(NormalizeString(value))
if err := actorType.Validate(); err != nil {
return "", InvalidRequest(err.Error())
}
return actorType, nil
}
// ParseOptionalActorID trims value and validates it as one optional stable
// actor identifier.
func ParseOptionalActorID(value string) (string, error) {
actorID := NormalizeString(value)
if actorID != value {
return "", InvalidRequest("actor_id must not contain surrounding whitespace")
}
return actorID, nil
}
// BuildRevocation validates one revoke request payload and returns the domain
// revocation metadata applied to a session mutation.
func BuildRevocation(reasonCode string, actorType string, actorID string, at time.Time) (devicesession.Revocation, error) {
if at.IsZero() {
return devicesession.Revocation{}, InternalError(fmt.Errorf("revocation time must not be zero"))
}
parsedReasonCode, err := ParseRevokeReasonCode(reasonCode)
if err != nil {
return devicesession.Revocation{}, err
}
parsedActorType, err := ParseRevokeActorType(actorType)
if err != nil {
return devicesession.Revocation{}, err
}
parsedActorID, err := ParseOptionalActorID(actorID)
if err != nil {
return devicesession.Revocation{}, err
}
revocation := devicesession.Revocation{
At: at.UTC(),
ReasonCode: parsedReasonCode,
ActorType: parsedActorType,
ActorID: parsedActorID,
}
if err := revocation.Validate(); err != nil {
return devicesession.Revocation{}, InternalError(fmt.Errorf("build revocation: %w", err))
}
return revocation, nil
}