rack-api-key 0.0.2 → 0.0.3
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.
- data/README.md +9 -2
- data/lib/rack-api-key.rb +83 -61
- data/lib/rack-api-key/version.rb +1 -1
- data/spec/rack_api_key_spec.rb +195 -148
- metadata +4 -4
data/README.md
CHANGED
@@ -31,8 +31,10 @@ Or install it yourself as:
|
|
31
31
|
|
32
32
|
```ruby
|
33
33
|
use RackApiKey, :api_key_proc => Proc.new { |val| ApiKey.find(val) },
|
34
|
-
|
35
|
-
|
34
|
+
:rack_api_key => "account.api.key",
|
35
|
+
:header_key => "HTTP_X_CUSTOM_API_HEADER",
|
36
|
+
:url_restriction => [/api/],
|
37
|
+
:url_exclusion => [/api\/status/]
|
36
38
|
```
|
37
39
|
|
38
40
|
### :header_key
|
@@ -61,6 +63,11 @@ authentication and some that might not. Or a combination of API endpoints and
|
|
61
63
|
publicly facing webpages. Perhaps you've scoped all of your API endpoints to
|
62
64
|
"/api", and the rest of the URL mappings or routes are supposed to be wide open.
|
63
65
|
|
66
|
+
### :url_exclusion
|
67
|
+
This is an option to allow specific URLs to bypass rack-api-middleware authentication.
|
68
|
+
This works well when you require a single or few endpoints to not require
|
69
|
+
authentication. Perhaps you've scoped all of your API endpoints to "/api" but wish
|
70
|
+
to leave "/api/status" publically facing.
|
64
71
|
|
65
72
|
### unauthorized_api_key method
|
66
73
|
This is a method that can be overridden with however you'd like to respond
|
data/lib/rack-api-key.rb
CHANGED
@@ -2,94 +2,107 @@ require "rack-api-key/version"
|
|
2
2
|
|
3
3
|
##
|
4
4
|
# RackApiKey is a middleware that relies on the client submitting requests
|
5
|
-
# with a header named "X-API-KEY" storing their private API key as the value.
|
6
|
-
# The middleware will then intercept the request, read the value from the named
|
7
|
-
# header and call the given "proc" used for API key lookup. The API key lookup
|
8
|
-
# should only return a value if there is an exact match for the value stored in
|
9
|
-
# the named API key header.
|
10
|
-
#
|
5
|
+
# with a header named "X-API-KEY" storing their private API key as the value.
|
6
|
+
# The middleware will then intercept the request, read the value from the named
|
7
|
+
# header and call the given "proc" used for API key lookup. The API key lookup
|
8
|
+
# should only return a value if there is an exact match for the value stored in
|
9
|
+
# the named API key header.
|
10
|
+
#
|
11
|
+
# If such a API key exists, the middleware will pass the request onward and also
|
11
12
|
# set a new value in the request representing the authenticated API key. Otherwise
|
12
13
|
# the middleware will return a HTTP status of 401, and a plain text message
|
13
14
|
# notifying the calling requestor that they are not authorized.
|
14
15
|
class RackApiKey
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
17
|
+
##
|
18
|
+
# ==== Options
|
19
|
+
# * +:api_key_proc+ - **REQUIRED** A proc that is intended to lookup the API key in your datastore.
|
20
|
+
# The given proc should take an argument, namely the value of the API key header.
|
21
|
+
# There is no default value for this option and will raise a
|
22
|
+
# NotImplementedError if left unspecified.
|
23
|
+
#
|
24
|
+
# * +:rack_api_key+ - A way to override the key's name set in the request
|
25
|
+
# on successful authentication. The default value is
|
26
|
+
# "rack_api_key".
|
27
|
+
#
|
28
|
+
# * +:header_key+ - A way to override the header's name used to store the API key.
|
29
|
+
# The value given here should reflect how Rack interprets the
|
30
|
+
# header. For example if the client passes "X-API-KEY" Rack
|
31
|
+
# transforms interprets it as "HTTP_X_API_KEY". The default
|
32
|
+
# value is "HTTP_X_API_KEY".
|
33
|
+
#
|
34
|
+
# * +:url_restriction+ - A way to restrict specific URLs that should pass through
|
35
|
+
# the rack-api-key middleware. In order to use pass an Array of Regex patterns.
|
36
|
+
# If left unspecified all requests will pass through the rack-api-key
|
37
|
+
# middleware.
|
38
|
+
#
|
39
|
+
# * +:url_exclusion+ - A way to exclude specific URLs that should not pass through the
|
40
|
+
# the rack-api-middleware. In order to use, pass an Array of Regex patterns.
|
41
|
+
#
|
42
|
+
# ==== Example
|
43
|
+
# use RackApiKey,
|
44
|
+
# :api_key_proc => Proc.new { |key| ApiKey.where(:key => key).first },
|
45
|
+
# :rack_api_key => "authenticated.api.key",
|
46
|
+
# :header_key => "HTTP_X_SECRET_API_KEY",
|
47
|
+
# :url_restriction => [/api/],
|
48
|
+
# :url_exclusion => [/api\/status/]
|
49
|
+
|
50
|
+
def initialize(app, options = {})
|
51
|
+
@app = app
|
52
|
+
default_options = {
|
53
|
+
:header_key => 'HTTP_X_API_KEY',
|
54
|
+
:rack_api_key => 'rack_api_key',
|
55
|
+
:api_key_proc => Proc.new { raise NotImplementedError.new('Caller must implement a way to lookup an API key.') },
|
56
|
+
:url_restriction => [],
|
57
|
+
:url_exclusion => []
|
58
|
+
}
|
59
|
+
@options = default_options.merge(options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def call(env)
|
63
|
+
|
64
|
+
if constraint?(:url_exclusion) && url_matches(:url_exclusion, env)
|
65
|
+
|
66
|
+
@app.call(env)
|
67
|
+
|
68
|
+
elsif constraint?(:url_restriction)
|
69
|
+
|
70
|
+
url_matches(:url_restriction, env) ? process_request(env) : @app.call(env)
|
71
|
+
|
72
|
+
else
|
73
|
+
|
54
74
|
process_request(env)
|
55
|
-
|
56
|
-
request = Rack::Request.new(env)
|
57
|
-
url_matches = @options[:url_restriction].select { |url_regex| request.fullpath.match(url_regex) }
|
58
|
-
unless url_matches.empty?
|
59
|
-
process_request(env)
|
60
|
-
else
|
61
|
-
@app.call(env)
|
62
|
-
end
|
75
|
+
|
63
76
|
end
|
64
77
|
|
65
78
|
end
|
66
79
|
|
67
80
|
##
|
68
81
|
# Sets the API key lookup value in the request. Intentionally left here
|
69
|
-
# if anyone wants to change or override what does or does not get set
|
82
|
+
# if anyone wants to change or override what does or does not get set
|
70
83
|
# in the request.
|
71
84
|
def rack_api_key_request_setter(env, api_key_lookup_val)
|
72
|
-
|
85
|
+
env[@options[:rack_api_key]] = api_key_lookup_val
|
73
86
|
end
|
74
87
|
|
75
88
|
##
|
76
89
|
# Returns a 401 HTTP status code when an API key is not found or is not
|
77
90
|
# authorized. Intentionally left here if anyone wants to override this
|
78
|
-
# functionality, specifically change the format of the message or the
|
91
|
+
# functionality, specifically change the format of the message or the
|
79
92
|
# media type.
|
80
|
-
|
81
|
-
body_text =
|
93
|
+
def unauthorized_api_key
|
94
|
+
body_text = 'The API key provided is not authorized.'
|
82
95
|
[401, {'Content-Type' => 'text/plain; charset=utf-8',
|
83
|
-
|
96
|
+
'Content-Length' => body_text.size.to_s}, [body_text]]
|
84
97
|
end
|
85
98
|
|
86
99
|
##
|
87
100
|
# Checks if the API key header value is present and the API key
|
88
101
|
# that was returned from the API key proc is present.
|
89
102
|
# Intentionally left here is anyone wants to override this functionality.
|
90
|
-
|
91
|
-
!api_header_val.nil? && api_header_val !=
|
92
|
-
!api_key_lookup_val.nil? && api_key_lookup_val !=
|
103
|
+
def valid_api_key?(api_header_val, api_key_lookup_val)
|
104
|
+
!api_header_val.nil? && api_header_val != '' &&
|
105
|
+
!api_key_lookup_val.nil? && api_key_lookup_val != ''
|
93
106
|
end
|
94
107
|
|
95
108
|
private
|
@@ -106,4 +119,13 @@ class RackApiKey
|
|
106
119
|
end
|
107
120
|
end
|
108
121
|
|
122
|
+
def constraint?(key)
|
123
|
+
!(@options[key].nil? || @options[key].empty?)
|
124
|
+
end
|
125
|
+
|
126
|
+
def url_matches(key, env)
|
127
|
+
path = Rack::Request.new(env).fullpath
|
128
|
+
@options[key].select { |url_regex| path.match(url_regex) }.empty? ? false : true
|
129
|
+
end
|
130
|
+
|
109
131
|
end
|
data/lib/rack-api-key/version.rb
CHANGED
data/spec/rack_api_key_spec.rb
CHANGED
@@ -1,164 +1,211 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe RackApiKey do
|
4
|
-
|
4
|
+
include Rack::Test::Methods
|
5
5
|
|
6
|
-
|
6
|
+
class Account; end
|
7
7
|
|
8
|
-
|
8
|
+
class ApiKey
|
9
9
|
|
10
|
-
|
10
|
+
attr_reader :value
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
def initialize(value)
|
13
|
+
@value = value
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
def self.find(value)
|
17
|
+
nil
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
def account
|
21
|
+
Account.new
|
22
|
+
end
|
23
23
|
|
24
|
-
|
24
|
+
end
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
# simple test app for the middleware test
|
27
|
+
def app
|
28
28
|
|
29
|
-
|
29
|
+
Rack::Builder.new do
|
30
30
|
map '/' do
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
ApiKey.stub(:find).and_return(api_key = ApiKey.new("SECRET API KEY"))
|
91
|
-
get "/", {}, "HTTP_X_API_KEY" => "SECRET API KEY"
|
92
|
-
last_request.env['rack_api_key'].should == api_key
|
93
|
-
end
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
context "when the API key lookup proc is not provided" do
|
98
|
-
|
99
|
-
it 'raises an error' do
|
100
|
-
expect { get "/no-api-proc", {}, {} }.to raise_error(NotImplementedError, "Caller must implement a way to lookup an API key.")
|
101
|
-
end
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
context "when all the options are specified" do
|
106
|
-
|
107
|
-
it 'attempts to find the ApiKey with the header value' do
|
108
|
-
ApiKey.should_receive(:find).with("SECRET API KEY")
|
109
|
-
get "/all-options", {}, "HTTP_X_CUSTOM_API_HEADER" => "SECRET API KEY"
|
110
|
-
end
|
111
|
-
|
112
|
-
it 'responds with a 200 upon successful authorization' do
|
113
|
-
ApiKey.stub(:find).and_return(ApiKey.new("SECRET API KEY"))
|
114
|
-
get "/all-options", {}, "HTTP_X_CUSTOM_API_HEADER" => "SECRET API KEY"
|
115
|
-
last_response.ok?.should be_true
|
116
|
-
end
|
117
|
-
|
118
|
-
it 'sets the api key in the env' do
|
119
|
-
ApiKey.stub(:find).and_return(api_key = ApiKey.new("SECRET API KEY"))
|
120
|
-
get "/all-options", {}, "HTTP_X_CUSTOM_API_HEADER" => "SECRET API KEY"
|
121
|
-
last_request.env['account.api.key'].should == api_key
|
122
|
-
end
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
context "when URL restriction is used" do
|
127
|
-
|
128
|
-
context "and the requesting URL matches the restricted list" do
|
129
|
-
|
130
|
-
it 'attempts to find the ApiKey with the header value' do
|
131
|
-
ApiKey.should_receive(:find).with("SECRET API KEY")
|
132
|
-
get "/url-restricted/foo", {}, "HTTP_X_API_KEY" => "SECRET API KEY"
|
133
|
-
end
|
134
|
-
|
135
|
-
it 'responds with a 200 upon successful authorization' do
|
136
|
-
ApiKey.stub(:find).and_return(ApiKey.new("SECRET API KEY"))
|
137
|
-
get "/url-restricted/foo", {}, "HTTP_X_API_KEY" => "SECRET API KEY"
|
138
|
-
last_response.ok?.should be_true
|
139
|
-
end
|
140
|
-
|
141
|
-
it 'responds with a 401 when the header is not set' do
|
142
|
-
header("HTTP_X_API_KEY", nil)
|
143
|
-
get "/url-restricted/foo"
|
144
|
-
last_response.status.should == 401
|
145
|
-
end
|
146
|
-
|
147
|
-
end
|
148
|
-
|
149
|
-
context "and the requesting URL is not in the restricted list" do
|
150
|
-
|
151
|
-
it 'does not attempt to find the ApiKey with the header value' do
|
152
|
-
ApiKey.should_not_receive(:find).with("SECRET API KEY")
|
153
|
-
get "/url-restricted/bar"
|
154
|
-
end
|
31
|
+
use RackApiKey, :api_key_proc => Proc.new { |val| ApiKey.find(val) }
|
32
|
+
run lambda { |env| [200, {"Content-Type" => "text/html"}, "Testing Middleware"] }
|
33
|
+
end
|
34
|
+
|
35
|
+
map "/no-api-proc" do
|
36
|
+
use RackApiKey
|
37
|
+
run lambda { |env| [200, {"Content-Type" => "text/html"}, "Testing Middleware"] }
|
38
|
+
end
|
39
|
+
|
40
|
+
map "/all-options" do
|
41
|
+
use RackApiKey,
|
42
|
+
:api_key_proc => Proc.new { |val| ApiKey.find(val) },
|
43
|
+
:rack_api_key => "account.api.key",
|
44
|
+
:header_key => "HTTP_X_CUSTOM_API_HEADER"
|
45
|
+
run lambda { |env| [200, {"Content-Type" => "text/html"}, "Testing Middleware"] }
|
46
|
+
end
|
47
|
+
|
48
|
+
map "/url-restricted" do
|
49
|
+
use RackApiKey,
|
50
|
+
:api_key_proc => Proc.new { |val| ApiKey.find(val) },
|
51
|
+
:url_restriction => [/url-restricted\/foo/]
|
52
|
+
run lambda { |env| [200, {"Content-Type" => "text/html"}, "Testing Middleware"] }
|
53
|
+
end
|
54
|
+
|
55
|
+
map '/url-exclusion' do
|
56
|
+
use RackApiKey,
|
57
|
+
:api_key_proc => Proc.new { |val| ApiKey.find(val) },
|
58
|
+
:url_exclusion => [/url-exclusion\/foo/]
|
59
|
+
run lambda { |env| [200, { 'Content-Type' => 'text/html' }, 'Testing Middleware'] }
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
context "when using the predefined default middleware options" do
|
67
|
+
|
68
|
+
it 'attempts to find the ApiKey with the header value' do
|
69
|
+
ApiKey.should_receive(:find).with("SECRET API KEY")
|
70
|
+
get "/", {}, "HTTP_X_API_KEY" => "SECRET API KEY"
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'responds with a 200 upon successful authorization' do
|
74
|
+
ApiKey.stub(:find).and_return(ApiKey.new("SECRET API KEY"))
|
75
|
+
get "/", {}, "HTTP_X_API_KEY" => "SECRET API KEY"
|
76
|
+
last_response.ok?.should be_true
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'responds with a 401 when the ApiKey is not found' do
|
80
|
+
ApiKey.stub(:find).and_return(nil)
|
81
|
+
get "/", {}, "HTTP_X_API_KEY" => "SECRET API KEY"
|
82
|
+
last_response.status.should == 401
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'responds with a 401 when the header is not set' do
|
86
|
+
header("HTTP_X_API_KEY", nil)
|
87
|
+
get "/"
|
88
|
+
last_response.status.should == 401
|
89
|
+
end
|
155
90
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
end
|
91
|
+
it 'responds with a JSON formatted error message' do
|
92
|
+
header("HTTP_X_API_KEY", nil)
|
93
|
+
get "/"
|
94
|
+
last_response.body.should == "The API key provided is not authorized."
|
95
|
+
end
|
162
96
|
|
163
|
-
|
97
|
+
it 'sets the api key in the env' do
|
98
|
+
ApiKey.stub(:find).and_return(api_key = ApiKey.new("SECRET API KEY"))
|
99
|
+
get "/", {}, "HTTP_X_API_KEY" => "SECRET API KEY"
|
100
|
+
last_request.env['rack_api_key'].should == api_key
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
context "when the API key lookup proc is not provided" do
|
106
|
+
|
107
|
+
it 'raises an error' do
|
108
|
+
expect { get "/no-api-proc", {}, {} }.to raise_error(NotImplementedError, "Caller must implement a way to lookup an API key.")
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
context "when all the options are specified" do
|
114
|
+
|
115
|
+
it 'attempts to find the ApiKey with the header value' do
|
116
|
+
ApiKey.should_receive(:find).with("SECRET API KEY")
|
117
|
+
get "/all-options", {}, "HTTP_X_CUSTOM_API_HEADER" => "SECRET API KEY"
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'responds with a 200 upon successful authorization' do
|
121
|
+
ApiKey.stub(:find).and_return(ApiKey.new("SECRET API KEY"))
|
122
|
+
get "/all-options", {}, "HTTP_X_CUSTOM_API_HEADER" => "SECRET API KEY"
|
123
|
+
last_response.ok?.should be_true
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'sets the api key in the env' do
|
127
|
+
ApiKey.stub(:find).and_return(api_key = ApiKey.new("SECRET API KEY"))
|
128
|
+
get "/all-options", {}, "HTTP_X_CUSTOM_API_HEADER" => "SECRET API KEY"
|
129
|
+
last_request.env['account.api.key'].should == api_key
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
context "when URL restriction is used" do
|
135
|
+
|
136
|
+
context "and the requesting URL matches the restricted list" do
|
137
|
+
|
138
|
+
it 'attempts to find the ApiKey with the header value' do
|
139
|
+
ApiKey.should_receive(:find).with("SECRET API KEY")
|
140
|
+
get "/url-restricted/foo", {}, "HTTP_X_API_KEY" => "SECRET API KEY"
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'responds with a 200 upon successful authorization' do
|
144
|
+
ApiKey.stub(:find).and_return(ApiKey.new("SECRET API KEY"))
|
145
|
+
get "/url-restricted/foo", {}, "HTTP_X_API_KEY" => "SECRET API KEY"
|
146
|
+
last_response.ok?.should be_true
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'responds with a 401 when the header is not set' do
|
150
|
+
header("HTTP_X_API_KEY", nil)
|
151
|
+
get "/url-restricted/foo"
|
152
|
+
last_response.status.should == 401
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
context "and the requesting URL is not in the restricted list" do
|
158
|
+
|
159
|
+
it 'does not attempt to find the ApiKey with the header value' do
|
160
|
+
ApiKey.should_not_receive(:find).with("SECRET API KEY")
|
161
|
+
get "/url-restricted/bar"
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'responds with a 200 without the header set' do
|
165
|
+
get "/url-restricted/bar"
|
166
|
+
last_response.ok?.should be_true
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'when URL exclusion is used' do
|
174
|
+
|
175
|
+
context 'and the requesting URL matches the excluded list' do
|
176
|
+
|
177
|
+
it 'does not attempt to find the ApiKey with the header value' do
|
178
|
+
ApiKey.should_not_receive(:find)
|
179
|
+
get '/url-exclusion/foo'
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'responds with a 200 without the header set' do
|
183
|
+
get '/url-exclusion/foo'
|
184
|
+
last_response.should be_ok
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
context 'and the requesting URL is not in the exclusion list' do
|
190
|
+
|
191
|
+
it 'attempts to find the ApiKey with the header value' do
|
192
|
+
ApiKey.should_receive(:find).with('SECRET API KEY')
|
193
|
+
get '/url-exclusion/bar', {}, 'HTTP_X_API_KEY' => 'SECRET API KEY'
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'responds with a 200 upon successful authorization' do
|
197
|
+
ApiKey.stub(:find).and_return(ApiKey.new('SECRET API KEY'))
|
198
|
+
get '/url-exclusion/bar', {}, 'HTTP_X_API_KEY' => 'SECRET API KEY'
|
199
|
+
last_response.should be_ok
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'responds with a 401 when the header is not set' do
|
203
|
+
header('HTTP_X_API_KEY', nil)
|
204
|
+
get '/url-exclusion/bar'
|
205
|
+
last_response.status.should eq 401
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
164
211
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-api-key
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -107,7 +107,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
107
|
version: '0'
|
108
108
|
segments:
|
109
109
|
- 0
|
110
|
-
hash:
|
110
|
+
hash: -216640359850820027
|
111
111
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
112
|
none: false
|
113
113
|
requirements:
|
@@ -116,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
116
|
version: '0'
|
117
117
|
segments:
|
118
118
|
- 0
|
119
|
-
hash:
|
119
|
+
hash: -216640359850820027
|
120
120
|
requirements: []
|
121
121
|
rubyforge_project:
|
122
122
|
rubygems_version: 1.8.25
|