-
Notifications
You must be signed in to change notification settings - Fork 147
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Unlike other HTTP client libraries, Faraday does not expose request objets too easily. Instead, it provides a framework for configuring connections. Authorization in Faraday is meant to be handled through the use of middlewares. By convention, Faraday middlewares are defined under the Faraday namespace, even when they’re not official. Like other middlewares, requiring `faraday/api_auth` registers the middleware into Faraday with a name. Since that side effect depends on the presence of Faraday, it cannot be part of ApiAuth directly. The previously-existing Faraday integration still exists for whoever managed to use it, though I suggest deprecating it.
- Loading branch information
Showing
9 changed files
with
370 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
module ApiAuth | ||
module RequestDrivers # :nodoc: | ||
# Internally, Faraday uses the class Faraday::Env to represent requests. The class is not meant | ||
# to be directly exposed to users, but this is what Faraday middlewares work with. See | ||
# <https://lostisland.github.io/faraday/middleware/>. | ||
class FaradayEnv | ||
include ApiAuth::Helpers | ||
|
||
def initialize(env) | ||
@env = env | ||
end | ||
|
||
def set_auth_header(header) | ||
@env.request_headers['Authorization'] = header | ||
@env | ||
end | ||
|
||
def calculated_hash | ||
sha256_base64digest(body) | ||
end | ||
|
||
def populate_content_hash | ||
return unless %w[POST PUT PATCH].include?(http_method) | ||
|
||
@env.request_headers['X-Authorization-Content-SHA256'] = calculated_hash | ||
end | ||
|
||
def content_hash_mismatch? | ||
if %w[POST PUT PATCH].include?(http_method) | ||
calculated_hash != content_hash | ||
else | ||
false | ||
end | ||
end | ||
|
||
def http_method | ||
@env.method.to_s.upcase | ||
end | ||
|
||
def content_type | ||
type = find_header(%w[CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE]) | ||
|
||
# When sending a body-less POST request, the Content-Type is set at the last minute by the | ||
# Net::HTTP adapter, which states in the documentation for Net::HTTP#post: | ||
# | ||
# > You should set Content-Type: header field for POST. If no Content-Type: field given, | ||
# > this method uses “application/x-www-form-urlencoded” by default. | ||
# | ||
# The same applies to PATCH and PUT. Hopefully the other HTTP adapters behave similarly. | ||
# | ||
type ||= 'application/x-www-form-urlencoded' if %w[POST PATCH PUT].include?(http_method) | ||
|
||
type | ||
end | ||
|
||
def content_hash | ||
find_header(%w[X-AUTHORIZATION-CONTENT-SHA256]) | ||
end | ||
|
||
def original_uri | ||
find_header(%w[X-ORIGINAL-URI X_ORIGINAL_URI HTTP_X_ORIGINAL_URI]) | ||
end | ||
|
||
def request_uri | ||
@env.url.request_uri | ||
end | ||
|
||
def set_date | ||
@env.request_headers['Date'] = Time.now.utc.httpdate | ||
end | ||
|
||
def timestamp | ||
find_header(%w[DATE HTTP_DATE]) | ||
end | ||
|
||
def authorization_header | ||
find_header(%w[Authorization AUTHORIZATION HTTP_AUTHORIZATION]) | ||
end | ||
|
||
def body | ||
body_source = @env.request_body | ||
if body_source.respond_to?(:read) | ||
result = body_source.read | ||
body_source.rewind | ||
result | ||
else | ||
body_source.to_s | ||
end | ||
end | ||
|
||
def fetch_headers | ||
capitalize_keys @env.request_headers | ||
end | ||
|
||
private | ||
|
||
def find_header(keys) | ||
keys.map { |key| @env.request_headers[key] }.compact.first | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
require_relative 'api_auth/middleware' | ||
|
||
module Faraday | ||
# Integrate ApiAuth into Faraday. | ||
module ApiAuth | ||
Faraday::Request.register_middleware(api_auth: Middleware) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
require 'api_auth' | ||
|
||
module Faraday | ||
module ApiAuth | ||
# Request middleware for Faraday. It takes the same arguments as ApiAuth.sign!. | ||
# | ||
# You will usually need to include it after the other middlewares since ApiAuth needs to hash | ||
# the final request. | ||
# | ||
# Usage: | ||
# | ||
# ```ruby | ||
# require 'faraday/api_auth' | ||
# | ||
# conn = Faraday.new do |f| | ||
# f.request :api_auth, access_id, secret_key | ||
# # Alternatively: | ||
# # f.use Faraday::ApiAuth::Middleware, access_id, secret_key | ||
# end | ||
# ``` | ||
# | ||
class Middleware < Faraday::Middleware | ||
def initialize(app, access_id, secret_key, options = {}) | ||
super(app) | ||
@access_id = access_id | ||
@secret_key = secret_key | ||
@options = options | ||
end | ||
|
||
def on_request(env) | ||
::ApiAuth.sign!(env, @access_id, @secret_key, @options) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
require 'spec_helper' | ||
require 'faraday/api_auth' | ||
|
||
describe Faraday::ApiAuth::Middleware do | ||
it 'adds the Authorization headers' do | ||
conn = Faraday.new('http://localhost/') do |f| | ||
f.request :api_auth, 'foo', 'secret', digest: 'sha256' | ||
f.adapter :test do |stub| | ||
stub.get('http://localhost/test') do |env| | ||
[200, {}, env.request_headers['Authorization']] | ||
end | ||
end | ||
end | ||
response = conn.get('test', nil, { 'Date' => 'Tue, 02 Aug 2022 09:29:24 GMT' }) | ||
expect(response.body).to eq 'APIAuth-HMAC-SHA256 foo:Tn/lIZ9kphcO32DwG4wFHenqBt37miDEIkA5ykLgGiQ=' | ||
end | ||
end |
Oops, something went wrong.