rack-api-key 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|