feat(protocol): define native session credentials
This commit is contained in:
+474
-19
@@ -13,11 +13,11 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const SchemaSHA256 = "b2bb0a8ac8ef56dbc0e1443eeb5b3028be9e71ec2f5fd8e73928d71b7cd9340c"
|
||||
const SchemaSHA256 = "dea3dd210c53d5a2d37050dd6afd8b0ac5bb8edcb7ab25a02e4026489ce8a00f"
|
||||
const ProtocolVersion = "1.0.0"
|
||||
const CurrentWireVersion = "1"
|
||||
const NMinus1WireVersion = "0"
|
||||
const NMinus2WireVersion = "-1"
|
||||
const CurrentWireVersion = "2"
|
||||
const NMinus1WireVersion = "1"
|
||||
const NMinus2WireVersion = "0"
|
||||
|
||||
type FieldViolation struct {
|
||||
Field string `json:"field"`
|
||||
@@ -69,6 +69,13 @@ type BrokerSession struct {
|
||||
EffectiveDisplayMode *DisplayMode `json:"effective_display_mode,omitempty"`
|
||||
}
|
||||
|
||||
type BrowserAuthenticatedSession struct {
|
||||
Username string `json:"username"`
|
||||
Provider string `json:"provider"`
|
||||
Roles []string `json:"roles"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
type CapabilityProfile struct {
|
||||
Transport string `json:"transport"`
|
||||
Framing string `json:"framing"`
|
||||
@@ -281,6 +288,14 @@ type ManifestTunnel struct {
|
||||
Features []string `json:"features"`
|
||||
}
|
||||
|
||||
type NativeAuthenticatedSession struct {
|
||||
Username string `json:"username"`
|
||||
Provider string `json:"provider"`
|
||||
Roles []string `json:"roles"`
|
||||
Role string `json:"role"`
|
||||
NativeIdentity NativeSessionIdentity `json:"native_identity"`
|
||||
}
|
||||
|
||||
type NativeCredential struct {
|
||||
DeviceID string `json:"device_id,omitempty"`
|
||||
FamilyID string `json:"family_id"`
|
||||
@@ -290,6 +305,19 @@ type NativeCredential struct {
|
||||
RefreshExpiresAt string `json:"refresh_expires_at,omitempty"`
|
||||
}
|
||||
|
||||
type NativeSessionIdentity struct {
|
||||
ClientDeviceID string `json:"client_device_id"`
|
||||
DeviceKeyID string `json:"device_key_id"`
|
||||
}
|
||||
|
||||
type NativeTunnelCredential struct {
|
||||
ClientDeviceID string `json:"client_device_id"`
|
||||
DeviceKeyID string `json:"device_key_id"`
|
||||
CertificateChainPem string `json:"certificate_chain_pem"`
|
||||
TrustBundlePem string `json:"trust_bundle_pem"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
}
|
||||
|
||||
type PageInfo struct {
|
||||
Limit int64 `json:"limit"`
|
||||
NextCursor string `json:"next_cursor"`
|
||||
@@ -392,12 +420,11 @@ type SessionAuthority struct {
|
||||
}
|
||||
|
||||
type SessionRequest struct {
|
||||
ClientDeviceID string `json:"client_device_id"`
|
||||
DeviceKeyID string `json:"device_key_id"`
|
||||
PoolID string `json:"pool_id"`
|
||||
IdempotencyKey string `json:"idempotency_key"`
|
||||
PolicySnapshot AllocationPolicy `json:"policy_snapshot"`
|
||||
RequestedDisplayMode *DisplayMode `json:"requested_display_mode,omitempty"`
|
||||
ClientDeviceID string `json:"client_device_id"`
|
||||
DeviceKeyID string `json:"device_key_id"`
|
||||
PoolID string `json:"pool_id"`
|
||||
IdempotencyKey string `json:"idempotency_key"`
|
||||
RequestedDisplayMode *DisplayMode `json:"requested_display_mode,omitempty"`
|
||||
}
|
||||
|
||||
type StableError struct {
|
||||
@@ -863,6 +890,105 @@ func EncodeBrokerSession(value BrokerSession) ([]byte, error) {
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v BrowserAuthenticatedSession) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.Username == "" {
|
||||
violations = append(violations, FieldViolation{Field: "username", Code: "required"})
|
||||
}
|
||||
if len(v.Username) < 1 && v.Username != "" {
|
||||
violations = append(violations, FieldViolation{Field: "username", Code: "min_length"})
|
||||
}
|
||||
if len(v.Username) > 256 {
|
||||
violations = append(violations, FieldViolation{Field: "username", Code: "max_length"})
|
||||
}
|
||||
if v.Provider == "" {
|
||||
violations = append(violations, FieldViolation{Field: "provider", Code: "required"})
|
||||
}
|
||||
if len(v.Provider) < 1 && v.Provider != "" {
|
||||
violations = append(violations, FieldViolation{Field: "provider", Code: "min_length"})
|
||||
}
|
||||
if len(v.Provider) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "provider", Code: "max_length"})
|
||||
}
|
||||
if v.Roles == nil {
|
||||
violations = append(violations, FieldViolation{Field: "roles", Code: "required"})
|
||||
}
|
||||
if len(v.Roles) > 16 {
|
||||
violations = append(violations, FieldViolation{Field: "roles", Code: "max_items"})
|
||||
}
|
||||
for _, item := range v.Roles {
|
||||
if len(item) < 1 {
|
||||
violations = append(violations, FieldViolation{Field: "roles", Code: "min_item_length"})
|
||||
}
|
||||
}
|
||||
for _, item := range v.Roles {
|
||||
if len(item) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "roles", Code: "max_item_length"})
|
||||
}
|
||||
}
|
||||
for _, item := range v.Roles {
|
||||
if len(item) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "roles", Code: "max_item_bytes"})
|
||||
}
|
||||
}
|
||||
if v.Role == "" {
|
||||
violations = append(violations, FieldViolation{Field: "role", Code: "required"})
|
||||
}
|
||||
if v.Role != "" && !(v.Role == "user" || v.Role == "admin") {
|
||||
violations = append(violations, FieldViolation{Field: "role", Code: "invalid_value"})
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeBrowserAuthenticatedSession(data []byte) (BrowserAuthenticatedSession, error) {
|
||||
var value BrowserAuthenticatedSession
|
||||
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["provider"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "provider", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["role"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "role", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["roles"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "roles", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["username"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "username", 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 EncodeBrowserAuthenticatedSession(value BrowserAuthenticatedSession) ([]byte, error) {
|
||||
if err := value.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v CapabilityProfile) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.Transport == "" {
|
||||
@@ -2689,6 +2815,16 @@ func (v GatewayRegistration) Validate() error {
|
||||
if len(v.Features) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "features", Code: "max_items"})
|
||||
}
|
||||
for _, item := range v.Features {
|
||||
if len(item) < 1 {
|
||||
violations = append(violations, FieldViolation{Field: "features", Code: "min_item_length"})
|
||||
}
|
||||
}
|
||||
for _, item := range v.Features {
|
||||
if len(item) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "features", Code: "max_item_length"})
|
||||
}
|
||||
}
|
||||
if reflect.DeepEqual(v.Capabilities, CapabilityProfile{}) {
|
||||
violations = append(violations, FieldViolation{Field: "capabilities", Code: "required"})
|
||||
}
|
||||
@@ -3231,6 +3367,16 @@ func (v ManifestGateway) Validate() error {
|
||||
if len(v.Addresses) > 4 {
|
||||
violations = append(violations, FieldViolation{Field: "addresses", Code: "max_items"})
|
||||
}
|
||||
for _, item := range v.Addresses {
|
||||
if len(item) < 1 {
|
||||
violations = append(violations, FieldViolation{Field: "addresses", Code: "min_item_length"})
|
||||
}
|
||||
}
|
||||
for _, item := range v.Addresses {
|
||||
if len(item) > 256 {
|
||||
violations = append(violations, FieldViolation{Field: "addresses", Code: "max_item_length"})
|
||||
}
|
||||
}
|
||||
if v.PublicIdentity == "" {
|
||||
violations = append(violations, FieldViolation{Field: "public_identity", Code: "required"})
|
||||
}
|
||||
@@ -3371,12 +3517,32 @@ func (v ManifestTunnel) Validate() error {
|
||||
if len(v.Versions) > 4 {
|
||||
violations = append(violations, FieldViolation{Field: "versions", Code: "max_items"})
|
||||
}
|
||||
for _, item := range v.Versions {
|
||||
if len(item) < 1 {
|
||||
violations = append(violations, FieldViolation{Field: "versions", Code: "min_item_length"})
|
||||
}
|
||||
}
|
||||
for _, item := range v.Versions {
|
||||
if len(item) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "versions", Code: "max_item_length"})
|
||||
}
|
||||
}
|
||||
if v.Features == nil {
|
||||
violations = append(violations, FieldViolation{Field: "features", Code: "required"})
|
||||
}
|
||||
if len(v.Features) > 32 {
|
||||
violations = append(violations, FieldViolation{Field: "features", Code: "max_items"})
|
||||
}
|
||||
for _, item := range v.Features {
|
||||
if len(item) < 1 {
|
||||
violations = append(violations, FieldViolation{Field: "features", Code: "min_item_length"})
|
||||
}
|
||||
}
|
||||
for _, item := range v.Features {
|
||||
if len(item) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "features", Code: "max_item_length"})
|
||||
}
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
@@ -3423,6 +3589,114 @@ func EncodeManifestTunnel(value ManifestTunnel) ([]byte, error) {
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v NativeAuthenticatedSession) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.Username == "" {
|
||||
violations = append(violations, FieldViolation{Field: "username", Code: "required"})
|
||||
}
|
||||
if len(v.Username) < 1 && v.Username != "" {
|
||||
violations = append(violations, FieldViolation{Field: "username", Code: "min_length"})
|
||||
}
|
||||
if len(v.Username) > 256 {
|
||||
violations = append(violations, FieldViolation{Field: "username", Code: "max_length"})
|
||||
}
|
||||
if v.Provider == "" {
|
||||
violations = append(violations, FieldViolation{Field: "provider", Code: "required"})
|
||||
}
|
||||
if len(v.Provider) < 1 && v.Provider != "" {
|
||||
violations = append(violations, FieldViolation{Field: "provider", Code: "min_length"})
|
||||
}
|
||||
if len(v.Provider) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "provider", Code: "max_length"})
|
||||
}
|
||||
if v.Roles == nil {
|
||||
violations = append(violations, FieldViolation{Field: "roles", Code: "required"})
|
||||
}
|
||||
if len(v.Roles) > 16 {
|
||||
violations = append(violations, FieldViolation{Field: "roles", Code: "max_items"})
|
||||
}
|
||||
for _, item := range v.Roles {
|
||||
if len(item) < 1 {
|
||||
violations = append(violations, FieldViolation{Field: "roles", Code: "min_item_length"})
|
||||
}
|
||||
}
|
||||
for _, item := range v.Roles {
|
||||
if len(item) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "roles", Code: "max_item_length"})
|
||||
}
|
||||
}
|
||||
for _, item := range v.Roles {
|
||||
if len(item) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "roles", Code: "max_item_bytes"})
|
||||
}
|
||||
}
|
||||
if v.Role == "" {
|
||||
violations = append(violations, FieldViolation{Field: "role", Code: "required"})
|
||||
}
|
||||
if v.Role != "" && !(v.Role == "user" || v.Role == "admin") {
|
||||
violations = append(violations, FieldViolation{Field: "role", Code: "invalid_value"})
|
||||
}
|
||||
if reflect.DeepEqual(v.NativeIdentity, NativeSessionIdentity{}) {
|
||||
violations = append(violations, FieldViolation{Field: "native_identity", Code: "required"})
|
||||
}
|
||||
if err := v.NativeIdentity.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "native_identity", Code: "invalid_object"})
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeNativeAuthenticatedSession(data []byte) (NativeAuthenticatedSession, error) {
|
||||
var value NativeAuthenticatedSession
|
||||
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["native_identity"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "native_identity", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["provider"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "provider", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["role"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "role", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["roles"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "roles", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["username"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "username", 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 EncodeNativeAuthenticatedSession(value NativeAuthenticatedSession) ([]byte, error) {
|
||||
if err := value.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v NativeCredential) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if len(v.DeviceID) > 128 {
|
||||
@@ -3526,6 +3800,176 @@ func EncodeNativeCredential(value NativeCredential) ([]byte, error) {
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v NativeSessionIdentity) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.ClientDeviceID == "" {
|
||||
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "required"})
|
||||
}
|
||||
if len(v.ClientDeviceID) < 1 && v.ClientDeviceID != "" {
|
||||
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "min_length"})
|
||||
}
|
||||
if len(v.ClientDeviceID) > 128 {
|
||||
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "max_length"})
|
||||
}
|
||||
if v.DeviceKeyID == "" {
|
||||
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "required"})
|
||||
}
|
||||
if len(v.DeviceKeyID) < 1 && v.DeviceKeyID != "" {
|
||||
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "min_length"})
|
||||
}
|
||||
if len(v.DeviceKeyID) > 128 {
|
||||
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "max_length"})
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeNativeSessionIdentity(data []byte) (NativeSessionIdentity, error) {
|
||||
var value NativeSessionIdentity
|
||||
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["client_device_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "client_device_id", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["device_key_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "device_key_id", 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 EncodeNativeSessionIdentity(value NativeSessionIdentity) ([]byte, error) {
|
||||
if err := value.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v NativeTunnelCredential) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.ClientDeviceID == "" {
|
||||
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "required"})
|
||||
}
|
||||
if len(v.ClientDeviceID) < 1 && v.ClientDeviceID != "" {
|
||||
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "min_length"})
|
||||
}
|
||||
if len(v.ClientDeviceID) > 128 {
|
||||
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "max_length"})
|
||||
}
|
||||
if v.DeviceKeyID == "" {
|
||||
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "required"})
|
||||
}
|
||||
if len(v.DeviceKeyID) < 1 && v.DeviceKeyID != "" {
|
||||
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "min_length"})
|
||||
}
|
||||
if len(v.DeviceKeyID) > 128 {
|
||||
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "max_length"})
|
||||
}
|
||||
if v.CertificateChainPem == "" {
|
||||
violations = append(violations, FieldViolation{Field: "certificate_chain_pem", Code: "required"})
|
||||
}
|
||||
if len(v.CertificateChainPem) < 1 && v.CertificateChainPem != "" {
|
||||
violations = append(violations, FieldViolation{Field: "certificate_chain_pem", Code: "min_length"})
|
||||
}
|
||||
if len(v.CertificateChainPem) > 65536 {
|
||||
violations = append(violations, FieldViolation{Field: "certificate_chain_pem", Code: "max_length"})
|
||||
}
|
||||
if v.TrustBundlePem == "" {
|
||||
violations = append(violations, FieldViolation{Field: "trust_bundle_pem", Code: "required"})
|
||||
}
|
||||
if len(v.TrustBundlePem) < 1 && v.TrustBundlePem != "" {
|
||||
violations = append(violations, FieldViolation{Field: "trust_bundle_pem", Code: "min_length"})
|
||||
}
|
||||
if len(v.TrustBundlePem) > 65536 {
|
||||
violations = append(violations, FieldViolation{Field: "trust_bundle_pem", Code: "max_length"})
|
||||
}
|
||||
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 len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeNativeTunnelCredential(data []byte) (NativeTunnelCredential, error) {
|
||||
var value NativeTunnelCredential
|
||||
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["certificate_chain_pem"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "certificate_chain_pem", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["client_device_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "client_device_id", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["device_key_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "device_key_id", 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["trust_bundle_pem"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "trust_bundle_pem", 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 EncodeNativeTunnelCredential(value NativeTunnelCredential) ([]byte, error) {
|
||||
if err := value.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v PageInfo) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.Limit == 0 {
|
||||
@@ -3875,6 +4319,16 @@ func (v ProviderState) Validate() error {
|
||||
if len(v.Channels) > 8 {
|
||||
violations = append(violations, FieldViolation{Field: "channels", Code: "max_items"})
|
||||
}
|
||||
for _, item := range v.Channels {
|
||||
if len(item) < 1 {
|
||||
violations = append(violations, FieldViolation{Field: "channels", Code: "min_item_length"})
|
||||
}
|
||||
}
|
||||
for _, item := range v.Channels {
|
||||
if len(item) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "channels", Code: "max_item_length"})
|
||||
}
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
@@ -4761,12 +5215,6 @@ func (v SessionRequest) Validate() error {
|
||||
if len(v.IdempotencyKey) > 256 {
|
||||
violations = append(violations, FieldViolation{Field: "idempotency_key", Code: "max_length"})
|
||||
}
|
||||
if reflect.DeepEqual(v.PolicySnapshot, AllocationPolicy{}) {
|
||||
violations = append(violations, FieldViolation{Field: "policy_snapshot", Code: "required"})
|
||||
}
|
||||
if err := v.PolicySnapshot.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "policy_snapshot", Code: "invalid_object"})
|
||||
}
|
||||
if v.RequestedDisplayMode != nil {
|
||||
if err := v.RequestedDisplayMode.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "requested_display_mode", Code: "invalid_object"})
|
||||
@@ -4796,9 +5244,6 @@ func DecodeSessionRequest(data []byte) (SessionRequest, error) {
|
||||
if raw, ok := fields["idempotency_key"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "idempotency_key", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["policy_snapshot"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "policy_snapshot", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["pool_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "pool_id", Code: "required"}}}
|
||||
}
|
||||
@@ -5057,12 +5502,22 @@ func (v VersionNegotiation) Validate() error {
|
||||
if len(v.SupportedVersions) > 3 {
|
||||
violations = append(violations, FieldViolation{Field: "supported_versions", Code: "max_items"})
|
||||
}
|
||||
for _, item := range v.SupportedVersions {
|
||||
if len(item) > 16 {
|
||||
violations = append(violations, FieldViolation{Field: "supported_versions", Code: "max_item_length"})
|
||||
}
|
||||
}
|
||||
if v.Features == nil {
|
||||
violations = append(violations, FieldViolation{Field: "features", Code: "required"})
|
||||
}
|
||||
if len(v.Features) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "features", Code: "max_items"})
|
||||
}
|
||||
for _, item := range v.Features {
|
||||
if len(item) > 64 {
|
||||
violations = append(violations, FieldViolation{Field: "features", Code: "max_item_length"})
|
||||
}
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user