feat(protocol): include Apollo client identity in session work
Verify Protocol / verify (push) Canceled after 0s
Verify Protocol / module (push) Successful in 2m35s

This commit is contained in:
sechmachine
2026-07-29 11:28:25 +07:00
parent 357e5e0dbc
commit 36f6edffca
8 changed files with 36 additions and 10 deletions
+14 -1
View File
@@ -12,7 +12,7 @@ import (
"time" "time"
) )
const SchemaSHA256 = "c075c6fc472ce858337c3ad6c4b2f4dcfa88446d5471afb46c2f6931cbcf1b1d" const SchemaSHA256 = "792abfb9cfe70e79911d499d76c009ab848713278bc240b88576df520580e480"
const ProtocolVersion = "1.0.0" const ProtocolVersion = "1.0.0"
const CurrentWireVersion = "1" const CurrentWireVersion = "1"
const NMinus1WireVersion = "0" const NMinus1WireVersion = "0"
@@ -250,6 +250,7 @@ type ProviderSessionWork struct {
ProviderIdentity string `json:"provider_identity"` ProviderIdentity string `json:"provider_identity"`
PolicyVersionID string `json:"policy_version_id"` PolicyVersionID string `json:"policy_version_id"`
ApplicationID string `json:"application_id"` ApplicationID string `json:"application_id"`
ClientID string `json:"client_id"`
ManagementHost string `json:"management_host"` ManagementHost string `json:"management_host"`
ManagementPort int64 `json:"management_port"` ManagementPort int64 `json:"management_port"`
StreamHost string `json:"stream_host"` StreamHost string `json:"stream_host"`
@@ -3009,6 +3010,15 @@ func (v ProviderSessionWork) Validate() error {
if len(v.ApplicationID) > 128 { if len(v.ApplicationID) > 128 {
violations = append(violations, FieldViolation{Field: "application_id", Code: "max_length"}) violations = append(violations, FieldViolation{Field: "application_id", Code: "max_length"})
} }
if v.ClientID == "" {
violations = append(violations, FieldViolation{Field: "client_id", Code: "required"})
}
if len(v.ClientID) < 1 && v.ClientID != "" {
violations = append(violations, FieldViolation{Field: "client_id", Code: "min_length"})
}
if len(v.ClientID) > 128 {
violations = append(violations, FieldViolation{Field: "client_id", Code: "max_length"})
}
if v.ManagementHost == "" { if v.ManagementHost == "" {
violations = append(violations, FieldViolation{Field: "management_host", Code: "required"}) violations = append(violations, FieldViolation{Field: "management_host", Code: "required"})
} }
@@ -3093,6 +3103,9 @@ func DecodeProviderSessionWork(data []byte) (ProviderSessionWork, error) {
if raw, ok := fields["client_certificate_pem"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) { if raw, ok := fields["client_certificate_pem"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "client_certificate_pem", Code: "required"}}} return value, ValidationError{Violations: []FieldViolation{{Field: "client_certificate_pem", Code: "required"}}}
} }
if raw, ok := fields["client_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "client_id", Code: "required"}}}
}
if raw, ok := fields["client_private_key_pem"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) { if raw, ok := fields["client_private_key_pem"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "client_private_key_pem", Code: "required"}}} return value, ValidationError{Violations: []FieldViolation{{Field: "client_private_key_pem", Code: "required"}}}
} }
+1 -1
View File
@@ -14,5 +14,5 @@
}, },
"generator_sha256": "e9c6ee1541585fcb00dcc5e94a5a6d93dbe3a719a5c545f31e5eda268f2638ab", "generator_sha256": "e9c6ee1541585fcb00dcc5e94a5a6d93dbe3a719a5c545f31e5eda268f2638ab",
"protocol_version": "1.0.0", "protocol_version": "1.0.0",
"schema_sha256": "c075c6fc472ce858337c3ad6c4b2f4dcfa88446d5471afb46c2f6931cbcf1b1d" "schema_sha256": "792abfb9cfe70e79911d499d76c009ab848713278bc240b88576df520580e480"
} }
Binary file not shown.
+8 -3
View File
@@ -1,6 +1,6 @@
// Code generated by tools/generate.py; DO NOT EDIT. // Code generated by tools/generate.py; DO NOT EDIT.
#![allow(non_snake_case)] #![allow(non_snake_case)]
pub const SCHEMA_SHA256: &str = "c075c6fc472ce858337c3ad6c4b2f4dcfa88446d5471afb46c2f6931cbcf1b1d"; pub const SCHEMA_SHA256: &str = "792abfb9cfe70e79911d499d76c009ab848713278bc240b88576df520580e480";
pub const CURRENT_WIRE_VERSION: &str = "1"; pub const CURRENT_WIRE_VERSION: &str = "1";
pub const N_MINUS_1_WIRE_VERSION: &str = "0"; pub const N_MINUS_1_WIRE_VERSION: &str = "0";
pub const N_MINUS_2_WIRE_VERSION: &str = "-1"; pub const N_MINUS_2_WIRE_VERSION: &str = "-1";
@@ -993,6 +993,7 @@ pub struct ProviderSessionWork {
providerIdentity: String, providerIdentity: String,
policyVersionId: String, policyVersionId: String,
applicationId: String, applicationId: String,
clientId: String,
managementHost: String, managementHost: String,
managementPort: i64, managementPort: i64,
streamHost: String, streamHost: String,
@@ -1003,8 +1004,8 @@ pub struct ProviderSessionWork {
} }
impl ProviderSessionWork { impl ProviderSessionWork {
pub fn new(version: String, sessionId: String, gatewayId: String, reconnectSequence: i64, expiresAt: String, providerProfile: String, providerIdentity: String, policyVersionId: String, applicationId: String, managementHost: String, managementPort: i64, streamHost: String, streamPort: i64, clientCertificatePem: String, clientPrivateKeyPem: String, serverCertificatePem: String) -> Result<Self, ValidationError> { pub fn new(version: String, sessionId: String, gatewayId: String, reconnectSequence: i64, expiresAt: String, providerProfile: String, providerIdentity: String, policyVersionId: String, applicationId: String, clientId: String, managementHost: String, managementPort: i64, streamHost: String, streamPort: i64, clientCertificatePem: String, clientPrivateKeyPem: String, serverCertificatePem: String) -> Result<Self, ValidationError> {
let value = Self { version, sessionId, gatewayId, reconnectSequence, expiresAt, providerProfile, providerIdentity, policyVersionId, applicationId, managementHost, managementPort, streamHost, streamPort, clientCertificatePem, clientPrivateKeyPem, serverCertificatePem }; let value = Self { version, sessionId, gatewayId, reconnectSequence, expiresAt, providerProfile, providerIdentity, policyVersionId, applicationId, clientId, managementHost, managementPort, streamHost, streamPort, clientCertificatePem, clientPrivateKeyPem, serverCertificatePem };
value.validate()?; value.validate()?;
Ok(value) Ok(value)
} }
@@ -1028,6 +1029,9 @@ impl ProviderSessionWork {
if self.applicationId.is_empty() { return Err(ValidationError::new("application_id", "required")); } if self.applicationId.is_empty() { return Err(ValidationError::new("application_id", "required")); }
if !self.applicationId.is_empty() && self.applicationId.len() < 1 { return Err(ValidationError::new("application_id", "min_length")); } if !self.applicationId.is_empty() && self.applicationId.len() < 1 { return Err(ValidationError::new("application_id", "min_length")); }
if self.applicationId.len() > 128 { return Err(ValidationError::new("application_id", "max_length")); } if self.applicationId.len() > 128 { return Err(ValidationError::new("application_id", "max_length")); }
if self.clientId.is_empty() { return Err(ValidationError::new("client_id", "required")); }
if !self.clientId.is_empty() && self.clientId.len() < 1 { return Err(ValidationError::new("client_id", "min_length")); }
if self.clientId.len() > 128 { return Err(ValidationError::new("client_id", "max_length")); }
if self.managementHost.is_empty() { return Err(ValidationError::new("management_host", "required")); } if self.managementHost.is_empty() { return Err(ValidationError::new("management_host", "required")); }
if !self.managementHost.is_empty() && self.managementHost.len() < 1 { return Err(ValidationError::new("management_host", "min_length")); } if !self.managementHost.is_empty() && self.managementHost.len() < 1 { return Err(ValidationError::new("management_host", "min_length")); }
if self.managementHost.len() > 256 { return Err(ValidationError::new("management_host", "max_length")); } if self.managementHost.len() > 256 { return Err(ValidationError::new("management_host", "max_length")); }
@@ -1058,6 +1062,7 @@ impl ProviderSessionWork {
pub fn providerIdentity(&self) -> &String { &self.providerIdentity } pub fn providerIdentity(&self) -> &String { &self.providerIdentity }
pub fn policyVersionId(&self) -> &String { &self.policyVersionId } pub fn policyVersionId(&self) -> &String { &self.policyVersionId }
pub fn applicationId(&self) -> &String { &self.applicationId } pub fn applicationId(&self) -> &String { &self.applicationId }
pub fn clientId(&self) -> &String { &self.clientId }
pub fn managementHost(&self) -> &String { &self.managementHost } pub fn managementHost(&self) -> &String { &self.managementHost }
pub fn managementPort(&self) -> &i64 { &self.managementPort } pub fn managementPort(&self) -> &i64 { &self.managementPort }
pub fn streamHost(&self) -> &String { &self.streamHost } pub fn streamHost(&self) -> &String { &self.streamHost }
+9 -3
View File
@@ -1,7 +1,7 @@
// Code generated by tools/generate.py; DO NOT EDIT. // Code generated by tools/generate.py; DO NOT EDIT.
import Foundation import Foundation
public typealias JSONObject = [String: String] public typealias JSONObject = [String: String]
public let schemaSHA256 = "c075c6fc472ce858337c3ad6c4b2f4dcfa88446d5471afb46c2f6931cbcf1b1d" public let schemaSHA256 = "792abfb9cfe70e79911d499d76c009ab848713278bc240b88576df520580e480"
public let currentWireVersion = "1" public let currentWireVersion = "1"
public let nMinus1WireVersion = "0" public let nMinus1WireVersion = "0"
public let nMinus2WireVersion = "-1" public let nMinus2WireVersion = "-1"
@@ -1320,6 +1320,7 @@ public struct ProviderSessionWork: Codable, Equatable {
public let providerIdentity: String public let providerIdentity: String
public let policyVersionId: String public let policyVersionId: String
public let applicationId: String public let applicationId: String
public let clientId: String
public let managementHost: String public let managementHost: String
public let managementPort: Int64 public let managementPort: Int64
public let streamHost: String public let streamHost: String
@@ -1337,6 +1338,7 @@ public struct ProviderSessionWork: Codable, Equatable {
case providerIdentity = "provider_identity" case providerIdentity = "provider_identity"
case policyVersionId = "policy_version_id" case policyVersionId = "policy_version_id"
case applicationId = "application_id" case applicationId = "application_id"
case clientId = "client_id"
case managementHost = "management_host" case managementHost = "management_host"
case managementPort = "management_port" case managementPort = "management_port"
case streamHost = "stream_host" case streamHost = "stream_host"
@@ -1346,7 +1348,7 @@ public struct ProviderSessionWork: Codable, Equatable {
case serverCertificatePem = "server_certificate_pem" case serverCertificatePem = "server_certificate_pem"
} }
public init(version: String, sessionId: String, gatewayId: String, reconnectSequence: Int64, expiresAt: String, providerProfile: String, providerIdentity: String, policyVersionId: String, applicationId: String, managementHost: String, managementPort: Int64, streamHost: String, streamPort: Int64, clientCertificatePem: String, clientPrivateKeyPem: String, serverCertificatePem: String) throws { public init(version: String, sessionId: String, gatewayId: String, reconnectSequence: Int64, expiresAt: String, providerProfile: String, providerIdentity: String, policyVersionId: String, applicationId: String, clientId: String, managementHost: String, managementPort: Int64, streamHost: String, streamPort: Int64, clientCertificatePem: String, clientPrivateKeyPem: String, serverCertificatePem: String) throws {
self.version = version self.version = version
self.sessionId = sessionId self.sessionId = sessionId
self.gatewayId = gatewayId self.gatewayId = gatewayId
@@ -1356,6 +1358,7 @@ public struct ProviderSessionWork: Codable, Equatable {
self.providerIdentity = providerIdentity self.providerIdentity = providerIdentity
self.policyVersionId = policyVersionId self.policyVersionId = policyVersionId
self.applicationId = applicationId self.applicationId = applicationId
self.clientId = clientId
self.managementHost = managementHost self.managementHost = managementHost
self.managementPort = managementPort self.managementPort = managementPort
self.streamHost = streamHost self.streamHost = streamHost
@@ -1370,7 +1373,7 @@ public struct ProviderSessionWork: Codable, Equatable {
let all = try decoder.container(keyedBy: AnyCodingKey.self) let all = try decoder.container(keyedBy: AnyCodingKey.self)
for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") } for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") }
let c = try decoder.container(keyedBy: CodingKeys.self) let c = try decoder.container(keyedBy: CodingKeys.self)
try self.init(version: try c.decode(String.self, forKey: .version), sessionId: try c.decode(String.self, forKey: .sessionId), gatewayId: try c.decode(String.self, forKey: .gatewayId), reconnectSequence: try c.decode(Int64.self, forKey: .reconnectSequence), expiresAt: try c.decode(String.self, forKey: .expiresAt), providerProfile: try c.decode(String.self, forKey: .providerProfile), providerIdentity: try c.decode(String.self, forKey: .providerIdentity), policyVersionId: try c.decode(String.self, forKey: .policyVersionId), applicationId: try c.decode(String.self, forKey: .applicationId), managementHost: try c.decode(String.self, forKey: .managementHost), managementPort: try c.decode(Int64.self, forKey: .managementPort), streamHost: try c.decode(String.self, forKey: .streamHost), streamPort: try c.decode(Int64.self, forKey: .streamPort), clientCertificatePem: try c.decode(String.self, forKey: .clientCertificatePem), clientPrivateKeyPem: try c.decode(String.self, forKey: .clientPrivateKeyPem), serverCertificatePem: try c.decode(String.self, forKey: .serverCertificatePem)) try self.init(version: try c.decode(String.self, forKey: .version), sessionId: try c.decode(String.self, forKey: .sessionId), gatewayId: try c.decode(String.self, forKey: .gatewayId), reconnectSequence: try c.decode(Int64.self, forKey: .reconnectSequence), expiresAt: try c.decode(String.self, forKey: .expiresAt), providerProfile: try c.decode(String.self, forKey: .providerProfile), providerIdentity: try c.decode(String.self, forKey: .providerIdentity), policyVersionId: try c.decode(String.self, forKey: .policyVersionId), applicationId: try c.decode(String.self, forKey: .applicationId), clientId: try c.decode(String.self, forKey: .clientId), managementHost: try c.decode(String.self, forKey: .managementHost), managementPort: try c.decode(Int64.self, forKey: .managementPort), streamHost: try c.decode(String.self, forKey: .streamHost), streamPort: try c.decode(Int64.self, forKey: .streamPort), clientCertificatePem: try c.decode(String.self, forKey: .clientCertificatePem), clientPrivateKeyPem: try c.decode(String.self, forKey: .clientPrivateKeyPem), serverCertificatePem: try c.decode(String.self, forKey: .serverCertificatePem))
} }
public func validate() throws { public func validate() throws {
@@ -1394,6 +1397,9 @@ public struct ProviderSessionWork: Codable, Equatable {
if self.applicationId.isEmpty { throw ContractValidationError(field: "application_id", code: "required") } if self.applicationId.isEmpty { throw ContractValidationError(field: "application_id", code: "required") }
if !self.applicationId.isEmpty && self.applicationId.utf8.count < 1 { throw ContractValidationError(field: "application_id", code: "min_length") } if !self.applicationId.isEmpty && self.applicationId.utf8.count < 1 { throw ContractValidationError(field: "application_id", code: "min_length") }
if self.applicationId.utf8.count > 128 { throw ContractValidationError(field: "application_id", code: "max_length") } if self.applicationId.utf8.count > 128 { throw ContractValidationError(field: "application_id", code: "max_length") }
if self.clientId.isEmpty { throw ContractValidationError(field: "client_id", code: "required") }
if !self.clientId.isEmpty && self.clientId.utf8.count < 1 { throw ContractValidationError(field: "client_id", code: "min_length") }
if self.clientId.utf8.count > 128 { throw ContractValidationError(field: "client_id", code: "max_length") }
if self.managementHost.isEmpty { throw ContractValidationError(field: "management_host", code: "required") } if self.managementHost.isEmpty { throw ContractValidationError(field: "management_host", code: "required") }
if !self.managementHost.isEmpty && self.managementHost.utf8.count < 1 { throw ContractValidationError(field: "management_host", code: "min_length") } if !self.managementHost.isEmpty && self.managementHost.utf8.count < 1 { throw ContractValidationError(field: "management_host", code: "min_length") }
if self.managementHost.utf8.count > 256 { throw ContractValidationError(field: "management_host", code: "max_length") } if self.managementHost.utf8.count > 256 { throw ContractValidationError(field: "management_host", code: "max_length") }
+1
View File
@@ -134,6 +134,7 @@ message ProviderSessionWork {
string client_certificate_pem = 14; string client_certificate_pem = 14;
string client_private_key_pem = 15; string client_private_key_pem = 15;
string server_certificate_pem = 16; string server_certificate_pem = 16;
string client_id = 17;
} }
message ChannelFrame { message ChannelFrame {
+2 -1
View File
@@ -438,7 +438,7 @@
"ProviderSessionWork": { "ProviderSessionWork": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": ["version", "session_id", "gateway_id", "reconnect_sequence", "expires_at", "provider_profile", "provider_identity", "policy_version_id", "application_id", "management_host", "management_port", "stream_host", "stream_port", "client_certificate_pem", "client_private_key_pem", "server_certificate_pem"], "required": ["version", "session_id", "gateway_id", "reconnect_sequence", "expires_at", "provider_profile", "provider_identity", "policy_version_id", "application_id", "client_id", "management_host", "management_port", "stream_host", "stream_port", "client_certificate_pem", "client_private_key_pem", "server_certificate_pem"],
"properties": { "properties": {
"version": {"type": "string", "const": "1"}, "version": {"type": "string", "const": "1"},
"session_id": {"type": "string", "minLength": 1, "maxLength": 128}, "session_id": {"type": "string", "minLength": 1, "maxLength": 128},
@@ -449,6 +449,7 @@
"provider_identity": {"type": "string", "minLength": 1, "maxLength": 256}, "provider_identity": {"type": "string", "minLength": 1, "maxLength": 256},
"policy_version_id": {"type": "string", "minLength": 1, "maxLength": 128}, "policy_version_id": {"type": "string", "minLength": 1, "maxLength": 128},
"application_id": {"type": "string", "minLength": 1, "maxLength": 128}, "application_id": {"type": "string", "minLength": 1, "maxLength": 128},
"client_id": {"type": "string", "minLength": 1, "maxLength": 128},
"management_host": {"type": "string", "minLength": 1, "maxLength": 256}, "management_host": {"type": "string", "minLength": 1, "maxLength": 256},
"management_port": {"type": "integer", "minimum": 1, "maximum": 65535}, "management_port": {"type": "integer", "minimum": 1, "maximum": 65535},
"stream_host": {"type": "string", "minLength": 1, "maxLength": 256}, "stream_host": {"type": "string", "minLength": 1, "maxLength": 256},
+1 -1
View File
@@ -114,7 +114,7 @@ func TestSessionAuthorityRejectsProviderRoute(t *testing.T) {
} }
func TestProviderSessionWorkIsStrictAndSessionBound(t *testing.T) { func TestProviderSessionWorkIsStrictAndSessionBound(t *testing.T) {
valid := `{"version":"1","session_id":"session-1","gateway_id":"gateway-1","reconnect_sequence":0,"expires_at":"2099-01-01T00:00:00Z","provider_profile":"apollo","provider_identity":"provider-1","policy_version_id":"policy-1","application_id":"42","management_host":"apollo.test","management_port":47990,"stream_host":"apollo.test","stream_port":47984,"client_certificate_pem":"certificate","client_private_key_pem":"private-key","server_certificate_pem":"server-certificate"}` valid := `{"version":"1","session_id":"session-1","gateway_id":"gateway-1","reconnect_sequence":0,"expires_at":"2099-01-01T00:00:00Z","provider_profile":"apollo","provider_identity":"provider-1","policy_version_id":"policy-1","application_id":"42","client_id":"paired-client-1","management_host":"apollo.test","management_port":47990,"stream_host":"apollo.test","stream_port":47984,"client_certificate_pem":"certificate","client_private_key_pem":"private-key","server_certificate_pem":"server-certificate"}`
if _, err := protocol.DecodeProviderSessionWork([]byte(valid)); err != nil { if _, err := protocol.DecodeProviderSessionWork([]byte(valid)); err != nil {
t.Fatalf("valid provider work rejected: %v", err) t.Fatalf("valid provider work rejected: %v", err)
} }