96 lines
2.7 KiB
Go
96 lines
2.7 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
maxOpIDLength = 128
|
|
maxEntityTypeLength = 64
|
|
maxEntityIDLength = 4096
|
|
maxOpTypeLength = 64
|
|
maxIdempotencyKeyLength = 128
|
|
maxDeviceIDLength = 128
|
|
maxDeviceNameLength = 128
|
|
maxClientVersionLength = 256
|
|
maxVaultIDLength = 256
|
|
maxLoginLength = 320
|
|
)
|
|
|
|
type syncPushOperation struct {
|
|
OpID string `json:"op_id"`
|
|
EntityType string `json:"entity_type"`
|
|
EntityID string `json:"entity_id"`
|
|
OpType string `json:"op_type"`
|
|
PayloadJSON string `json:"payload_json"`
|
|
ClientSequence int `json:"client_sequence"`
|
|
LastSeenServerSeq int `json:"last_seen_server_seq"`
|
|
CreatedAt string `json:"created_at"`
|
|
}
|
|
|
|
type syncPushRequest struct {
|
|
DeviceID string `json:"device_id"`
|
|
IdempotencyKey string `json:"idempotency_key"`
|
|
Ops []syncPushOperation `json:"ops"`
|
|
}
|
|
|
|
type syncPullRequest struct {
|
|
SinceSequence int `json:"since_sequence"`
|
|
PageLimit int `json:"page_limit"`
|
|
}
|
|
|
|
func (s *Server) validateSyncPush(req syncPushRequest) (code, message string) {
|
|
if len(req.Ops) > s.cfg.Limits.MaxPushOperations {
|
|
return "too_many_operations", "too many operations in one push"
|
|
}
|
|
for name, value := range map[string]struct {
|
|
value string
|
|
max int
|
|
}{
|
|
"device_id": {req.DeviceID, maxDeviceIDLength},
|
|
"idempotency_key": {req.IdempotencyKey, maxIdempotencyKeyLength},
|
|
} {
|
|
if err := validateStringLength(name, value.value, value.max); err != nil {
|
|
return "field_too_long", err.Error()
|
|
}
|
|
}
|
|
for _, op := range req.Ops {
|
|
for _, value := range []struct {
|
|
name string
|
|
value string
|
|
max int
|
|
}{
|
|
{"op_id", op.OpID, maxOpIDLength},
|
|
{"entity_type", op.EntityType, maxEntityTypeLength},
|
|
{"entity_id", op.EntityID, maxEntityIDLength},
|
|
{"op_type", op.OpType, maxOpTypeLength},
|
|
} {
|
|
if strings.TrimSpace(value.value) == "" {
|
|
return "invalid_operation", fmt.Sprintf("%s is required", value.name)
|
|
}
|
|
if err := validateStringLength(value.name, value.value, value.max); err != nil {
|
|
return "field_too_long", err.Error()
|
|
}
|
|
}
|
|
if len(op.PayloadJSON) > s.cfg.Limits.MaxPayloadJSON {
|
|
return "payload_too_large", "operation payload is too large"
|
|
}
|
|
if op.PayloadJSON != "" && !json.Valid([]byte(op.PayloadJSON)) {
|
|
return "invalid_payload", "operation payload_json must be valid JSON"
|
|
}
|
|
if op.ClientSequence < 0 || op.LastSeenServerSeq < 0 {
|
|
return "invalid_operation", "operation sequences must be non-negative"
|
|
}
|
|
}
|
|
return "", ""
|
|
}
|
|
|
|
func (s *Server) pullPageLimit(requested int) int {
|
|
if requested <= 0 || requested > s.cfg.Limits.MaxPullPage {
|
|
return s.cfg.Limits.MaxPullPage
|
|
}
|
|
return requested
|
|
}
|