rack-contrib-sign 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e063e40cf6ff43aa7ccd9f89e905d4f87a733baf
4
+ data.tar.gz: 7a20e29567951a4d2908bb5a64aea76820fb4c87
5
+ SHA512:
6
+ metadata.gz: 7d2255f91a0a46d4f65a26ab6589a1c480ef9caa5fa0a2b27f55900cb90c367f4f1689e06ea836df5552a074f7c3256aa939b0e0a6bd9f41f64d3092461321a2
7
+ data.tar.gz: e4013973326b23f2f14101a725b775f908409ccefaa313208257460b76e2fafddad8166c4014de3a671d51f50ea73df4947343e83d229f25029f06da19e373ea
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=doc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ guard :rspec, all_on_start: false, all_after_pass: false, notification: false do
4
+ watch(%r{^spec/.+_spec\.rb$})
5
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
6
+ watch('spec/spec_helper.rb') { "spec" }
7
+ end
8
+
9
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 ZippyKid
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,30 @@
1
+ # Rack::Contrib::Sign
2
+
3
+ Implement thorough request signing in Rack.
4
+
5
+ ## Installation
6
+
7
+
8
+ gem 'rack-contrib-sign'
9
+
10
+ And then execute:
11
+
12
+ $ bundle
13
+
14
+ Or install it yourself as:
15
+
16
+ $ gem install rack-contrib-sign
17
+
18
+ ## Usage
19
+
20
+ Install in Rack by adding the following to your config.ru:
21
+
22
+ ```ruby
23
+ require 'rack/contrib/sign'
24
+ use Rack::Contrib::Sign::Middleware
25
+ ```
26
+
27
+ ## Specific Authentication Details
28
+
29
+ This gem works by creating a receipt which gets HMAC hashed with a secret.
30
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
6
+
@@ -0,0 +1,15 @@
1
+
2
+ require 'logger'
3
+
4
+ require "rack/contrib/sign/version"
5
+ require 'rack/contrib/sign/middleware'
6
+ require 'rack/contrib/sign/receipt'
7
+
8
+ module Rack
9
+ module Contrib
10
+ module Sign
11
+
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,109 @@
1
+
2
+ module Rack
3
+ module Contrib
4
+ module Sign
5
+ class Middleware
6
+ def initialize app, opts
7
+
8
+ @app = app
9
+ @logger = opts[:logger]
10
+ @realm = opts[:realm]
11
+ @credentials = opts[:credentials]
12
+ @header_prefix = (opts[:prefix] || "").gsub(/-/, '_').downcase
13
+ end
14
+
15
+ def call env
16
+ creds = extract_credentials env['HTTP_AUTHORIZATION']
17
+ unless creds
18
+ @logger.info "Denied: Authorization header not present or invalid."
19
+ return [401, {}, []]
20
+ end
21
+
22
+ receipt = build_receipt env, creds
23
+ unless receipt.api_secret
24
+ @logger.info "Denied: API key not recognized."
25
+ return [401, {}, []]
26
+ end
27
+
28
+ sign = receipt.to_s
29
+
30
+ digest = OpenSSL::Digest::Digest.new('sha1')
31
+ validation = OpenSSL::HMAC.hexdigest(digest, receipt.api_secret, sign)
32
+
33
+ unless validation == creds[:signature]
34
+ @logger.error "Denied: Authorization signature does not match"
35
+ @logger.info "Denied: EXPECTED: %s GOT: %s" % [
36
+ validation,
37
+ creds[:signature]
38
+ ]
39
+ @logger.debug "Generated signing data:"
40
+ @logger.debug sign
41
+ return [401, {}, []]
42
+ end
43
+
44
+ @app.call env
45
+ end
46
+
47
+ # Extract environmental data into a Receipt
48
+ def build_receipt env, credentials
49
+ receipt = Rack::Contrib::Sign::Receipt.new
50
+ receipt.uri = env['REQUEST_URI']
51
+ receipt.request_method = env['REQUEST_METHOD']
52
+ receipt.body = extract_body env
53
+
54
+ extract_headers(env).each { |h,v| receipt.headers[h] = v }
55
+
56
+ receipt.api_key = credentials[:key]
57
+ receipt.api_secret = get_secret(credentials[:key])
58
+
59
+ receipt
60
+ end
61
+
62
+ # Extract the body from the environment, ensuring to rewind
63
+ # the input back to zero, so future access gets the arguments.
64
+ def extract_body env
65
+ env['rack.input'].read
66
+ ensure
67
+ env['rack.input'].rewind
68
+ end
69
+
70
+ # Extract all the headers with our Prefix from the ENV
71
+ # and return the hash
72
+ def extract_headers env
73
+ headers = {}
74
+
75
+ env.sort_by { |k,v| k.to_s.downcase }.each do |key,val|
76
+ next unless key =~ /^http_#{@header_prefix}/i
77
+ header = key.sub(/^http_/i, '').gsub(/_/, '-')
78
+ headers[header] = val
79
+ end
80
+
81
+ headers
82
+ end
83
+
84
+ # Pass in the Authorization header, and get back the key
85
+ # and signature.
86
+ def extract_credentials auth_header
87
+ return false if auth_header.nil?
88
+
89
+ pattern = /^(?<realm>.*) (?<api_key>.*):(?<signature>.*)$/
90
+ matches = auth_header.match(pattern)
91
+
92
+ return false if matches.nil?
93
+ return false unless matches[:realm] == @realm
94
+
95
+ {
96
+ key: matches[:api_key],
97
+ signature: matches[:signature]
98
+ }
99
+ end
100
+
101
+ def get_secret api_key
102
+ return false unless @credentials.has_key? api_key
103
+
104
+ return @credentials.fetch(api_key)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,48 @@
1
+
2
+ module Rack
3
+ module Contrib
4
+ module Sign
5
+ class Receipt
6
+ attr_reader :request_method
7
+ attr_reader :headers
8
+ attr_accessor :api_key
9
+ attr_accessor :api_secret
10
+ attr_accessor :body
11
+ attr_accessor :uri
12
+
13
+ def initialize
14
+ @headers = {}
15
+ end
16
+
17
+ def request_method= s
18
+ @request_method = s.upcase
19
+ end
20
+
21
+ def to_s
22
+ preamble + header_text
23
+ end
24
+
25
+ def preamble
26
+ s = ""
27
+ s << "%s %s\n" % [request_method, uri]
28
+ s << "%s\n" % api_key
29
+ s << "%s\n" % api_secret
30
+ s << "%s\n" % body
31
+ s << "--\n"
32
+ s
33
+ end
34
+
35
+ def header_text
36
+ s = ""
37
+
38
+ headers.sort_by { |k,v| k.downcase }.each do |header, value|
39
+ s << "%s:%s\n" % [header.downcase, value]
40
+ end
41
+
42
+ s
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,7 @@
1
+ module Rack
2
+ module Contrib
3
+ module Sign
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rack/contrib/sign/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rack-contrib-sign"
8
+ spec.version = Rack::Contrib::Sign::VERSION
9
+ spec.authors = ["Graham Christensen"]
10
+ spec.email = ["info@zippykid.com"]
11
+ spec.description = %q{Implement secure API request igning.}
12
+ spec.summary = %q{Validates headers and API keys in Authorization}
13
+ spec.homepage = "https://github.com/zippykid/rack-contrib-sign"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "simplecov"
25
+ spec.add_development_dependency('guard')
26
+ spec.add_development_dependency('guard-rspec')
27
+
28
+ end
29
+
@@ -0,0 +1,174 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rack::Contrib::Sign::Middleware do
4
+ let (:app) do
5
+ app = double()
6
+ app.stub(:call => 'Hello, world!')
7
+
8
+ app
9
+ end
10
+ let (:log_string) { StringIO.new }
11
+ let (:logger) do
12
+ logger = Logger.new(log_string, Logger::DEBUG)
13
+ logger.formatter = proc do |severity, datetime, progname, msg|
14
+ "#{severity} - #{msg}\n"
15
+ end
16
+
17
+ logger
18
+ end
19
+ let (:cred_provider) { Hash.new }
20
+ let (:ware) { Rack::Contrib::Sign::Middleware.new(
21
+ app,
22
+ logger: logger,
23
+ realm: "foo-bar",
24
+ prefix: "HI-",
25
+ credentials: cred_provider,
26
+ )}
27
+
28
+ describe "#build_receipt" do
29
+ it "sets various options" do
30
+ env = {
31
+ 'REQUEST_METHOD' => 'POST',
32
+ 'REQUEST_URI' => 'foo/bar',
33
+ 'HTTP_HI_FOOO' => 'YIPEE',
34
+ 'rack.input' => StringIO.new('foo=bar')
35
+ }
36
+ cred_provider['123'] = 'abc'
37
+ creds = { key: '123', signature: 'foo' }
38
+
39
+ receipt = ware.build_receipt(env, creds)
40
+
41
+ receipt.uri.should eq('foo/bar')
42
+ receipt.request_method.should eq('POST')
43
+ receipt.body.should eq('foo=bar')
44
+ receipt.api_key.should eq('123')
45
+ receipt.api_secret.should eq('abc')
46
+ receipt.headers.should eq({
47
+ 'HI-FOOO' => 'YIPEE',
48
+ })
49
+
50
+ end
51
+ end
52
+
53
+ describe "#extract_body" do
54
+ it "returns the body of a request from the environment" do
55
+ env = {
56
+ 'rack.input' => StringIO.new('fooz=baz')
57
+ }
58
+
59
+ ware.extract_body(env).should eq('fooz=baz')
60
+ end
61
+
62
+ it "rewinds the input after reading" do
63
+ str = StringIO.new('fooz=baz')
64
+ env = {
65
+ 'rack.input' => str
66
+ }
67
+
68
+ str.pos.should eq(0)
69
+ end
70
+ end
71
+
72
+
73
+ describe "#extract_headers" do
74
+ it "returns headers prefixed with HI-" do
75
+ headers = {
76
+ 'HTTP_HI_FOO' => ':/',
77
+ 'HTTP_BYE_FOO' => ':(',
78
+ 'HTTP_HI_oentuheou' => ':)'
79
+ }
80
+
81
+ found_headers = ware.extract_headers(headers)
82
+
83
+ expected_headers = {
84
+ 'HI-FOO' => ':/',
85
+ 'HI-oentuheou' => ':)'
86
+ }
87
+ found_headers.should eq(expected_headers)
88
+ end
89
+ end
90
+
91
+ describe "#extract_credentials" do
92
+ it "returns false when nil is provided" do
93
+ header = nil
94
+
95
+ ware.extract_credentials(header).should eq(false)
96
+ end
97
+
98
+ it "returns false when the header is garbled" do
99
+ header = "LOL WHAT I DON'T EVEN"
100
+
101
+ ware.extract_credentials(header).should eq(false)
102
+ end
103
+
104
+ it "returns my key and signature when a formatted string is provided" do
105
+ header = 'foo-bar mykey:signature'
106
+
107
+ ware.extract_credentials(header).should eq(key: 'mykey', signature: 'signature')
108
+ end
109
+
110
+ it "returns false if the realm fails to match" do
111
+ header = 'foo-bar-baz mykey:signature'
112
+
113
+ ware.extract_credentials(header).should eq(false)
114
+ end
115
+
116
+ end
117
+
118
+ describe "#call" do
119
+ context "I have no other tests" do
120
+ it "abandons ship if there is no authorization header" do
121
+ env = {}
122
+
123
+ returned = ware.call(env)
124
+
125
+ returned.should eq([401, {}, []])
126
+ log_string.string.should eq("INFO - Denied: Authorization header not present or invalid.\n")
127
+ end
128
+
129
+ it "abandons ship if the API key is not known" do
130
+ env = {
131
+ 'HTTP_AUTHORIZATION' => 'foo-bar 123:foo',
132
+ 'REQUEST_METHOD' => '?',
133
+ 'rack.input' => StringIO.new()
134
+ }
135
+
136
+ returned = ware.call(env)
137
+
138
+ returned.should eq([401, {}, []])
139
+ log_string.string.should eq("INFO - Denied: API key not recognized.\n")
140
+ end
141
+
142
+ it "401s when I don't sign it right" do
143
+ env = {
144
+ 'HTTP_AUTHORIZATION' => 'foo-bar abc:YABBA DABBA DOOO',
145
+ 'REQUEST_METHOD' => 'POST',
146
+ 'REQUEST_URI' => 'http://foo/bar/baz',
147
+ 'rack.input' => StringIO.new('foo=bar'),
148
+ }
149
+
150
+ returned = ware.call(env)
151
+
152
+ returned.should eq([401, {}, []])
153
+ end
154
+ it "works when I sign it right" do
155
+ cred_provider['123'] = 'abc'
156
+ env = {
157
+ 'HTTP_AUTHORIZATION' => 'foo-bar 123:0d501b6934dc0ec5f1452947a7afd108e41c91af',
158
+ 'REQUEST_METHOD' => 'POST',
159
+ 'REQUEST_URI' => 'http://foo/bar/baz',
160
+ 'rack.input' => StringIO.new('foo=bar'),
161
+ 'HTTP_HI_FOOOO' => 'aoenuoneuh',
162
+ 'HTTP_BYE_FOOO' => 'oeucorgcgc'
163
+ }
164
+
165
+ returned = ware.call(env)
166
+
167
+ log_string.string.should eq('')
168
+ app.should have_received(:call).once.with(env)
169
+ returned.should eq('Hello, world!')
170
+ end
171
+ end
172
+ end
173
+ end
174
+
@@ -0,0 +1,91 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ describe Rack::Contrib::Sign::Receipt do
5
+ let (:receipt) { Rack::Contrib::Sign::Receipt.new }
6
+ it { should respond_to(:api_key) }
7
+ it { should respond_to(:api_key=) }
8
+ it { should respond_to(:api_secret) }
9
+ it { should respond_to(:api_secret=) }
10
+ it { should respond_to(:body) }
11
+ it { should respond_to(:body=) }
12
+ it { should respond_to(:request_method) }
13
+ it { should respond_to(:request_method=) }
14
+ it { should respond_to(:uri) }
15
+ it { should respond_to(:uri=) }
16
+ it { should respond_to(:headers) }
17
+
18
+ describe "#request_method" do
19
+ it "upcases the method" do
20
+ receipt.request_method = 'post'
21
+ receipt.request_method.should eq('POST')
22
+ end
23
+ end
24
+
25
+ describe "#headers" do
26
+ it "defaults to an empty hash" do
27
+ receipt.headers.should eq({})
28
+ end
29
+
30
+ it "keeps around my headers" do
31
+ receipt.headers['A-a'] = 'foo'
32
+ receipt.headers['B-b'] = 'bar'
33
+
34
+ headers = receipt.headers
35
+
36
+ headers.should eq({
37
+ 'A-a' => 'foo',
38
+ 'B-b' => 'bar'
39
+ })
40
+ end
41
+ end
42
+
43
+ describe "#preamble" do
44
+ it "incorporates all the preamble elements in a string block" do
45
+ receipt.api_key = 'abc'
46
+ receipt.api_secret = '123'
47
+ receipt.body = 'foo=bar'
48
+ receipt.request_method = 'post'
49
+ receipt.uri = 'http://example.com/123'
50
+
51
+ returned = receipt.preamble
52
+
53
+ r = "POST http://example.com/123\n"
54
+ r << "abc\n"
55
+ r << "123\n"
56
+ r << "foo=bar\n"
57
+ r << "--\n"
58
+
59
+ returned.should eq(r)
60
+ end
61
+ end
62
+
63
+ describe "#header_test" do
64
+ it "sorts the headers alphabetically and lowercases them" do
65
+ receipt.headers['B-b'] = 'bar'
66
+ receipt.headers['A-a'] = 'foo'
67
+
68
+ headers = receipt.header_text
69
+
70
+ r = ""
71
+ r << "a-a:foo\n"
72
+ r << "b-b:bar\n"
73
+
74
+ headers.should eq(r)
75
+ end
76
+ end
77
+
78
+ describe "#to_s" do
79
+ it "mushes to gether the preamble and headers" do
80
+ receipt.stub(:preamble => "foo\n--\n")
81
+ receipt.stub(:header_text=> "bar\n")
82
+
83
+ returned = receipt.to_s
84
+
85
+ receipt.should have_received(:preamble)
86
+ receipt.should have_received(:header_text)
87
+ returned.should eq("foo\n--\nbar\n")
88
+ end
89
+ end
90
+ end
91
+
@@ -0,0 +1,11 @@
1
+
2
+ require "spec_helper"
3
+
4
+ describe Rack::Contrib::Sign do
5
+ describe "#VERSION" do
6
+ it "has a version" do
7
+ Rack::Contrib::Sign::VERSION.should_not eq('')
8
+ end
9
+ end
10
+ end
11
+
@@ -0,0 +1,14 @@
1
+ require 'simplecov'
2
+ require 'stringio'
3
+
4
+ SimpleCov.start do
5
+ add_group "Rack Code", "/lib"
6
+ end
7
+
8
+ require 'rspec'
9
+ require 'rack/contrib/sign'
10
+
11
+ RSpec.configure do |config|
12
+ config.mock_with :rspec
13
+ end
14
+
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-contrib-sign
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Graham Christensen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Implement secure API request igning.
98
+ email:
99
+ - info@zippykid.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - .rspec
106
+ - Gemfile
107
+ - Guardfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - lib/rack/contrib/sign.rb
112
+ - lib/rack/contrib/sign/middleware.rb
113
+ - lib/rack/contrib/sign/receipt.rb
114
+ - lib/rack/contrib/sign/version.rb
115
+ - rack-contrib-sign.gemspec
116
+ - spec/rack/contrib/sign/middleware_spec.rb
117
+ - spec/rack/contrib/sign/receipt_spec.rb
118
+ - spec/rack/contrib/sign/version_spec.rb
119
+ - spec/spec_helper.rb
120
+ homepage: https://github.com/zippykid/rack-contrib-sign
121
+ licenses:
122
+ - MIT
123
+ metadata: {}
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ requirements: []
139
+ rubyforge_project:
140
+ rubygems_version: 2.0.5
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: Validates headers and API keys in Authorization
144
+ test_files:
145
+ - spec/rack/contrib/sign/middleware_spec.rb
146
+ - spec/rack/contrib/sign/receipt_spec.rb
147
+ - spec/rack/contrib/sign/version_spec.rb
148
+ - spec/spec_helper.rb