bixby-auth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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