Module: DebugBundle::TriggerToken
- Defined in:
- lib/debugbundle/trigger_token.rb
Constant Summary collapse
- HEADER_NAME =
'x-debugbundle-probe-trigger'- QUERY_PARAMETER_NAME =
'_debug_probe'- TOKEN_PREFIX =
'dbundle_probe_'
Class Method Summary collapse
- .base64url_decode(value) ⇒ Object
- .base64url_decode_bytes(value) ⇒ Object
- .decode_payload(payload_segment) ⇒ Object
- .extract_map_value(mapping, target_key, case_insensitive:) ⇒ Object
- .extract_token(request) ⇒ Object
- .resolve_request_directives(request:, trigger_token_key:) ⇒ Object
- .secure_compare(left, right) ⇒ Object
- .split_token(token) ⇒ Object
- .valid_signature?(payload_segment, signature_segment, trigger_token_key) ⇒ Boolean
Class Method Details
.base64url_decode(value) ⇒ Object
108 109 110 111 |
# File 'lib/debugbundle/trigger_token.rb', line 108 def self.base64url_decode(value) decoded = base64url_decode_bytes(value) decoded&.force_encoding('UTF-8') end |
.base64url_decode_bytes(value) ⇒ Object
113 114 115 116 117 118 119 120 |
# File 'lib/debugbundle/trigger_token.rb', line 113 def self.base64url_decode_bytes(value) padded = value.dup remainder = padded.length % 4 padded += '=' * (4 - remainder) if remainder.positive? Base64.urlsafe_decode64(padded) rescue ArgumentError nil end |
.decode_payload(payload_segment) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/debugbundle/trigger_token.rb', line 54 def self.decode_payload(payload_segment) decoded = base64url_decode(payload_segment) return nil unless decoded parsed = JSON.parse(decoded) return nil unless parsed.is_a?(Hash) activation_id = parsed['activation_id'] label_pattern = parsed['label_pattern'] service = parsed['service'] environment = parsed['environment'] expires_at = Time.iso8601(parsed['trigger_expires_at']) return nil if [activation_id, label_pattern, service, environment].any? { |value| value.to_s.empty? } { activation_id: activation_id, label_pattern: label_pattern, service: service, environment: environment, expires_at: expires_at } rescue JSON::ParserError, ArgumentError, TypeError nil end |
.extract_map_value(mapping, target_key, case_insensitive:) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/debugbundle/trigger_token.rb', line 96 def self.extract_map_value(mapping, target_key, case_insensitive:) mapping.each do |key, value| matches = case_insensitive ? key.to_s.downcase == target_key.downcase : key.to_s == target_key next unless matches return value if value.is_a?(String) && !value.empty? return value.first if value.is_a?(Array) && value.first.is_a?(String) && !value.first.empty? end nil end |
.extract_token(request) ⇒ Object
38 39 40 41 42 43 44 45 |
# File 'lib/debugbundle/trigger_token.rb', line 38 def self.extract_token(request) headers = request[:headers] || request['headers'] || {} header_token = extract_map_value(headers, HEADER_NAME, case_insensitive: true) return header_token if header_token query = request[:query] || request['query'] || {} extract_map_value(query, QUERY_PARAMETER_NAME, case_insensitive: false) end |
.resolve_request_directives(request:, trigger_token_key:) ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/debugbundle/trigger_token.rb', line 13 def self.resolve_request_directives(request:, trigger_token_key:) return [] if request.nil? || trigger_token_key.to_s.empty? token = extract_token(request) return [] unless token&.start_with?(TOKEN_PREFIX) payload_segment, signature_segment = split_token(token.delete_prefix(TOKEN_PREFIX)) return [] unless payload_segment && signature_segment return [] unless valid_signature?(payload_segment, signature_segment, trigger_token_key) payload = decode_payload(payload_segment) return [] unless payload return [] if payload[:expires_at] <= Time.now.utc [ RemoteConfig::Directive.new( id: payload[:activation_id], label_pattern: payload[:label_pattern], service: payload[:service], environment: payload[:environment], expires_at: payload[:expires_at] ) ] end |
.secure_compare(left, right) ⇒ Object
88 89 90 91 92 93 94 |
# File 'lib/debugbundle/trigger_token.rb', line 88 def self.secure_compare(left, right) result = 0 left.bytes.zip(right.bytes) do |left_byte, right_byte| result |= left_byte ^ right_byte end result.zero? end |
.split_token(token) ⇒ Object
47 48 49 50 51 52 |
# File 'lib/debugbundle/trigger_token.rb', line 47 def self.split_token(token) separator_index = token.index('.') return [nil, nil] unless separator_index&.positive? && separator_index < (token.length - 1) [token[0...separator_index], token[(separator_index + 1)..]] end |
.valid_signature?(payload_segment, signature_segment, trigger_token_key) ⇒ Boolean
80 81 82 83 84 85 86 |
# File 'lib/debugbundle/trigger_token.rb', line 80 def self.valid_signature?(payload_segment, signature_segment, trigger_token_key) expected = OpenSSL::HMAC.digest('sha256', trigger_token_key, payload_segment) actual = base64url_decode_bytes(signature_segment) return false unless actual && actual.bytesize == expected.bytesize secure_compare(expected, actual) end |