grape 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -2
- data/.rubocop_todo.yml +80 -0
- data/.travis.yml +2 -2
- data/CHANGELOG.md +21 -2
- data/Gemfile +1 -6
- data/Guardfile +1 -5
- data/README.md +110 -27
- data/Rakefile +1 -1
- data/UPGRADING.md +35 -0
- data/grape.gemspec +5 -2
- data/lib/grape.rb +20 -4
- data/lib/grape/api.rb +25 -467
- data/lib/grape/api/helpers.rb +7 -0
- data/lib/grape/dsl/callbacks.rb +27 -0
- data/lib/grape/dsl/configuration.rb +27 -0
- data/lib/grape/dsl/helpers.rb +86 -0
- data/lib/grape/dsl/inside_route.rb +227 -0
- data/lib/grape/dsl/middleware.rb +33 -0
- data/lib/grape/dsl/parameters.rb +79 -0
- data/lib/grape/dsl/request_response.rb +152 -0
- data/lib/grape/dsl/routing.rb +172 -0
- data/lib/grape/dsl/validations.rb +29 -0
- data/lib/grape/endpoint.rb +6 -226
- data/lib/grape/error_formatter/base.rb +28 -0
- data/lib/grape/error_formatter/json.rb +2 -0
- data/lib/grape/error_formatter/txt.rb +2 -0
- data/lib/grape/error_formatter/xml.rb +2 -0
- data/lib/grape/exceptions/base.rb +6 -0
- data/lib/grape/exceptions/validation.rb +3 -3
- data/lib/grape/exceptions/validation_errors.rb +19 -6
- data/lib/grape/locale/en.yml +5 -3
- data/lib/grape/middleware/auth/base.rb +28 -12
- data/lib/grape/middleware/auth/dsl.rb +35 -0
- data/lib/grape/middleware/auth/strategies.rb +24 -0
- data/lib/grape/middleware/auth/strategy_info.rb +15 -0
- data/lib/grape/validations.rb +3 -92
- data/lib/grape/validations/at_least_one_of.rb +25 -0
- data/lib/grape/validations/coerce.rb +2 -2
- data/lib/grape/validations/exactly_one_of.rb +2 -2
- data/lib/grape/validations/mutual_exclusion.rb +2 -2
- data/lib/grape/validations/presence.rb +1 -1
- data/lib/grape/validations/regexp.rb +1 -1
- data/lib/grape/validations/values.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/helpers_spec.rb +36 -0
- data/spec/grape/api_spec.rb +72 -19
- data/spec/grape/dsl/callbacks_spec.rb +44 -0
- data/spec/grape/dsl/configuration_spec.rb +37 -0
- data/spec/grape/dsl/helpers_spec.rb +54 -0
- data/spec/grape/dsl/inside_route_spec.rb +222 -0
- data/spec/grape/dsl/middleware_spec.rb +40 -0
- data/spec/grape/dsl/parameters_spec.rb +108 -0
- data/spec/grape/dsl/request_response_spec.rb +123 -0
- data/spec/grape/dsl/routing_spec.rb +132 -0
- data/spec/grape/dsl/validations_spec.rb +55 -0
- data/spec/grape/endpoint_spec.rb +60 -11
- data/spec/grape/entity_spec.rb +9 -4
- data/spec/grape/exceptions/validation_errors_spec.rb +31 -1
- data/spec/grape/middleware/auth/base_spec.rb +34 -0
- data/spec/grape/middleware/auth/dsl_spec.rb +53 -0
- data/spec/grape/middleware/auth/strategies_spec.rb +81 -0
- data/spec/grape/middleware/error_spec.rb +33 -1
- data/spec/grape/middleware/exception_spec.rb +13 -0
- data/spec/grape/validations/at_least_one_of_spec.rb +63 -0
- data/spec/grape/validations/exactly_one_of_spec.rb +1 -1
- data/spec/grape/validations/presence_spec.rb +159 -122
- data/spec/grape/validations/zh-CN.yml +1 -1
- data/spec/grape/validations_spec.rb +77 -15
- data/spec/spec_helper.rb +1 -0
- data/spec/support/endpoint_faker.rb +23 -0
- metadata +93 -15
- data/lib/grape/middleware/auth/basic.rb +0 -13
- data/lib/grape/middleware/auth/digest.rb +0 -13
- data/lib/grape/middleware/auth/oauth2.rb +0 -83
- data/spec/grape/middleware/auth/basic_spec.rb +0 -31
- data/spec/grape/middleware/auth/digest_spec.rb +0 -47
- data/spec/grape/middleware/auth/oauth2_spec.rb +0 -135
@@ -1,83 +0,0 @@
|
|
1
|
-
module Grape
|
2
|
-
module Middleware
|
3
|
-
module Auth
|
4
|
-
# OAuth 2.0 authorization for Grape APIs.
|
5
|
-
class OAuth2 < Grape::Middleware::Base
|
6
|
-
def default_options
|
7
|
-
{
|
8
|
-
token_class: 'AccessToken',
|
9
|
-
realm: 'OAuth API',
|
10
|
-
parameter: %w(bearer_token oauth_token access_token),
|
11
|
-
accepted_headers: %w(HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION),
|
12
|
-
header: [/Bearer (.*)/i, /OAuth (.*)/i],
|
13
|
-
required: true
|
14
|
-
}
|
15
|
-
end
|
16
|
-
|
17
|
-
def before
|
18
|
-
verify_token(token_parameter || token_header)
|
19
|
-
end
|
20
|
-
|
21
|
-
def request
|
22
|
-
@request ||= Grape::Request.new(env)
|
23
|
-
end
|
24
|
-
|
25
|
-
def params
|
26
|
-
@params ||= request.params
|
27
|
-
end
|
28
|
-
|
29
|
-
def token_parameter
|
30
|
-
Array(options[:parameter]).each do |p|
|
31
|
-
return params[p] if params[p]
|
32
|
-
end
|
33
|
-
nil
|
34
|
-
end
|
35
|
-
|
36
|
-
def token_header
|
37
|
-
return false unless authorization_header
|
38
|
-
Array(options[:header]).each do |regexp|
|
39
|
-
return $1 if authorization_header =~ regexp
|
40
|
-
end
|
41
|
-
nil
|
42
|
-
end
|
43
|
-
|
44
|
-
def authorization_header
|
45
|
-
options[:accepted_headers].each do |head|
|
46
|
-
return env[head] if env[head]
|
47
|
-
end
|
48
|
-
nil
|
49
|
-
end
|
50
|
-
|
51
|
-
def token_class
|
52
|
-
@klass ||= eval(options[:token_class]) # rubocop:disable Eval
|
53
|
-
end
|
54
|
-
|
55
|
-
def verify_token(token)
|
56
|
-
token = token_class.verify(token)
|
57
|
-
if token
|
58
|
-
if token.respond_to?(:expired?) && token.expired?
|
59
|
-
error_out(401, 'invalid_grant')
|
60
|
-
else
|
61
|
-
if !token.respond_to?(:permission_for?) || token.permission_for?(env)
|
62
|
-
env['api.token'] = token
|
63
|
-
else
|
64
|
-
error_out(403, 'insufficient_scope')
|
65
|
-
end
|
66
|
-
end
|
67
|
-
elsif !!options[:required]
|
68
|
-
error_out(401, 'invalid_grant')
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def error_out(status, error)
|
73
|
-
throw :error,
|
74
|
-
message: error,
|
75
|
-
status: status,
|
76
|
-
headers: {
|
77
|
-
'WWW-Authenticate' => "OAuth realm='#{options[:realm]}', error='#{error}'"
|
78
|
-
}
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
require 'base64'
|
4
|
-
|
5
|
-
describe Grape::Middleware::Auth::Basic do
|
6
|
-
def app
|
7
|
-
Rack::Builder.new do |b|
|
8
|
-
b.use Grape::Middleware::Error
|
9
|
-
b.use(Grape::Middleware::Auth::Basic) do |u, p|
|
10
|
-
u && p && u == p
|
11
|
-
end
|
12
|
-
b.run lambda { |env| [200, {}, ["Hello there."]] }
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'throws a 401 if no auth is given' do
|
17
|
-
@proc = lambda { false }
|
18
|
-
get '/whatever'
|
19
|
-
expect(last_response.status).to eq(401)
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'authenticates if given valid creds' do
|
23
|
-
get '/whatever', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('admin', 'admin')
|
24
|
-
expect(last_response.status).to eq(200)
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'throws a 401 is wrong auth is given' do
|
28
|
-
get '/whatever', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('admin', 'wrong')
|
29
|
-
expect(last_response.status).to eq(401)
|
30
|
-
end
|
31
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec::Matchers.define :be_challenge do
|
4
|
-
match do |actual_response|
|
5
|
-
actual_response.status == 401 &&
|
6
|
-
actual_response['WWW-Authenticate'] =~ /^Digest / &&
|
7
|
-
actual_response.body.empty?
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
class Test < Grape::API
|
12
|
-
http_digest(realm: 'Test Api', opaque: 'secret') do |username|
|
13
|
-
{ 'foo' => 'bar' }[username]
|
14
|
-
end
|
15
|
-
|
16
|
-
get '/test' do
|
17
|
-
[{ hey: 'you' }, { there: 'bar' }, { foo: 'baz' }]
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
describe Grape::Middleware::Auth::Digest do
|
22
|
-
def app
|
23
|
-
Test
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'is a digest authentication challenge' do
|
27
|
-
get '/test'
|
28
|
-
expect(last_response).to be_challenge
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'throws a 401 if no auth is given' do
|
32
|
-
get '/test'
|
33
|
-
expect(last_response.status).to eq(401)
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'authenticates if given valid creds' do
|
37
|
-
digest_authorize "foo", "bar"
|
38
|
-
get '/test'
|
39
|
-
expect(last_response.status).to eq(200)
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'throws a 401 if given invalid creds' do
|
43
|
-
digest_authorize "bar", "foo"
|
44
|
-
get '/test'
|
45
|
-
expect(last_response.status).to eq(401)
|
46
|
-
end
|
47
|
-
end
|
@@ -1,135 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Grape::Middleware::Auth::OAuth2 do
|
4
|
-
class FakeToken
|
5
|
-
attr_accessor :token
|
6
|
-
|
7
|
-
def self.verify(token)
|
8
|
-
FakeToken.new(token) if !!token && %w(g e).include?(token[0..0])
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize(token)
|
12
|
-
@token = token
|
13
|
-
end
|
14
|
-
|
15
|
-
def expired?
|
16
|
-
@token[0..0] == 'e'
|
17
|
-
end
|
18
|
-
|
19
|
-
def permission_for?(env)
|
20
|
-
env['PATH_INFO'] == '/forbidden' ? false : true
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def app
|
25
|
-
Rack::Builder.app do
|
26
|
-
use Grape::Middleware::Auth::OAuth2, token_class: 'FakeToken'
|
27
|
-
run lambda { |env| [200, {}, [(env['api.token'].token if env['api.token'])]] }
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'with the token in the query string' do
|
32
|
-
context 'and a valid token' do
|
33
|
-
before { get '/awesome?access_token=g123' }
|
34
|
-
|
35
|
-
it 'sets env["api.token"]' do
|
36
|
-
expect(last_response.body).to eq('g123')
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
context 'and an invalid token' do
|
41
|
-
before do
|
42
|
-
@err = catch :error do
|
43
|
-
get '/awesome?access_token=b123'
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'throws an error' do
|
48
|
-
expect(@err[:status]).to eq(401)
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'sets the WWW-Authenticate header in the response' do
|
52
|
-
expect(@err[:headers]['WWW-Authenticate']).to eq("OAuth realm='OAuth API', error='invalid_grant'")
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
context 'with an expired token' do
|
58
|
-
before do
|
59
|
-
@err = catch :error do
|
60
|
-
get '/awesome?access_token=e123'
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'throws an error' do
|
65
|
-
expect(@err[:status]).to eq(401)
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'sets the WWW-Authenticate header in the response to error' do
|
69
|
-
expect(@err[:headers]['WWW-Authenticate']).to eq("OAuth realm='OAuth API', error='invalid_grant'")
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
%w(HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION).each do |head|
|
74
|
-
context "with the token in the #{head} header" do
|
75
|
-
before do
|
76
|
-
get '/awesome', {}, head => 'OAuth g123'
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'sets env["api.token"]' do
|
80
|
-
expect(last_response.body).to eq('g123')
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
context 'with the token in the POST body' do
|
86
|
-
before do
|
87
|
-
post '/awesome', 'access_token' => 'g123'
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'sets env["api.token"]' do
|
91
|
-
expect(last_response.body).to eq('g123')
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
context 'when accessing something outside its scope' do
|
96
|
-
before do
|
97
|
-
@err = catch :error do
|
98
|
-
get '/forbidden?access_token=g123'
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
it 'throws an error' do
|
103
|
-
expect(@err[:status]).to eq(403)
|
104
|
-
end
|
105
|
-
|
106
|
-
it 'sets the WWW-Authenticate header in the response to error' do
|
107
|
-
expect(@err[:headers]['WWW-Authenticate']).to eq("OAuth realm='OAuth API', error='insufficient_scope'")
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
context 'when authorization is not required' do
|
112
|
-
def app
|
113
|
-
Rack::Builder.app do
|
114
|
-
use Grape::Middleware::Auth::OAuth2, token_class: 'FakeToken', required: false
|
115
|
-
run lambda { |env| [200, {}, [(env['api.token'].token if env['api.token'])]] }
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
context 'with no token' do
|
120
|
-
before { post '/awesome' }
|
121
|
-
|
122
|
-
it 'succeeds anyway' do
|
123
|
-
expect(last_response.status).to eq(200)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
context 'with a valid token' do
|
128
|
-
before { get '/awesome?access_token=g123' }
|
129
|
-
|
130
|
-
it 'sets env["api.token"]' do
|
131
|
-
expect(last_response.body).to eq('g123')
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|