simple-api-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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +14 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +16 -0
  7. data/Gemfile +12 -0
  8. data/Guardfile +5 -0
  9. data/LICENSE +22 -0
  10. data/README.md +169 -0
  11. data/Rakefile +7 -0
  12. data/lib/simple-api-auth/authenticable.rb +72 -0
  13. data/lib/simple-api-auth/authenticator.rb +32 -0
  14. data/lib/simple-api-auth/config.rb +61 -0
  15. data/lib/simple-api-auth/hashers/sha1_hasher.rb +14 -0
  16. data/lib/simple-api-auth/helpers/auth_helpers.rb +49 -0
  17. data/lib/simple-api-auth/helpers/request_helpers.rb +18 -0
  18. data/lib/simple-api-auth/request.rb +34 -0
  19. data/lib/simple-api-auth/signer.rb +53 -0
  20. data/lib/simple-api-auth/version.rb +6 -0
  21. data/lib/simple-api-auth.rb +47 -0
  22. data/simple-api-auth.gemspec +25 -0
  23. data/spec/extensions/string_spec.rb +13 -0
  24. data/spec/internal/app/models/models.rb +13 -0
  25. data/spec/internal/config/database.yml +4 -0
  26. data/spec/internal/db/schema.rb +7 -0
  27. data/spec/lib/simple-api-auth/authenticable_spec.rb +106 -0
  28. data/spec/lib/simple-api-auth/authenticator_spec.rb +35 -0
  29. data/spec/lib/simple-api-auth/config_spec.rb +48 -0
  30. data/spec/lib/simple-api-auth/helpers/auth_helpers_spec.rb +61 -0
  31. data/spec/lib/simple-api-auth/helpers/request_helpers_spec.rb +39 -0
  32. data/spec/lib/simple-api-auth/request_spec.rb +49 -0
  33. data/spec/lib/simple-api-auth/signer_spec.rb +37 -0
  34. data/spec/lib/simple-api-auth_spec.rb +110 -0
  35. data/spec/spec_helper.rb +31 -0
  36. data/spec/support/auth.rb +13 -0
  37. data/spec/support/database.rb +14 -0
  38. data/spec/support/database_cleaner.rb +19 -0
  39. data/spec/support/extensions.rb +10 -0
  40. data/spec/support/mocks.rb +67 -0
  41. data/spec/support/requests.rb +51 -0
  42. metadata +189 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4b66b03e6dde55a63109efefcf8571850b7c471e
4
+ data.tar.gz: 8409349483eea331a07698ccf8c25de59ad14fb4
5
+ SHA512:
6
+ metadata.gz: 1696e31b7c4af412db8382b66ed70727422dfdb0c6a2225331f96c9d94eb4dc4e35cd04dde3e970963aee49545947b834765d2557c8663cce24f5ba80ed81119
7
+ data.tar.gz: 2123dd8a3fe6c5eac4c430e1ac80594d18596d7fe0df9eaf8ecddd7bfcbc8c29d88c96b4698fffa4fca2a4e8dae975ce07519aae464cbf17f153ef17161b980d
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require spec_helper
3
+ --format documentation
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ Documentation:
2
+ Enabled: false
3
+ Style/FileName:
4
+ Enabled: false
5
+
6
+ Metrics/LineLength:
7
+ Max: 100
8
+
9
+ AllCops:
10
+ Exclude:
11
+ - 'db/**/*'
12
+ - 'config/**/*'
13
+ - 'bin/**/*'
14
+ - 'Guardfile'
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.4
data/.travis.yml ADDED
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 1.9.3-p484
5
+ - 2.0.0-p353
6
+ - 2.1.0
7
+ - 2.1.2
8
+ - 2.1.3
9
+ - 2.1.4
10
+ notifications:
11
+ email: false
12
+ slack:
13
+ secure: n7JigGAnMzisSryncblhmgh+/WoiOJJ3VoMiS6gYufJEjoiAByiOGKuMDYld/IfIZ9laD/XFx2l4aAl7QwCgae/EaKaZQeEoVSpIqacwsPNsQwqXf0IehgYv7/vykihYlFLu6eOCoqOXgDDWxP3Knned251MYzp8EcjNkUtZMrU=
14
+ addons:
15
+ code_climate:
16
+ repo_token: 9bd643c8090c8ef179d2b7d20d41d2e4399c031f621efdb125c1dc2ca5d9e6cc
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'guard', require: false
7
+ gem 'guard-rspec', require: false
8
+ end
9
+
10
+ group :test do
11
+ gem 'codeclimate-test-reporter', require: false
12
+ end
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard :rspec, cmd: 'bundle exec rspec' do
2
+ watch(/^spec\/.+_spec\.rb$/)
3
+ watch(/^lib\/(.+)\.rb$/) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { 'spec' }
5
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Claude Tech
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # simple-api-auth [![Build Status](https://travis-ci.org/claudetech/ruby-simple-api-auth.svg?branch=master)](https://travis-ci.org/claudetech/ruby-simple-api-auth) [![Coverage Status](https://coveralls.io/repos/claudetech/ruby-simple-api-auth/badge.png?branch=master)](https://coveralls.io/r/claudetech/ruby-simple-api-auth?branch=master) [![Code Climate](https://codeclimate.com/github/claudetech/ruby-simple-api-auth/badges/gpa.svg)](https://codeclimate.com/github/claudetech/ruby-simple-api-auth)
2
+
3
+ This gem provides a very basic token based authentication
4
+ to use for API.
5
+ This is meant to be used as a lightweight authentication
6
+ solution when OAuth2 is too much.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'simple-api-auth'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install simple-api-auth
23
+
24
+ ## Usage
25
+
26
+ This gem works out of the box with `ActiveRecord` and can be used stand alone
27
+ with a little more work.
28
+
29
+ ### ActiveRecord usage
30
+
31
+ Just include `acts_as_api_authenticable` in your model.
32
+
33
+ ```ruby
34
+ class User < ActiveRecord::Base
35
+ acts_as_api_authenticable
36
+ end
37
+ ```
38
+
39
+ you will need to have `ssa_key` and `ssa_secret` defined as strings
40
+ in your database for this to work. If you want to change the columns name,
41
+ you can pass them in option.
42
+
43
+ ```ruby
44
+ class User < ActiveRecord::Base
45
+ acts_as_api_authenticable ssa_key: :resource_key_field, ssa_secret: :secret_token
46
+ end
47
+ ```
48
+
49
+ If you want the keys to be generated when you create a new instance of the model, you can pass `:auto_generate` with either `true`, or the field you want
50
+ to generate.
51
+
52
+ ```ruby
53
+ class User < ActiveRecord::Base
54
+ # this will generate a after_initialize to assign `ssa_key`
55
+ acts_as_api_authenticable auto_generate: :ssa_key
56
+ # this will generate a after_initialize for both `ssa_key` and `ssa_secret`
57
+ acts_as_api_authenticable auto_generate: true
58
+ end
59
+ ```
60
+
61
+ Note that the keys for autogenerate should be `:ssa_key` and `:ssa_secret` even if you change the key column name.
62
+
63
+ You can then use
64
+
65
+ ```ruby
66
+ User.authenticate(request)
67
+ ```
68
+
69
+ this will return the user if the request is valid, or `nil` otherwise.
70
+
71
+ ### Standalone usage
72
+
73
+ This gem can easily used without `ActiveRecord`.
74
+
75
+ ```ruby
76
+ ssa_key = SimpleApiAuth.extract_key(request)
77
+ secret_key = your_logic_to_get_secret_key(ssa_key)
78
+ valid = SimpleApiAuth.valid_signature?(request, secret_key)
79
+ ```
80
+
81
+ ### Configuration
82
+
83
+ The library accepts the following configurations
84
+
85
+ ```ruby
86
+ SimpleApiAuth.configure do |config|
87
+ # values used as default with `acts_as_api_authenticable`
88
+ config.model_defaults = { ssa_key: :ssa_key, ssa_secret: :ssa_secret, auto_generate: false }
89
+
90
+ # the normalized keys for the HTTP headers
91
+ config.header_keys = { key: :x_saa_key, time: :x_saa_auth_time, authorization: :authorization
92
+ }
93
+
94
+ # the methods name for the HTTP request used
95
+ config.request_fields = {
96
+ headers: :headers,
97
+ http_verb: :method,
98
+ uri: :path,
99
+ query_string: :query_string,
100
+ body: :body
101
+ }
102
+
103
+ # the allowed HTTP methods
104
+ config.allowed_methods = [:get, :post, :put, :patch, :delete]
105
+
106
+ # the required headers for the HTTP request
107
+ config.required_headers = config.header_keys.values
108
+
109
+ # the class to use to hash requests
110
+ # it must contain a `hmac` and a `hash` method
111
+ config.hasher = SimpleApiAuth::Hasher::SHA1
112
+
113
+ # the class to use to sign requests
114
+ # it must contain a `sign` method
115
+ config.signer = SimpleApiAuth::Signer
116
+ config.request_timeout = 5
117
+ config.logger = nil
118
+ end
119
+ ```
120
+
121
+
122
+ ### Supported frameworks
123
+
124
+ This library should be configurable enough to work with more or less any framework.
125
+ It will work out of the box for Rails, and will just need a little setup
126
+ to work with `request` objects with a different API.
127
+
128
+ For example, to work with Sinatra request, the following can be passed
129
+
130
+ ```ruby
131
+ SimpleApiAuth.configure do |config|
132
+ config.request_fields.merge! {
133
+ headers: :env,
134
+ http_verb: :request_method,
135
+ uri: :path_info
136
+ }
137
+ end
138
+ ```
139
+
140
+ Note that `body` should return something with a `read` method,
141
+ and `query_string` should contain the URI encoded query string.
142
+
143
+ ## Clients
144
+
145
+ This library can also be used as a client. You can compute the request
146
+ signature with
147
+
148
+ ```ruby
149
+ signature = SimpleApiAuth.compute_signature(request, secret_key)
150
+ ```
151
+
152
+ or directly sign the request with
153
+
154
+ ```ruby
155
+ SimpleApiAuth.sign!(request, secret_key)
156
+ ```
157
+
158
+ A JS client, with an AngularJS module intregrated with the `$http` service
159
+ is in progress and should be available soon (with a few weeks).
160
+
161
+ Contributions are very welcome for other clients.
162
+
163
+ ## Contributing
164
+
165
+ 1. Fork it ( https://github.com/claudetech/simple-api-auth/fork )
166
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
167
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
168
+ 4. Push to the branch (`git push origin my-new-feature`)
169
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
@@ -0,0 +1,72 @@
1
+ module SimpleApiAuth
2
+ module Authenticable
3
+ def api_authenticable?
4
+ false
5
+ end
6
+
7
+ def acts_as_api_authenticable(options = {})
8
+ if api_authenticable?
9
+ self.ssa_options = ssa_options.merge(options)
10
+ else
11
+ extend ClassMethods
12
+ include InstanceMethods
13
+ self.ssa_options = SimpleApiAuth.config.make_model_options(options)
14
+ ssa_options[:auto_generate].each do |field|
15
+ send(:after_initialize, "assign_#{field}")
16
+ end
17
+ end
18
+ end
19
+
20
+ module ClassMethods
21
+ attr_accessor :ssa_options
22
+
23
+ def api_authenticable?
24
+ true
25
+ end
26
+
27
+ def ssa_authenticate(request)
28
+ request = SimpleApiAuth::Request.create(request)
29
+ entity = ssa_find(request)
30
+ return false if entity.nil?
31
+ secret_key = entity.send(ssa_options[:ssa_secret])
32
+ return false unless SimpleApiAuth.valid_signature?(request, secret_key)
33
+ entity
34
+ end
35
+
36
+ def ssa_find(request)
37
+ key = SimpleApiAuth.extract_key(request)
38
+ find_by(ssa_options[:ssa_key] => key)
39
+ end
40
+
41
+ def generate_ssa_key(options = {})
42
+ length = options[:length] || (Math.log(count + 1, 64) + 5)
43
+ loop do
44
+ key = SecureRandom.urlsafe_base64(length)
45
+ break key unless exists?(ssa_options[:ssa_key] => key)
46
+ end
47
+ end
48
+
49
+ def generate_ssa_secret(options = {})
50
+ length = options[:length] || 64
51
+ SecureRandom.urlsafe_base64(length)
52
+ end
53
+ end
54
+
55
+ module InstanceMethods
56
+ def assign_ssa_key(options = {})
57
+ assign_ssa(:ssa_key, options)
58
+ end
59
+
60
+ def assign_ssa_secret(options = {})
61
+ assign_ssa(:ssa_secret, options)
62
+ end
63
+
64
+ private
65
+
66
+ def assign_ssa(field, options = {})
67
+ key_name = self.class.ssa_options[field]
68
+ send("#{key_name}=", self.class.send("generate_#{field}", options))
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,32 @@
1
+ module SimpleApiAuth
2
+ class Authenticator
3
+ include SimpleApiAuth::Helpers::Auth
4
+
5
+ attr_accessor :request, :signer
6
+
7
+ def initialize(request, secret_key, options = {})
8
+ self.request = SimpleApiAuth::Request.create(request)
9
+ self.signer = SimpleApiAuth.config.signer.new
10
+ @options = options
11
+ @secret_key = secret_key
12
+ end
13
+
14
+ def valid_signature?
15
+ return false if !check_data(request) || too_old?(request)
16
+ signed_request = signer.sign(request, @secret_key)
17
+ SimpleApiAuth.log(Logger::DEBUG, "Signed request: #{signed_request}")
18
+ SimpleApiAuth.log(Logger::DEBUG, "User signature: #{signature}")
19
+ secure_equals?(signed_request, signature, @secret_key)
20
+ end
21
+
22
+ private
23
+
24
+ def signature
25
+ extract_signature(headers)
26
+ end
27
+
28
+ def headers
29
+ request.headers
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,61 @@
1
+ module SimpleApiAuth
2
+ class Config
3
+ attr_accessor :request_fields, :allowed_methods
4
+ attr_accessor :signer, :request_timeout, :required_headers, :hasher
5
+ attr_accessor :logger, :header_keys, :model_defaults
6
+
7
+ def initialize
8
+ reset!
9
+ end
10
+
11
+ def reset!
12
+ self.model_defaults = model_default_values
13
+ self.header_keys = default_header_keys
14
+ self.request_fields = default_request_fields
15
+ self.allowed_methods = [:get, :post, :put, :patch, :delete]
16
+ self.required_headers = default_header_keys.values
17
+ self.hasher = SimpleApiAuth::Hasher::SHA1
18
+ self.signer = SimpleApiAuth::Signer
19
+ self.request_timeout = 5
20
+ self.logger = nil
21
+ end
22
+
23
+ def make_model_options(options)
24
+ options = model_defaults.merge(options)
25
+ if options[:auto_generate].is_a?(Symbol)
26
+ options[:auto_generate] = [options[:auto_generate]]
27
+ elsif !options[:auto_generate].is_a?(Array)
28
+ options[:auto_generate] = options[:auto_generate] ? [:ssa_key, :ssa_secret] : []
29
+ end
30
+ options
31
+ end
32
+
33
+ private
34
+
35
+ def default_request_fields
36
+ {
37
+ headers: :headers,
38
+ http_verb: :method,
39
+ uri: :path,
40
+ query_string: :query_string,
41
+ body: :body
42
+ }
43
+ end
44
+
45
+ def default_header_keys
46
+ {
47
+ key: :x_saa_key,
48
+ time: :x_saa_auth_time,
49
+ authorization: :authorization
50
+ }
51
+ end
52
+
53
+ def model_default_values
54
+ {
55
+ ssa_key: :ssa_key,
56
+ ssa_secret: :ssa_secret,
57
+ auto_generate: []
58
+ }
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,14 @@
1
+ module SimpleApiAuth
2
+ module Hasher
3
+ class SHA1
4
+ def hash(value)
5
+ Digest::SHA1.digest(value)
6
+ end
7
+
8
+ def hmac(key, message)
9
+ digest = OpenSSL::Digest.new('sha1')
10
+ OpenSSL::HMAC.digest(digest, key, message)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,49 @@
1
+ module SimpleApiAuth
2
+ module Helpers
3
+ module Auth
4
+ def extract_signature(headers)
5
+ header_key = SimpleApiAuth.config.header_keys[:authorization]
6
+ match = /Signature: (.+)/.match(headers[header_key])
7
+ match && match[1]
8
+ end
9
+
10
+ def required_headers
11
+ options[:required_headers] || SimpleApiAuth.config.required_headers
12
+ end
13
+
14
+ def request_timeout
15
+ (options[:request_timeout] || SimpleApiAuth.config.request_timeout) * 60
16
+ end
17
+
18
+ def allowed_methods
19
+ options[:allowed_methods] || SimpleApiAuth.config.allowed_methods
20
+ end
21
+
22
+ def options
23
+ @options || {}
24
+ end
25
+
26
+ def check_data(request)
27
+ required_headers.each do |k, _|
28
+ return false unless request.headers.key?(k)
29
+ end
30
+ allowed_methods.include?(request.http_verb)
31
+ end
32
+
33
+ def too_old?(request)
34
+ request_time = request.time
35
+ return false if request_time.nil?
36
+ difference = Time.now - request_time
37
+ difference < 0 || difference > request_timeout
38
+ end
39
+
40
+ def secure_equals?(m1, m2, key)
41
+ sha1_hmac(key, m1) == sha1_hmac(key, m2)
42
+ end
43
+
44
+ def sha1_hmac(key, message)
45
+ SimpleApiAuth::Hasher::SHA1.new.hmac(key, message)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,18 @@
1
+ module SimpleApiAuth
2
+ module Helpers
3
+ module Request
4
+ def normalize_headers(headers)
5
+ normalized_headers = {}
6
+ headers.each do |key, value|
7
+ normalized_key = normalize(key)
8
+ normalized_headers[normalized_key] = value
9
+ end
10
+ normalized_headers
11
+ end
12
+
13
+ def normalize(s)
14
+ s.to_s.downcase.gsub(/-/, '_').to_sym
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,34 @@
1
+ module SimpleApiAuth
2
+ class Request
3
+ include SimpleApiAuth::Helpers::Request
4
+
5
+ attr_accessor :headers, :http_verb, :query_string, :uri, :body
6
+
7
+ def initialize(options = {})
8
+ options.each do |k, v|
9
+ send("#{k}=", v)
10
+ end
11
+ self.headers = normalize_headers(headers)
12
+ self.http_verb = normalize(http_verb)
13
+ end
14
+
15
+ def time
16
+ header_key = SimpleApiAuth.config.header_keys[:time]
17
+ Time.parse(headers[header_key])
18
+ rescue ArgumentError
19
+ nil
20
+ end
21
+
22
+ def self.create(request)
23
+ if request.is_a?(Request)
24
+ request
25
+ else
26
+ options = {}
27
+ SimpleApiAuth.config.request_fields.each do |k, v|
28
+ options[k] = request.send(v)
29
+ end
30
+ Request.new(options)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,53 @@
1
+ module SimpleApiAuth
2
+ class Signer
3
+ attr_accessor :hasher
4
+
5
+ def initialize(options = {})
6
+ hahser_class = options[:hasher] || SimpleApiAuth.config.hasher
7
+ self.hasher = hahser_class.new
8
+ end
9
+
10
+ def sign(request, secret_key)
11
+ signing_key = make_singing_key(request, secret_key)
12
+ SimpleApiAuth.log(Logger::DEBUG, "Signing key(hex): #{Digest.hexencode(signing_key)}")
13
+
14
+ string_to_sign = make_string_to_sign(request)
15
+ SimpleApiAuth.log(Logger::DEBUG, "String to sign: #{string_to_sign}")
16
+
17
+ signature = hasher.hmac(signing_key, string_to_sign)
18
+ Digest.hexencode(signature)
19
+ end
20
+
21
+ def make_string_to_sign(request)
22
+ hashed_request = make_hashed_request(request)
23
+ SimpleApiAuth.log(Logger::DEBUG, "Hashed request: #{hashed_request}")
24
+ [
25
+ request.time.iso8601,
26
+ hashed_request
27
+ ].join("\n")
28
+ end
29
+
30
+ def make_hashed_request(request)
31
+ canonical_request_string = make_canonical_request(request)
32
+ SimpleApiAuth.log(Logger::DEBUG, "Canonical request string: #{canonical_request_string}")
33
+ Digest.hexencode(hasher.hash(canonical_request_string))
34
+ end
35
+
36
+ private
37
+
38
+ def make_singing_key(request, secret_key)
39
+ date = request.time.strftime('%Y%m%d')
40
+ hashed_date = hasher.hmac('ssa' + secret_key, date)
41
+ hasher.hmac(hashed_date, 'ssa_request')
42
+ end
43
+
44
+ def make_canonical_request(request)
45
+ [
46
+ request.http_verb,
47
+ URI.encode(request.uri),
48
+ URI.encode(request.query_string),
49
+ Digest.hexencode(hasher.hash(request.body.read))
50
+ ].join("\n")
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,6 @@
1
+ module SimpleApiAuth
2
+ MAJOR = 0
3
+ MINOR = 1
4
+ PATCH = 0
5
+ VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
6
+ end
@@ -0,0 +1,47 @@
1
+ require 'open-uri'
2
+
3
+ ['extensions/', 'helpers/', '', 'hashers/'].each do |path|
4
+ files = Dir[File.expand_path("simple-api-auth/#{path}*.rb", File.dirname(__FILE__))]
5
+ files.each do |m|
6
+ require m
7
+ end
8
+ end
9
+
10
+ module SimpleApiAuth
11
+ def self.config
12
+ @config ||= Config.new
13
+ end
14
+
15
+ def self.configure
16
+ yield config
17
+ end
18
+
19
+ def self.log(severity, message = nil, progname = nil, &block)
20
+ config.logger.log(severity, message, progname, &block) unless config.logger.nil?
21
+ end
22
+
23
+ def self.extract_key(request)
24
+ request = SimpleApiAuth::Request.create(request)
25
+ request.headers[SimpleApiAuth.config.header_keys[:key]]
26
+ end
27
+
28
+ def self.valid_signature?(request, secret_key, options = {})
29
+ authenticator = Authenticator.new(request, secret_key, options)
30
+ authenticator.valid_signature?
31
+ end
32
+
33
+ def self.compute_signature(request, secret_key, options = {})
34
+ signer = SimpleApiAuth.config.signer.new(options)
35
+ signer.sign(request, secret_key)
36
+ end
37
+
38
+ def self.sign!(request, secret_key, options = {})
39
+ signature = compute_signature(request, secret_key, options)
40
+ header_name = SimpleApiAuth.config.request_fields[:headers]
41
+ authorization_key = SimpleApiAuth.config.header_keys[:authorization]
42
+ request.send(header_name)[authorization_key] = "Signature: #{signature}"
43
+ request
44
+ end
45
+ end
46
+
47
+ ActiveRecord::Base.send(:extend, SimpleApiAuth::Authenticable) if defined?(ActiveRecord::Base)