Module: Ruby::Rego::Builtins::Providers::Aws
- Extended by:
- RegistryHelpers
- Defined in:
- lib/ruby/rego/builtins/providers/aws.rb
Overview
providers.aws.sign_req(request, aws_config, time_ns): sign an HTTP request with AWS Signature
Version 4, matching OPA’s topdown builtinAWSSigV4SignReq (internal/providers/aws/signing_v4.go).
Deterministic: the signing time is the time_ns argument, not a clock. Returns the request copied
with its headers replaced by the original (single-valued) headers plus the signing headers
(Authorization, host, x-amz-date, and conditionally x-amz-content-sha256 / x-amz-security-token).
OPA’s SigV4 deviates from the AWS spec in three byte-affecting ways the port reproduces: the canonical query is RawQuery VERBATIM (not sorted/re-encoded); header values are signed verbatim (not trimmed); and the canonical URI is url.EscapedPath() (user %-encoding preserved, never double-encoded). The body is hashed as raw_body (string, wins over body) or json.Marshal(body) (reusing Codecs.canonical_json — sorted keys + Go HTML escaping). With the arbitrary-precision number model that canonical JSON is byte-exact with OPA, so a body number written as a Rego literal (1.50, 1e10) hashes identically to OPA; only a number reaching the body from parsed JSON input still collapses through Float (the deferred input-precision gap), which exact-byte callers avoid by passing raw_body.
Two documented divergences, both on inputs real callers don’t hit: (1) two request headers whose names differ ONLY in case (e.g. “X-Foo” and “x-foo”) — OPA’s signature is nondeterministic there (Go map iteration order decides which lowercased value wins), so the gem’s deterministic choice matches only one of OPA’s branches; (2) an invalid-UTF-8 url or header key — the gem returns undefined (like uri.parse) rather than signing, to stay total.
Any precondition failure is undefined (the registry maps BuiltinArgumentError -> undefined): a non-object request/config; a request key outside http.send’s allowed set or a missing/non-string method/url; an unparseable url; an aws_config missing one of the four required string keys (empty strings are accepted) or holding a non-string for one; a non-integer / int64-overflowing time_ns; or a non-boolean disable_payload_signing. rubocop:disable Metrics/ModuleLength – a faithful step-by-step port of OPA’s SigV4 signer.
Constant Summary collapse
- AWS_FUNCTIONS =
{ "providers.aws.sign_req" => { arity: 3, handler: :sign_req } }.freeze
- CONTEXT =
"providers.aws.sign_req"- REQUIRED_CONFIG =
aws_config keys that must be present and string-typed (empty strings allowed).
%w[aws_access_key aws_secret_access_key aws_service aws_region].freeze
- ALLOWED_REQUEST_KEYS =
The http.send request keys OPA’s validateHTTPRequestOperand accepts; any other key is an error.
%w[ method url body enable_redirect force_json_decode force_yaml_decode headers raw_body timeout tls_use_system_certs tls_ca_cert tls_ca_cert_file tls_ca_cert_env_variable tls_client_cert tls_client_cert_file tls_client_cert_env_variable tls_client_key tls_client_key_file tls_client_key_env_variable tls_server_name tls_insecure_skip_verify cache force_cache force_cache_duration_seconds raise_error caching_mode max_retry_attempts cache_ignored_headers ].freeze
- IGNORED_HEADERS =
Headers excluded from signing regardless of the request (awsSigv4IgnoredHeaders), lowercased.
%w[authorization user-agent x-amzn-trace-id].freeze
- PAYLOAD_HASH_SERVICES =
Services for which OPA emits the x-amz-content-sha256 header (string-equal, case-sensitive).
%w[s3 glacier].freeze
- ALGORITHM =
"AWS4-HMAC-SHA256"- AMZ_DATE_FORMAT =
"%Y%m%dT%H%M%SZ"- DATE_FORMAT =
"%Y%m%d"- INT64_MIN =
-(2**63)
- INT64_MAX =
(2**63) - 1
Class Method Summary collapse
-
.register! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
-
.sign_req(request_value, config_value, time_ns_value) ⇒ Ruby::Rego::Value
The signed request object, or undefined.
Methods included from RegistryHelpers
Class Method Details
.register! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
72 73 74 75 76 |
# File 'lib/ruby/rego/builtins/providers/aws.rb', line 72 def self.register! registry = BuiltinRegistry.instance register_configured_functions(registry, AWS_FUNCTIONS) registry end |
.sign_req(request_value, config_value, time_ns_value) ⇒ Ruby::Rego::Value
Returns the signed request object, or undefined.
84 85 86 87 88 89 90 91 92 93 |
# File 'lib/ruby/rego/builtins/providers/aws.rb', line 84 def self.sign_req(request_value, config_value, time_ns_value) request = object_or_undefined(request_value) config = object_or_undefined(config_value) time = signing_time(time_ns_value) validate_request_keys!(request) creds = credentials(config) signed_headers = sign(request, creds, time) output_request(request, signed_headers) end |