lib/api_signature/validator.rb in api_signature-0.1.5 vs lib/api_signature/validator.rb in api_signature-1.0.0

- old
+ new

@@ -1,39 +1,98 @@ # frozen_string_literal: true module ApiSignature + # Validate a request + # + # request = { + # http_method: 'PUT', + # url: 'https://domain.com', + # headers: { + # 'Authorization' => 'API-HMAC-SHA256 Credential=access_key/20191227/api_request...', + # 'Host' => 'example.com, + # 'X-Content-Sha256' => '...', + # 'X-Datetime' => '2019-12-27T09:13:14.873+0000' + # }, + # body: 'body' + # } + # validator = ApiSignature::Validator.new(request, uri_escape_path: true) + # validator.access_key # get key from request headers + # validator.valid?('secret_key') + # class Validator - attr_reader :timestamp + attr_reader :request - def initialize(options) + def initialize(request, options = {}) + @request = request @options = options - @timestamp = Time.at(@options[:timestamp].to_i).utc end - def valid?(signature, secret) - return false if signature.blank? || secret.blank? || expired? - generator.generate_signature(secret) == signature + def access_key + return unless valid_credential? + + @access_key ||= auth_header.credential.split('/')[0] end - def expired? - !alive? + def signed_headers + @signed_headers ||= headers.slice(*auth_header.signed_headers) end + # Validate a signature. Returns boolean + # + # validator.valid?('secret_key_here') + # + # @param [String] secret key + # + def valid?(secret_key) + valid_authorization? && valid_timestamp? && valid_signature?(secret_key) + end + + def valid_authorization? + valid_credential? && !auth_header.signature.nil? + end + + def valid_credential? + !auth_header.credential.nil? + end + + def valid_timestamp? + timestamp && ttl_range.cover?(timestamp.to_time) + end + + def valid_signature?(secret_key) + return false unless secret_key + + signer = Signer.new(access_key, secret_key, @options) + data = signer.sign_request(request) + + Utils.secure_compare( + auth_header.signature, + data.signature + ) + end + private - def generator - @generator ||= Generator.new(@options) + def auth_header + @auth_header ||= AuthHeader.new(headers[signature_header_name]) end - def alive? - alive_timerange.cover?(timestamp) + def signature_header_name + @options[:signature_header] || ApiSignature.configuration.signature_header end - def alive_timerange - @alive_timerange ||= (ttl.ago..ttl.from_now) + def timestamp + @timestamp ||= Utils.safe_parse_datetime(headers['x-datetime']) end - def ttl - ApiSignature.signature_ttl + def headers + @headers ||= Utils.normalize_keys(request[:headers]) + end + + def ttl_range + to = Time.now.utc + from = to - ApiSignature.configuration.signature_ttl + + from..to end end end