Protocol: split client session authority
This commit is contained in:
+127
-1
@@ -14,7 +14,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const SchemaSHA256 = "dea3dd210c53d5a2d37050dd6afd8b0ac5bb8edcb7ab25a02e4026489ce8a00f"
|
||||
const SchemaSHA256 = "762d009c3d25d80c3850d975e45f7a6b3fd8adf5c93c8fa7dd11dfa993f8bbb1"
|
||||
const ProtocolVersion = "1.0.0"
|
||||
const CurrentWireVersion = "2"
|
||||
const NMinus1WireVersion = "1"
|
||||
@@ -97,6 +97,16 @@ type ChannelFrame struct {
|
||||
Payload string `json:"payload"`
|
||||
}
|
||||
|
||||
type ClientSessionAuthority struct {
|
||||
Version string `json:"version"`
|
||||
SessionID string `json:"session_id"`
|
||||
GatewayID string `json:"gateway_id"`
|
||||
Audience string `json:"audience"`
|
||||
ReconnectSequence int64 `json:"reconnect_sequence"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
Capabilities CapabilityProfile `json:"capabilities"`
|
||||
}
|
||||
|
||||
type ClipboardPolicy struct {
|
||||
ClientToProviderEnabled bool `json:"client_to_provider_enabled"`
|
||||
ProviderToClientEnabled bool `json:"provider_to_client_enabled"`
|
||||
@@ -1233,6 +1243,122 @@ func EncodeChannelFrame(value ChannelFrame) ([]byte, error) {
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v ClientSessionAuthority) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.Version == "" {
|
||||
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
||||
}
|
||||
if v.Version != "1" && v.Version != "" {
|
||||
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
||||
}
|
||||
if v.SessionID == "" {
|
||||
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
||||
}
|
||||
if len(v.SessionID) < 1 && v.SessionID != "" {
|
||||
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
||||
}
|
||||
if len(v.SessionID) > 128 {
|
||||
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
||||
}
|
||||
if v.GatewayID == "" {
|
||||
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
||||
}
|
||||
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
||||
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
||||
}
|
||||
if len(v.GatewayID) > 128 {
|
||||
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
||||
}
|
||||
if v.Audience == "" {
|
||||
violations = append(violations, FieldViolation{Field: "audience", Code: "required"})
|
||||
}
|
||||
if len(v.Audience) < 1 && v.Audience != "" {
|
||||
violations = append(violations, FieldViolation{Field: "audience", Code: "min_length"})
|
||||
}
|
||||
if len(v.Audience) > 256 {
|
||||
violations = append(violations, FieldViolation{Field: "audience", Code: "max_length"})
|
||||
}
|
||||
if v.ReconnectSequence != 0 && v.ReconnectSequence < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "reconnect_sequence", Code: "minimum"})
|
||||
}
|
||||
if v.ExpiresAt == "" {
|
||||
violations = append(violations, FieldViolation{Field: "expires_at", Code: "required"})
|
||||
}
|
||||
if len(v.ExpiresAt) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "expires_at", Code: "max_length"})
|
||||
}
|
||||
if v.ExpiresAt != "" {
|
||||
if parsed, err := time.Parse(time.RFC3339Nano, v.ExpiresAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.ExpiresAt {
|
||||
violations = append(violations, FieldViolation{Field: "expires_at", Code: "invalid_time"})
|
||||
}
|
||||
}
|
||||
if reflect.DeepEqual(v.Capabilities, CapabilityProfile{}) {
|
||||
violations = append(violations, FieldViolation{Field: "capabilities", Code: "required"})
|
||||
}
|
||||
if err := v.Capabilities.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "capabilities", Code: "invalid_object"})
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeClientSessionAuthority(data []byte) (ClientSessionAuthority, error) {
|
||||
var value ClientSessionAuthority
|
||||
if len(data) > 1024*1024 {
|
||||
return value, errors.New("protocol payload exceeds limit")
|
||||
}
|
||||
var fields map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &fields); err != nil {
|
||||
return value, err
|
||||
}
|
||||
if raw, ok := fields["audience"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "audience", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["capabilities"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "capabilities", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["expires_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "expires_at", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["gateway_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "gateway_id", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["reconnect_sequence"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "reconnect_sequence", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["session_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "session_id", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "version", Code: "required"}}}
|
||||
}
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&value); err != nil {
|
||||
return value, err
|
||||
}
|
||||
var trailing any
|
||||
if err := decoder.Decode(&trailing); err != io.EOF {
|
||||
if err == nil {
|
||||
return value, errors.New("trailing JSON value")
|
||||
}
|
||||
return value, err
|
||||
}
|
||||
if err := value.Validate(); err != nil {
|
||||
return value, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func EncodeClientSessionAuthority(value ClientSessionAuthority) ([]byte, error) {
|
||||
if err := value.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v ClipboardPolicy) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.MaxTextBytes == 0 {
|
||||
|
||||
Reference in New Issue
Block a user