bixby-auth 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,82 @@
1
+ module ApiAuth
2
+
3
+ # Builds the canonical string given a request object.
4
+ class Headers
5
+
6
+ include RequestDrivers
7
+
8
+ def initialize(request)
9
+ @original_request = request
10
+ @request = initialize_request_driver(request)
11
+ true
12
+ end
13
+
14
+ def initialize_request_driver(request)
15
+ clazz = request.class.to_s
16
+ if RequestDrivers.drivers.include?(clazz) then
17
+ return RequestDrivers.drivers[clazz].new(request)
18
+
19
+ elsif clazz == "ActionController::TestRequest" then
20
+ # special handling for rails 3 vs 4
21
+ if defined?(ActionDispatch) then
22
+ return ActionDispatchRequest.new(request)
23
+ else
24
+ return ActionControllerRequest.new(request)
25
+ end
26
+
27
+ elsif Module.const_defined?(:Rack) && Rack.const_defined?(:Request) && request.kind_of?(Rack::Request) then
28
+ # this goes last because TestRequest is also a subclass of Rack::Request
29
+ return RackRequest.new(request)
30
+ end
31
+
32
+ raise UnknownHTTPRequest, "#{clazz} is not yet supported."
33
+ end
34
+ private :initialize_request_driver
35
+
36
+ # Returns the request timestamp
37
+ def timestamp
38
+ @request.timestamp
39
+ end
40
+
41
+ # Returns the canonical string computed from the request's headers
42
+ def canonical_string
43
+ [ @request.content_type,
44
+ @request.content_md5,
45
+ @request.request_uri.gsub(/https?:\/\/[^(,|\?|\/)]*/,''), # remove host
46
+ @request.timestamp
47
+ ].join(",")
48
+ end
49
+
50
+ # Returns the authorization header from the request's headers
51
+ def authorization_header
52
+ @request.authorization_header
53
+ end
54
+
55
+ def set_date
56
+ @request.set_date if @request.timestamp.empty?
57
+ end
58
+
59
+ def calculate_md5
60
+ @request.populate_content_md5 if @request.content_md5.empty?
61
+ end
62
+
63
+ def md5_mismatch?
64
+ if @request.content_md5.empty?
65
+ false
66
+ else
67
+ @request.md5_mismatch?
68
+ end
69
+ end
70
+
71
+ # Sets the request's authorization header with the passed in value.
72
+ # The header should be the ApiAuth HMAC signature.
73
+ #
74
+ # This will return the original request object with the signed Authorization
75
+ # header already in place.
76
+ def sign_header(header)
77
+ @request.set_auth_header header
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,39 @@
1
+ module ApiAuth
2
+
3
+ module Helpers # :nodoc:
4
+
5
+ def b64_encode(string)
6
+ if Base64.respond_to?(:strict_encode64)
7
+ Base64.strict_encode64(string)
8
+ else
9
+ # Fall back to stripping out newlines on Ruby 1.8.
10
+ Base64.encode64(string).gsub(/\n/, '')
11
+ end
12
+ end
13
+
14
+ def md5_base64digest(string)
15
+ if Digest::MD5.respond_to?(:base64digest)
16
+ Digest::MD5.base64digest(string)
17
+ else
18
+ b64_encode(Digest::MD5.digest(string))
19
+ end
20
+ end
21
+
22
+ # Capitalizes the keys of a hash
23
+ def capitalize_keys(hsh)
24
+ capitalized_hash = {}
25
+ hsh.each_pair {|k,v| capitalized_hash[k.to_s.upcase] = v }
26
+ capitalized_hash
27
+ end
28
+
29
+ def time_as_httpdate
30
+ Helpers.time_as_httpdate
31
+ end
32
+
33
+ def self.time_as_httpdate
34
+ Time.now.utc.strftime("%a, %d %b %Y %T GMT")
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,129 @@
1
+ module ApiAuth
2
+
3
+ # Integration with Rails
4
+ #
5
+ class Rails # :nodoc:
6
+
7
+ module ControllerMethods # :nodoc:
8
+
9
+ module InstanceMethods # :nodoc:
10
+
11
+ def get_api_access_id_from_request
12
+ ApiAuth.access_id(request)
13
+ end
14
+
15
+ def api_authenticated?(secret_key)
16
+ ApiAuth.authentic?(request, secret_key)
17
+ end
18
+
19
+ end
20
+
21
+ unless defined?(ActionController)
22
+ begin
23
+ require 'rubygems'
24
+ gem 'actionpack'
25
+ gem 'activesupport'
26
+ require 'action_controller'
27
+ require 'active_support'
28
+ rescue LoadError
29
+ nil
30
+ end
31
+ end
32
+
33
+ if defined?(ActionController::Base)
34
+ ActionController::Base.send(:include, ControllerMethods::InstanceMethods)
35
+ end
36
+
37
+ end # ControllerMethods
38
+
39
+ module ActiveResourceExtension # :nodoc:
40
+
41
+ module ActiveResourceApiAuth # :nodoc:
42
+
43
+ def self.included(base)
44
+ base.extend(ClassMethods)
45
+
46
+ if base.respond_to?('class_attribute')
47
+ base.class_attribute :hmac_access_id
48
+ base.class_attribute :hmac_secret_key
49
+ base.class_attribute :use_hmac
50
+ else
51
+ base.class_inheritable_accessor :hmac_access_id
52
+ base.class_inheritable_accessor :hmac_secret_key
53
+ base.class_inheritable_accessor :use_hmac
54
+ end
55
+
56
+ end
57
+
58
+ module ClassMethods
59
+
60
+ def with_api_auth(access_id, secret_key)
61
+ self.hmac_access_id = access_id
62
+ self.hmac_secret_key = secret_key
63
+ self.use_hmac = true
64
+
65
+ class << self
66
+ alias_method_chain :connection, :auth
67
+ end
68
+ end
69
+
70
+ def connection_with_auth(refresh = false)
71
+ c = connection_without_auth(refresh)
72
+ c.hmac_access_id = self.hmac_access_id
73
+ c.hmac_secret_key = self.hmac_secret_key
74
+ c.use_hmac = self.use_hmac
75
+ c
76
+ end
77
+
78
+ end # class methods
79
+
80
+ module InstanceMethods
81
+ end
82
+
83
+ end # BaseApiAuth
84
+
85
+ module Connection
86
+
87
+ def self.included(base)
88
+ base.send :alias_method_chain, :request, :auth
89
+ base.class_eval do
90
+ attr_accessor :hmac_secret_key, :hmac_access_id, :use_hmac
91
+ end
92
+ end
93
+
94
+ def request_with_auth(method, path, *arguments)
95
+ if use_hmac && hmac_access_id && hmac_secret_key
96
+ h = arguments.last
97
+ tmp = "Net::HTTP::#{method.to_s.capitalize}".constantize.new(path, h)
98
+ tmp.body = arguments[0] if arguments.length > 1
99
+ ApiAuth.sign!(tmp, hmac_access_id, hmac_secret_key)
100
+ arguments.last['Content-MD5'] = tmp['Content-MD5'] if tmp['Content-MD5']
101
+ arguments.last['DATE'] = tmp['DATE']
102
+ arguments.last['Authorization'] = tmp['Authorization']
103
+ end
104
+
105
+ request_without_auth(method, path, *arguments)
106
+ end
107
+
108
+ end # Connection
109
+
110
+ unless defined?(ActiveResource)
111
+ begin
112
+ require 'rubygems'
113
+ gem 'activeresource'
114
+ require 'active_resource'
115
+ rescue LoadError
116
+ nil
117
+ end
118
+ end
119
+
120
+ if defined?(ActiveResource)
121
+ ActiveResource::Base.send(:include, ActiveResourceApiAuth)
122
+ ActiveResource::Connection.send(:include, Connection)
123
+ end
124
+
125
+ end # ActiveResourceExtension
126
+
127
+ end # Rails
128
+
129
+ end # ApiAuth
@@ -0,0 +1,84 @@
1
+ module ApiAuth
2
+
3
+ module RequestDrivers # :nodoc:
4
+
5
+ class ActionControllerRequest # :nodoc:
6
+
7
+ include ApiAuth::Helpers
8
+
9
+ def initialize(request)
10
+ @request = request
11
+ @headers = fetch_headers
12
+ true
13
+ end
14
+
15
+ def set_auth_header(header)
16
+ @request.env["Authorization"] = header
17
+ @headers = fetch_headers
18
+ @request
19
+ end
20
+
21
+ def calculated_md5
22
+ body = @request.raw_post
23
+ md5_base64digest(body)
24
+ end
25
+
26
+ def populate_content_md5
27
+ if @request.put? || @request.post?
28
+ @request.env["Content-MD5"] = calculated_md5
29
+ end
30
+ end
31
+
32
+ def md5_mismatch?
33
+ if @request.put? || @request.post?
34
+ calculated_md5 != content_md5
35
+ else
36
+ false
37
+ end
38
+ end
39
+
40
+ def fetch_headers
41
+ capitalize_keys @request.env
42
+ end
43
+
44
+ def content_type
45
+ value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
46
+ value.nil? ? "" : value
47
+ end
48
+
49
+ def content_md5
50
+ value = find_header(%w(CONTENT-MD5 CONTENT_MD5 HTTP_CONTENT_MD5))
51
+ value.nil? ? "" : value
52
+ end
53
+
54
+ def request_uri
55
+ @request.request_uri
56
+ end
57
+
58
+ def set_date
59
+ @request.env['HTTP_DATE'] = time_as_httpdate
60
+ end
61
+
62
+ def timestamp
63
+ value = find_header(%w(DATE HTTP_DATE))
64
+ value.nil? ? "" : value
65
+ end
66
+
67
+ def authorization_header
68
+ find_header %w(Authorization AUTHORIZATION HTTP_AUTHORIZATION)
69
+ end
70
+
71
+ private
72
+
73
+ def find_header(keys)
74
+ keys.map {|key| @headers[key] }.compact.first
75
+ end
76
+
77
+ end
78
+
79
+ drivers["ActionController::Request"] = ActionControllerRequest
80
+ drivers["ActionController::CgiRequest"] = ActionControllerRequest
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,17 @@
1
+ module ApiAuth
2
+
3
+ module RequestDrivers # :nodoc:
4
+
5
+ class ActionDispatchRequest < ActionControllerRequest # :nodoc:
6
+
7
+ def request_uri
8
+ @request.fullpath
9
+ end
10
+
11
+ end
12
+
13
+ drivers["ActionDispatch::Request"] = ActionDispatchRequest
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,65 @@
1
+
2
+ module ApiAuth
3
+ module RequestDrivers
4
+
5
+ class BixbyRequest
6
+
7
+ include ApiAuth::Helpers
8
+
9
+ def initialize(request)
10
+ @request = request
11
+ @headers = request.headers
12
+ true
13
+ end
14
+
15
+ def set_auth_header(header)
16
+ @headers["Authorization"] = header
17
+ @request
18
+ end
19
+
20
+ def calculated_md5
21
+ Digest::MD5.base64digest(@request.body || '')
22
+ end
23
+
24
+ def populate_content_md5
25
+ # Should *always* be a POST!
26
+ @headers["Content-MD5"] = calculated_md5
27
+ end
28
+
29
+ def md5_mismatch?
30
+ calculated_md5 != content_md5
31
+ end
32
+
33
+ def content_type
34
+ value = @headers["Content-Type"]
35
+ value.nil? ? "" : value
36
+ end
37
+
38
+ def content_md5
39
+ value = @headers["Content-MD5"]
40
+ value.nil? ? "" : value
41
+ end
42
+
43
+ def request_uri
44
+ @request.path
45
+ end
46
+
47
+ def set_date
48
+ @request.headers["Date"] = time_as_httpdate
49
+ end
50
+
51
+ def timestamp
52
+ value = @headers["Date"]
53
+ value.nil? ? "" : value
54
+ end
55
+
56
+ def authorization_header
57
+ @headers["Authorization"]
58
+ end
59
+
60
+ end
61
+
62
+ drivers["Bixby::SignedJsonRequest"] = BixbyRequest
63
+
64
+ end
65
+ end
@@ -0,0 +1,72 @@
1
+ module ApiAuth
2
+
3
+ module RequestDrivers # :nodoc:
4
+
5
+ class CurbRequest # :nodoc:
6
+
7
+ include ApiAuth::Helpers
8
+
9
+ def initialize(request)
10
+ @request = request
11
+ @headers = fetch_headers
12
+ true
13
+ end
14
+
15
+ def set_auth_header(header)
16
+ @request.headers.merge!({ "Authorization" => header })
17
+ @headers = fetch_headers
18
+ @request
19
+ end
20
+
21
+ def populate_content_md5
22
+ nil #doesn't appear to be possible
23
+ end
24
+
25
+ def md5_mismatch?
26
+ false
27
+ end
28
+
29
+ def fetch_headers
30
+ capitalize_keys @request.headers
31
+ end
32
+
33
+ def content_type
34
+ value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
35
+ value.nil? ? "" : value
36
+ end
37
+
38
+ def content_md5
39
+ value = find_header(%w(CONTENT-MD5 CONTENT_MD5))
40
+ value.nil? ? "" : value
41
+ end
42
+
43
+ def request_uri
44
+ @request.url
45
+ end
46
+
47
+ def set_date
48
+ @request.headers.merge!({ "DATE" => time_as_httpdate })
49
+ end
50
+
51
+ def timestamp
52
+ value = find_header(%w(DATE HTTP_DATE))
53
+ value.nil? ? "" : value
54
+ end
55
+
56
+ def authorization_header
57
+ find_header %w(Authorization AUTHORIZATION HTTP_AUTHORIZATION)
58
+ end
59
+
60
+ private
61
+
62
+ def find_header(keys)
63
+ keys.map {|key| @headers[key] }.compact.first
64
+ end
65
+
66
+ end
67
+
68
+ drivers["Curl::Easy"] = CurbRequest
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,82 @@
1
+ module ApiAuth
2
+
3
+ module RequestDrivers # :nodoc:
4
+
5
+ class HttpiRequest # :nodoc:
6
+
7
+ include ApiAuth::Helpers
8
+
9
+ def initialize(request)
10
+ @request = request
11
+ @headers = fetch_headers
12
+ true
13
+ end
14
+
15
+ def set_auth_header(header)
16
+ @request.headers["Authorization"] = header
17
+ @headers = fetch_headers
18
+ @request
19
+ end
20
+
21
+ def calculated_md5
22
+ md5_base64digest(@request.body || '')
23
+ end
24
+
25
+ def populate_content_md5
26
+ if @request.body
27
+ @request.headers["Content-MD5"] = calculated_md5
28
+ end
29
+ end
30
+
31
+ def md5_mismatch?
32
+ if @request.body
33
+ calculated_md5 != content_md5
34
+ else
35
+ false
36
+ end
37
+ end
38
+
39
+ def fetch_headers
40
+ capitalize_keys @request.headers
41
+ end
42
+
43
+ def content_type
44
+ value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
45
+ value.nil? ? "" : value
46
+ end
47
+
48
+ def content_md5
49
+ value = find_header(%w(CONTENT-MD5 CONTENT_MD5))
50
+ value.nil? ? "" : value
51
+ end
52
+
53
+ def request_uri
54
+ @request.url.request_uri
55
+ end
56
+
57
+ def set_date
58
+ @request.headers["DATE"] = time_as_httpdate
59
+ end
60
+
61
+ def timestamp
62
+ value = find_header(%w(DATE HTTP_DATE))
63
+ value.nil? ? "" : value
64
+ end
65
+
66
+ def authorization_header
67
+ find_header %w(Authorization AUTHORIZATION HTTP_AUTHORIZATION)
68
+ end
69
+
70
+ private
71
+
72
+ def find_header(keys)
73
+ keys.map {|key| @headers[key] }.compact.first
74
+ end
75
+
76
+ end
77
+
78
+ drivers["HTTPI::Request"] = HttpiRequest
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,98 @@
1
+ module ApiAuth
2
+
3
+ module RequestDrivers # :nodoc:
4
+
5
+ class NetHttpRequest # :nodoc:
6
+
7
+ include ApiAuth::Helpers
8
+
9
+ def initialize(request)
10
+ @request = request
11
+ @headers = fetch_headers
12
+ true
13
+ end
14
+
15
+ def set_auth_header(header)
16
+ @request["Authorization"] = header
17
+ @headers = fetch_headers
18
+ @request
19
+ end
20
+
21
+ def calculated_md5
22
+ if @request.respond_to?(:body_stream) && @request.body_stream
23
+ body = @request.body_stream.read
24
+ @request.body_stream.rewind
25
+ else
26
+ body = @request.body
27
+ end
28
+
29
+ md5_base64digest(body || '')
30
+ end
31
+
32
+ def populate_content_md5
33
+ if @request.class::REQUEST_HAS_BODY
34
+ @request["Content-MD5"] = calculated_md5
35
+ end
36
+ end
37
+
38
+ def md5_mismatch?
39
+ if @request.class::REQUEST_HAS_BODY
40
+ calculated_md5 != content_md5
41
+ else
42
+ false
43
+ end
44
+ end
45
+
46
+ def fetch_headers
47
+ @request
48
+ end
49
+
50
+ def content_type
51
+ value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
52
+ value.nil? ? "" : value
53
+ end
54
+
55
+ def content_md5
56
+ value = find_header(%w(CONTENT-MD5 CONTENT_MD5))
57
+ value.nil? ? "" : value
58
+ end
59
+
60
+ def request_uri
61
+ @request.path
62
+ end
63
+
64
+ def set_date
65
+ @request["DATE"] = time_as_httpdate
66
+ end
67
+
68
+ def timestamp
69
+ value = find_header(%w(DATE HTTP_DATE))
70
+ value.nil? ? "" : value
71
+ end
72
+
73
+ def authorization_header
74
+ find_header %w(Authorization AUTHORIZATION HTTP_AUTHORIZATION)
75
+ end
76
+
77
+ private
78
+
79
+ def find_header(keys)
80
+ keys.map {|key| @headers[key] }.compact.first
81
+ end
82
+
83
+ end
84
+
85
+ drivers["Net::HTTP"] = NetHttpRequest
86
+ drivers["Net::HTTP::Put::Multipart"] = NetHttpRequest
87
+ drivers["Net::HTTP::Post::Multipart"] = NetHttpRequest
88
+
89
+ Net::HTTP.constants.each do |c|
90
+ c = Net::HTTP.const_get(c)
91
+ if c.kind_of?(Class) && c.ancestors.include?(Net::HTTPRequest) then
92
+ drivers[c.to_s] = NetHttpRequest
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ end