Skip to content

Commit 9747046

Browse files
committed
Refactor mTLS route options to RFC-0027 compliant flat format
Change from nested mtls_allowed_sources object to flat options: - mtls_allowed_apps: comma-separated app GUIDs (string) - mtls_allowed_spaces: comma-separated space GUIDs (string) - mtls_allowed_orgs: comma-separated org GUIDs (string) - mtls_allow_any: boolean (true/false) This complies with RFC-0027 which requires route options to only use numbers, strings, and boolean values (no nested objects or arrays).
1 parent 936d4dd commit 9747046

2 files changed

Lines changed: 168 additions & 159 deletions

File tree

app/messages/route_options_message.rb

Lines changed: 45 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
module VCAP::CloudController
44
class RouteOptionsMessage < BaseMessage
55
# Register all possible keys upfront so attr_accessors are created
6-
register_allowed_keys %i[loadbalancing hash_header hash_balance mtls_allowed_sources]
6+
# RFC-0027 compliant: only string/number/boolean values (no nested objects/arrays)
7+
# mtls_allowed_apps, mtls_allowed_spaces, mtls_allowed_orgs are comma-separated GUIDs
8+
# mtls_allow_any is a boolean
9+
register_allowed_keys %i[loadbalancing hash_header hash_balance mtls_allowed_apps mtls_allowed_spaces mtls_allowed_orgs mtls_allow_any]
710

811
def self.valid_route_options
912
options = %i[loadbalancing]
1013
options += %i[hash_header hash_balance] if VCAP::CloudController::FeatureFlag.enabled?(:hash_based_routing)
11-
options += %i[mtls_allowed_sources] if VCAP::CloudController::FeatureFlag.enabled?(:app_to_app_mtls_routing)
14+
options += %i[mtls_allowed_apps mtls_allowed_spaces mtls_allowed_orgs mtls_allow_any] if VCAP::CloudController::FeatureFlag.enabled?(:app_to_app_mtls_routing)
1215
options.freeze
1316
end
1417

@@ -86,98 +89,93 @@ def validate_hash_options_with_loadbalancing
8689
end
8790

8891
def mtls_allowed_sources_options_are_valid
89-
# Only validate mtls_allowed_sources when the feature flag is enabled
90-
# If disabled, route_options_are_valid will already report it as unknown field
92+
# Only validate mtls options when the feature flag is enabled
93+
# If disabled, route_options_are_valid will already report them as unknown fields
9194
return unless VCAP::CloudController::FeatureFlag.enabled?(:app_to_app_mtls_routing)
92-
return if mtls_allowed_sources.blank?
9395

94-
validate_mtls_allowed_sources_structure
95-
validate_mtls_allowed_sources_any_exclusivity
96-
validate_mtls_allowed_sources_guids_exist
96+
validate_mtls_string_types
97+
validate_mtls_allow_any_type
98+
validate_mtls_allow_any_exclusivity
99+
validate_mtls_guids_exist
97100
end
98101

99102
private
100103

101-
# Normalize mtls_allowed_sources to use string keys (Rails may parse JSON with symbol keys)
102-
def normalized_mtls_allowed_sources
103-
@normalized_mtls_allowed_sources ||= mtls_allowed_sources.is_a?(Hash) ? mtls_allowed_sources.transform_keys(&:to_s) : mtls_allowed_sources
104-
end
105-
106-
def validate_mtls_allowed_sources_structure
107-
unless mtls_allowed_sources.is_a?(Hash)
108-
errors.add(:mtls_allowed_sources, 'must be an object')
109-
return
110-
end
104+
# Parse comma-separated GUIDs into an array
105+
def parse_guid_list(value)
106+
return [] if value.blank?
111107

112-
valid_keys = %w[apps spaces orgs any]
113-
invalid_keys = normalized_mtls_allowed_sources.keys - valid_keys
114-
errors.add(:mtls_allowed_sources, "contains invalid keys: #{invalid_keys.join(', ')}") if invalid_keys.any?
108+
value.to_s.split(',').map(&:strip).reject(&:empty?)
109+
end
115110

116-
# Validate types
117-
%w[apps spaces orgs].each do |key|
118-
next unless normalized_mtls_allowed_sources[key].present?
111+
def validate_mtls_string_types
112+
# These should be strings (comma-separated GUIDs) per RFC-0027
113+
%i[mtls_allowed_apps mtls_allowed_spaces mtls_allowed_orgs].each do |key|
114+
value = public_send(key)
115+
next if value.blank?
119116

120-
unless normalized_mtls_allowed_sources[key].is_a?(Array) && normalized_mtls_allowed_sources[key].all? { |v| v.is_a?(String) }
121-
errors.add(:mtls_allowed_sources, "#{key} must be an array of strings")
117+
unless value.is_a?(String)
118+
errors.add(key, 'must be a string of comma-separated GUIDs')
122119
end
123120
end
121+
end
124122

125-
return unless normalized_mtls_allowed_sources['any'].present? && ![true, false].include?(normalized_mtls_allowed_sources['any'])
123+
def validate_mtls_allow_any_type
124+
return if mtls_allow_any.nil?
126125

127-
errors.add(:mtls_allowed_sources, 'any must be a boolean')
126+
unless [true, false, 'true', 'false'].include?(mtls_allow_any)
127+
errors.add(:mtls_allow_any, 'must be a boolean (true or false)')
128+
end
128129
end
129130

130-
def validate_mtls_allowed_sources_any_exclusivity
131-
return unless mtls_allowed_sources.is_a?(Hash)
132-
133-
has_any = normalized_mtls_allowed_sources['any'] == true
134-
has_lists = %w[apps spaces orgs].any? { |key| normalized_mtls_allowed_sources[key].present? && normalized_mtls_allowed_sources[key].any? }
131+
def validate_mtls_allow_any_exclusivity
132+
allow_any = mtls_allow_any == true || mtls_allow_any == 'true'
133+
has_specific = [mtls_allowed_apps, mtls_allowed_spaces, mtls_allowed_orgs].any?(&:present?)
135134

136-
return unless has_any && has_lists
135+
return unless allow_any && has_specific
137136

138-
errors.add(:mtls_allowed_sources, 'any is mutually exclusive with apps, spaces, and orgs')
137+
errors.add(:mtls_allow_any, 'is mutually exclusive with mtls_allowed_apps, mtls_allowed_spaces, and mtls_allowed_orgs')
139138
end
140139

141-
def validate_mtls_allowed_sources_guids_exist
142-
return unless mtls_allowed_sources.is_a?(Hash)
143-
return if errors[:mtls_allowed_sources].any? # Skip if already invalid
140+
def validate_mtls_guids_exist
141+
return if errors.any? # Skip if already invalid
144142

145143
validate_app_guids_exist
146144
validate_space_guids_exist
147145
validate_org_guids_exist
148146
end
149147

150148
def validate_app_guids_exist
151-
app_guids = normalized_mtls_allowed_sources['apps']
152-
return if app_guids.blank?
149+
app_guids = parse_guid_list(mtls_allowed_apps)
150+
return if app_guids.empty?
153151

154152
existing_guids = AppModel.where(guid: app_guids).select_map(:guid)
155153
missing_guids = app_guids - existing_guids
156154
return if missing_guids.empty?
157155

158-
errors.add(:mtls_allowed_sources, "apps contains non-existent app GUIDs: #{missing_guids.join(', ')}")
156+
errors.add(:mtls_allowed_apps, "contains non-existent app GUIDs: #{missing_guids.join(', ')}")
159157
end
160158

161159
def validate_space_guids_exist
162-
space_guids = normalized_mtls_allowed_sources['spaces']
163-
return if space_guids.blank?
160+
space_guids = parse_guid_list(mtls_allowed_spaces)
161+
return if space_guids.empty?
164162

165163
existing_guids = Space.where(guid: space_guids).select_map(:guid)
166164
missing_guids = space_guids - existing_guids
167165
return if missing_guids.empty?
168166

169-
errors.add(:mtls_allowed_sources, "spaces contains non-existent space GUIDs: #{missing_guids.join(', ')}")
167+
errors.add(:mtls_allowed_spaces, "contains non-existent space GUIDs: #{missing_guids.join(', ')}")
170168
end
171169

172170
def validate_org_guids_exist
173-
org_guids = normalized_mtls_allowed_sources['orgs']
174-
return if org_guids.blank?
171+
org_guids = parse_guid_list(mtls_allowed_orgs)
172+
return if org_guids.empty?
175173

176174
existing_guids = Organization.where(guid: org_guids).select_map(:guid)
177175
missing_guids = org_guids - existing_guids
178176
return if missing_guids.empty?
179177

180-
errors.add(:mtls_allowed_sources, "orgs contains non-existent organization GUIDs: #{missing_guids.join(', ')}")
178+
errors.add(:mtls_allowed_orgs, "contains non-existent organization GUIDs: #{missing_guids.join(', ')}")
181179
end
182180
end
183181
end

0 commit comments

Comments
 (0)