grape 0.3.0 → 0.7.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 +15 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +70 -0
- data/.travis.yml +7 -6
- data/CHANGELOG.md +134 -4
- data/CONTRIBUTING.md +118 -0
- data/Gemfile +5 -2
- data/README.md +551 -116
- data/RELEASING.md +105 -0
- data/Rakefile +29 -8
- data/UPGRADING.md +124 -0
- data/grape.gemspec +3 -3
- data/lib/grape/api.rb +207 -88
- data/lib/grape/cookies.rb +4 -8
- data/lib/grape/endpoint.rb +198 -144
- data/lib/grape/error_formatter/base.rb +5 -7
- data/lib/grape/error_formatter/json.rb +3 -5
- data/lib/grape/error_formatter/txt.rb +1 -3
- data/lib/grape/error_formatter/xml.rb +4 -6
- data/lib/grape/exceptions/base.rb +9 -9
- data/lib/grape/exceptions/incompatible_option_values.rb +10 -0
- data/lib/grape/exceptions/invalid_formatter.rb +1 -4
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -5
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -6
- data/lib/grape/exceptions/missing_mime_type.rb +1 -5
- data/lib/grape/exceptions/missing_option.rb +1 -4
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -4
- data/lib/grape/exceptions/unknown_options.rb +1 -5
- data/lib/grape/exceptions/unknown_validator.rb +1 -3
- data/lib/grape/exceptions/validation.rb +13 -3
- data/lib/grape/exceptions/validation_errors.rb +43 -0
- data/lib/grape/formatter/base.rb +5 -7
- data/lib/grape/formatter/json.rb +0 -3
- data/lib/grape/formatter/serializable_hash.rb +15 -15
- data/lib/grape/formatter/txt.rb +0 -2
- data/lib/grape/formatter/xml.rb +0 -2
- data/lib/grape/http/request.rb +26 -0
- data/lib/grape/locale/en.yml +8 -5
- data/lib/grape/middleware/auth/base.rb +30 -0
- data/lib/grape/middleware/auth/basic.rb +3 -20
- data/lib/grape/middleware/auth/digest.rb +2 -19
- data/lib/grape/middleware/auth/oauth2.rb +31 -24
- data/lib/grape/middleware/base.rb +7 -7
- data/lib/grape/middleware/error.rb +36 -22
- data/lib/grape/middleware/filter.rb +3 -3
- data/lib/grape/middleware/formatter.rb +99 -61
- data/lib/grape/middleware/globals.rb +13 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +67 -0
- data/lib/grape/middleware/versioner/header.rb +22 -16
- data/lib/grape/middleware/versioner/param.rb +9 -11
- data/lib/grape/middleware/versioner/path.rb +10 -13
- data/lib/grape/middleware/versioner.rb +3 -1
- data/lib/grape/namespace.rb +23 -0
- data/lib/grape/parser/base.rb +3 -5
- data/lib/grape/parser/json.rb +0 -2
- data/lib/grape/parser/xml.rb +0 -2
- data/lib/grape/path.rb +70 -0
- data/lib/grape/route.rb +10 -6
- data/lib/grape/util/content_types.rb +2 -1
- data/lib/grape/util/deep_merge.rb +5 -5
- data/lib/grape/util/hash_stack.rb +13 -2
- data/lib/grape/validations/coerce.rb +11 -10
- data/lib/grape/validations/default.rb +25 -0
- data/lib/grape/validations/presence.rb +7 -3
- data/lib/grape/validations/regexp.rb +2 -5
- data/lib/grape/validations/values.rb +17 -0
- data/lib/grape/validations.rb +161 -54
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +19 -4
- data/spec/grape/api_spec.rb +897 -268
- data/spec/grape/endpoint_spec.rb +283 -66
- data/spec/grape/entity_spec.rb +132 -29
- data/spec/grape/exceptions/missing_mime_type_spec.rb +3 -9
- data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
- data/spec/grape/middleware/auth/basic_spec.rb +8 -8
- data/spec/grape/middleware/auth/digest_spec.rb +5 -5
- data/spec/grape/middleware/auth/oauth2_spec.rb +81 -36
- data/spec/grape/middleware/base_spec.rb +8 -13
- data/spec/grape/middleware/error_spec.rb +13 -17
- data/spec/grape/middleware/exception_spec.rb +47 -27
- data/spec/grape/middleware/formatter_spec.rb +103 -41
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +121 -0
- data/spec/grape/middleware/versioner/header_spec.rb +76 -51
- data/spec/grape/middleware/versioner/param_spec.rb +18 -18
- data/spec/grape/middleware/versioner/path_spec.rb +6 -6
- data/spec/grape/middleware/versioner_spec.rb +5 -2
- data/spec/grape/path_spec.rb +229 -0
- data/spec/grape/util/hash_stack_spec.rb +31 -32
- data/spec/grape/validations/coerce_spec.rb +116 -51
- data/spec/grape/validations/default_spec.rb +123 -0
- data/spec/grape/validations/presence_spec.rb +42 -44
- data/spec/grape/validations/regexp_spec.rb +9 -9
- data/spec/grape/validations/values_spec.rb +138 -0
- data/spec/grape/validations/zh-CN.yml +4 -3
- data/spec/grape/validations_spec.rb +681 -48
- data/spec/shared/versioning_examples.rb +22 -6
- data/spec/spec_helper.rb +3 -2
- data/spec/support/basic_auth_encode_helpers.rb +0 -1
- data/spec/support/content_type_helpers.rb +11 -0
- data/spec/support/versioned_helpers.rb +13 -5
- metadata +34 -84
@@ -1,9 +1,9 @@
|
|
1
1
|
module Grape
|
2
2
|
module Exceptions
|
3
3
|
class Base < StandardError
|
4
|
-
|
5
4
|
BASE_MESSAGES_KEY = 'grape.errors.messages'
|
6
5
|
BASE_ATTRIBUTES_KEY = 'grape.errors.attributes'
|
6
|
+
FALLBACK_LOCALE = :en
|
7
7
|
|
8
8
|
attr_reader :status, :message, :headers
|
9
9
|
|
@@ -14,20 +14,21 @@ module Grape
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def [](index)
|
17
|
-
|
17
|
+
send index
|
18
18
|
end
|
19
19
|
|
20
20
|
protected
|
21
|
+
|
21
22
|
# TODO: translate attribute first
|
22
23
|
# if BASE_ATTRIBUTES_KEY.key respond to a string message, then short_message is returned
|
23
24
|
# if BASE_ATTRIBUTES_KEY.key respond to a Hash, means it may have problem , summary and resolution
|
24
|
-
def compose_message(key, attributes = {}
|
25
|
+
def compose_message(key, attributes = {})
|
25
26
|
short_message = translate_message(key, attributes)
|
26
27
|
if short_message.is_a? Hash
|
27
28
|
@problem = problem(key, attributes)
|
28
29
|
@summary = summary(key, attributes)
|
29
30
|
@resolution = resolution(key, attributes)
|
30
|
-
[
|
31
|
+
[["Problem", @problem], ["Summary", @summary], ["Resolution", @resolution]].reduce("") do |message, detail_array|
|
31
32
|
message << "\n#{detail_array[0]}:\n #{detail_array[1]}" unless detail_array[1].blank?
|
32
33
|
message
|
33
34
|
end
|
@@ -48,19 +49,18 @@ module Grape
|
|
48
49
|
translate_message("#{key}.resolution", attributes)
|
49
50
|
end
|
50
51
|
|
51
|
-
|
52
52
|
def translate_attribute(key, options = {})
|
53
|
-
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", { :
|
53
|
+
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", { default: key }.merge(options))
|
54
54
|
end
|
55
55
|
|
56
56
|
def translate_message(key, options = {})
|
57
|
-
translate("#{BASE_MESSAGES_KEY}.#{key}", {:
|
57
|
+
translate("#{BASE_MESSAGES_KEY}.#{key}", { default: '' }.merge(options))
|
58
58
|
end
|
59
59
|
|
60
60
|
def translate(key, options = {})
|
61
|
-
::I18n.translate(key, options)
|
61
|
+
message = ::I18n.translate(key, options)
|
62
|
+
message.present? ? message : ::I18n.translate(key, options.merge(locale: FALLBACK_LOCALE))
|
62
63
|
end
|
63
|
-
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Grape
|
3
|
+
module Exceptions
|
4
|
+
class IncompatibleOptionValues < Base
|
5
|
+
def initialize(option1, value1, option2, value2)
|
6
|
+
super(message: compose_message("incompatible_option_values", option1: option1, value1: value1, option2: option2, value2: value2))
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -2,12 +2,9 @@
|
|
2
2
|
module Grape
|
3
3
|
module Exceptions
|
4
4
|
class InvalidFormatter < Base
|
5
|
-
|
6
5
|
def initialize(klass, to_format)
|
7
|
-
super(:
|
6
|
+
super(message: compose_message("invalid_formatter", klass: klass, to_format: to_format))
|
8
7
|
end
|
9
|
-
|
10
8
|
end
|
11
|
-
|
12
9
|
end
|
13
10
|
end
|
@@ -1,14 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Grape
|
3
3
|
module Exceptions
|
4
|
-
|
5
4
|
class InvalidVersionerOption < Base
|
6
|
-
|
7
5
|
def initialize(strategy)
|
8
|
-
super(:
|
6
|
+
super(message: compose_message("invalid_versioner_option", strategy: strategy))
|
9
7
|
end
|
10
|
-
|
11
8
|
end
|
12
|
-
|
13
9
|
end
|
14
10
|
end
|
@@ -2,14 +2,9 @@
|
|
2
2
|
module Grape
|
3
3
|
module Exceptions
|
4
4
|
class InvalidWithOptionForRepresent < Base
|
5
|
-
|
6
5
|
def initialize
|
7
|
-
super(:
|
6
|
+
super(message: compose_message("invalid_with_option_for_represent"))
|
8
7
|
end
|
9
|
-
|
10
8
|
end
|
11
|
-
|
12
9
|
end
|
13
|
-
|
14
10
|
end
|
15
|
-
|
@@ -2,13 +2,9 @@
|
|
2
2
|
module Grape
|
3
3
|
module Exceptions
|
4
4
|
class MissingMimeType < Base
|
5
|
-
|
6
5
|
def initialize(new_format)
|
7
|
-
super(:
|
6
|
+
super(message: compose_message("missing_mime_type", new_format: new_format))
|
8
7
|
end
|
9
|
-
|
10
8
|
end
|
11
|
-
|
12
9
|
end
|
13
|
-
|
14
10
|
end
|
@@ -2,12 +2,9 @@
|
|
2
2
|
module Grape
|
3
3
|
module Exceptions
|
4
4
|
class MissingOption < Base
|
5
|
-
|
6
5
|
def initialize(option)
|
7
|
-
super(:
|
6
|
+
super(message: compose_message("missing_option", option: option))
|
8
7
|
end
|
9
|
-
|
10
8
|
end
|
11
|
-
|
12
9
|
end
|
13
10
|
end
|
@@ -2,13 +2,9 @@
|
|
2
2
|
module Grape
|
3
3
|
module Exceptions
|
4
4
|
class UnknownOptions < Base
|
5
|
-
|
6
5
|
def initialize(options)
|
7
|
-
super(:
|
6
|
+
super(message: compose_message("unknown_options", options: options))
|
8
7
|
end
|
9
|
-
|
10
8
|
end
|
11
|
-
|
12
9
|
end
|
13
|
-
|
14
10
|
end
|
@@ -2,11 +2,9 @@
|
|
2
2
|
module Grape
|
3
3
|
module Exceptions
|
4
4
|
class UnknownValidator < Base
|
5
|
-
|
6
5
|
def initialize(validator_type)
|
7
|
-
super(:
|
6
|
+
super(message: compose_message("unknown_validator", validator_type: validator_type))
|
8
7
|
end
|
9
|
-
|
10
8
|
end
|
11
9
|
end
|
12
10
|
end
|
@@ -6,11 +6,21 @@ module Grape
|
|
6
6
|
attr_accessor :param
|
7
7
|
|
8
8
|
def initialize(args = {})
|
9
|
-
|
10
|
-
|
11
|
-
args[:message] = translate_message(args[:message_key]
|
9
|
+
raise "Param is missing:" unless args.has_key? :param
|
10
|
+
@param = args[:param]
|
11
|
+
args[:message] = translate_message(args[:message_key]) if args.has_key? :message_key
|
12
12
|
super
|
13
13
|
end
|
14
|
+
|
15
|
+
# remove all the unnecessary stuff from Grape::Exceptions::Base like status
|
16
|
+
# and headers when converting a validation error to json or string
|
17
|
+
def as_json(*args)
|
18
|
+
to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
message
|
23
|
+
end
|
14
24
|
end
|
15
25
|
end
|
16
26
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'grape/exceptions/base'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Exceptions
|
5
|
+
class ValidationErrors < Grape::Exceptions::Base
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :errors
|
9
|
+
|
10
|
+
def initialize(args = {})
|
11
|
+
@errors = {}
|
12
|
+
args[:errors].each do |validation_error|
|
13
|
+
@errors[validation_error.param] ||= []
|
14
|
+
@errors[validation_error.param] << validation_error
|
15
|
+
end
|
16
|
+
super message: full_messages.join(', '), status: 400
|
17
|
+
end
|
18
|
+
|
19
|
+
def each
|
20
|
+
errors.each_pair do |attribute, errors|
|
21
|
+
errors.each do |error|
|
22
|
+
yield attribute, error
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def full_messages
|
30
|
+
map { |attribute, error| full_message(attribute, error) }.uniq
|
31
|
+
end
|
32
|
+
|
33
|
+
def full_message(attribute, error)
|
34
|
+
I18n.t(
|
35
|
+
"grape.errors.format".to_sym,
|
36
|
+
default: "%{attribute} %{message}",
|
37
|
+
attribute: translate_attribute(attribute),
|
38
|
+
message: error.message
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/grape/formatter/base.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
module Grape
|
2
2
|
module Formatter
|
3
3
|
module Base
|
4
|
-
|
5
4
|
class << self
|
6
|
-
|
7
5
|
FORMATTERS = {
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
6
|
+
json: Grape::Formatter::Json,
|
7
|
+
jsonapi: Grape::Formatter::Json,
|
8
|
+
serializable_hash: Grape::Formatter::SerializableHash,
|
9
|
+
txt: Grape::Formatter::Txt,
|
10
|
+
xml: Grape::Formatter::Xml
|
12
11
|
}
|
13
12
|
|
14
13
|
def formatters(options)
|
@@ -26,7 +25,6 @@ module Grape
|
|
26
25
|
spec
|
27
26
|
end
|
28
27
|
end
|
29
|
-
|
30
28
|
end
|
31
29
|
end
|
32
30
|
end
|
data/lib/grape/formatter/json.rb
CHANGED
@@ -2,7 +2,6 @@ module Grape
|
|
2
2
|
module Formatter
|
3
3
|
module SerializableHash
|
4
4
|
class << self
|
5
|
-
|
6
5
|
def call(object, env)
|
7
6
|
return object if object.is_a?(String)
|
8
7
|
return MultiJson.dump(serialize(object)) if serializable?(object)
|
@@ -12,23 +11,24 @@ module Grape
|
|
12
11
|
|
13
12
|
private
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
object.kind_of?(Hash)
|
19
|
-
end
|
14
|
+
def serializable?(object)
|
15
|
+
object.respond_to?(:serializable_hash) || object.kind_of?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false) || object.kind_of?(Hash)
|
16
|
+
end
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
18
|
+
def serialize(object)
|
19
|
+
if object.respond_to? :serializable_hash
|
20
|
+
object.serializable_hash
|
21
|
+
elsif object.kind_of?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false)
|
22
|
+
object.map { |o| o.serializable_hash }
|
23
|
+
elsif object.kind_of?(Hash)
|
24
|
+
object.inject({}) do |h, (k, v)|
|
25
|
+
h[k] = serialize(v)
|
26
|
+
h
|
30
27
|
end
|
28
|
+
else
|
29
|
+
object
|
31
30
|
end
|
31
|
+
end
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
data/lib/grape/formatter/txt.rb
CHANGED
data/lib/grape/formatter/xml.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Grape
|
2
|
+
class Request < Rack::Request
|
3
|
+
def params
|
4
|
+
@params ||= begin
|
5
|
+
params = Hashie::Mash.new(super)
|
6
|
+
if env['rack.routing_args']
|
7
|
+
args = env['rack.routing_args'].dup
|
8
|
+
# preserve version from query string parameters
|
9
|
+
args.delete(:version)
|
10
|
+
params.deep_merge!(args)
|
11
|
+
end
|
12
|
+
params
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def headers
|
17
|
+
@headers ||= env.dup.inject({}) do |h, (k, v)|
|
18
|
+
if k.to_s.start_with? 'HTTP_'
|
19
|
+
k = k[5..-1].gsub('_', '-').downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }
|
20
|
+
h[k] = v
|
21
|
+
end
|
22
|
+
h
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/grape/locale/en.yml
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
en:
|
2
2
|
grape:
|
3
3
|
errors:
|
4
|
+
format: ! '%{attribute} %{message}'
|
4
5
|
messages:
|
5
|
-
coerce: 'invalid
|
6
|
-
presence: 'missing
|
7
|
-
regexp: 'invalid
|
6
|
+
coerce: 'is invalid'
|
7
|
+
presence: 'is missing'
|
8
|
+
regexp: 'is invalid'
|
9
|
+
values: 'does not have a valid value'
|
8
10
|
missing_vendor_option:
|
9
11
|
problem: 'missing :vendor option.'
|
10
|
-
summary: 'when version using header, you must specify :
|
12
|
+
summary: 'when version using header, you must specify :vendor option. '
|
11
13
|
resolution: "eg: version 'v1', :using => :header, :vendor => 'twitter'"
|
12
14
|
missing_mime_type:
|
13
15
|
problem: 'missing mime type for %{new_format}'
|
14
16
|
resolution:
|
15
|
-
"you can choose
|
17
|
+
"you can choose existing mime type from Grape::ContentTypes::CONTENT_TYPES
|
16
18
|
or add your own with content_type :%{new_format}, 'application/%{new_format}'
|
17
19
|
"
|
18
20
|
invalid_with_option_for_represent:
|
@@ -25,3 +27,4 @@ en:
|
|
25
27
|
resolution: 'available strategy for :using is :path, :header, :param'
|
26
28
|
unknown_validator: 'unknown validator: %{validator_type}'
|
27
29
|
unknown_options: 'unknown options: %{options}'
|
30
|
+
incompatible_option_values: '%{option1}: %{value1} is incompatible with %{option2}: %{value2}'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rack/auth/basic'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Middleware
|
5
|
+
module Auth
|
6
|
+
class Base < Grape::Middleware::Base
|
7
|
+
attr_reader :authenticator
|
8
|
+
|
9
|
+
def initialize(app, options = {}, &authenticator)
|
10
|
+
super(app, options)
|
11
|
+
@authenticator = authenticator
|
12
|
+
end
|
13
|
+
|
14
|
+
def base_request
|
15
|
+
raise NotImplementedError, "You must implement base_request."
|
16
|
+
end
|
17
|
+
|
18
|
+
def credentials
|
19
|
+
base_request.provided? ? base_request.credentials : [nil, nil]
|
20
|
+
end
|
21
|
+
|
22
|
+
def before
|
23
|
+
unless authenticator.call(*credentials)
|
24
|
+
throw :error, status: 401, message: "API Authorization Failed."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -3,28 +3,11 @@ require 'rack/auth/basic'
|
|
3
3
|
module Grape
|
4
4
|
module Middleware
|
5
5
|
module Auth
|
6
|
-
class Basic < Grape::Middleware::Base
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(app, options = {}, &authenticator)
|
10
|
-
super(app, options)
|
11
|
-
@authenticator = authenticator
|
12
|
-
end
|
13
|
-
|
14
|
-
def basic_request
|
6
|
+
class Basic < Grape::Middleware::Auth::Base
|
7
|
+
def base_request
|
15
8
|
Rack::Auth::Basic::Request.new(env)
|
16
9
|
end
|
17
|
-
|
18
|
-
def credentials
|
19
|
-
basic_request.provided?? basic_request.credentials : [nil, nil]
|
20
|
-
end
|
21
|
-
|
22
|
-
def before
|
23
|
-
unless authenticator.call(*credentials)
|
24
|
-
throw :error, :status => 401, :message => "API Authorization Failed."
|
25
|
-
end
|
26
|
-
end
|
27
10
|
end
|
28
11
|
end
|
29
12
|
end
|
30
|
-
end
|
13
|
+
end
|
@@ -3,27 +3,10 @@ require 'rack/auth/digest/md5'
|
|
3
3
|
module Grape
|
4
4
|
module Middleware
|
5
5
|
module Auth
|
6
|
-
class Digest < Grape::Middleware::Base
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(app, options = {}, &authenticator)
|
10
|
-
super(app, options)
|
11
|
-
@authenticator = authenticator
|
12
|
-
end
|
13
|
-
|
14
|
-
def digest_request
|
6
|
+
class Digest < Grape::Middleware::Auth::Base
|
7
|
+
def base_request
|
15
8
|
Rack::Auth::Digest::Request.new(env)
|
16
9
|
end
|
17
|
-
|
18
|
-
def credentials
|
19
|
-
digest_request.provided?? digest_request.credentials : [nil, nil]
|
20
|
-
end
|
21
|
-
|
22
|
-
def before
|
23
|
-
unless authenticator.call(*credentials)
|
24
|
-
throw :error, :status => 401, :message => "API Authorization Failed."
|
25
|
-
end
|
26
|
-
end
|
27
10
|
end
|
28
11
|
end
|
29
12
|
end
|
@@ -3,21 +3,30 @@ module Grape::Middleware::Auth
|
|
3
3
|
class OAuth2 < Grape::Middleware::Base
|
4
4
|
def default_options
|
5
5
|
{
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
6
|
+
token_class: 'AccessToken',
|
7
|
+
realm: 'OAuth API',
|
8
|
+
parameter: %w(bearer_token oauth_token access_token),
|
9
|
+
accepted_headers: %w(HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION),
|
10
|
+
header: [/Bearer (.*)/i, /OAuth (.*)/i],
|
11
|
+
required: true
|
11
12
|
}
|
12
13
|
end
|
13
|
-
|
14
|
+
|
14
15
|
def before
|
15
16
|
verify_token(token_parameter || token_header)
|
16
17
|
end
|
17
18
|
|
19
|
+
def request
|
20
|
+
@request ||= Grape::Request.new(env)
|
21
|
+
end
|
22
|
+
|
23
|
+
def params
|
24
|
+
@params ||= request.params
|
25
|
+
end
|
26
|
+
|
18
27
|
def token_parameter
|
19
28
|
Array(options[:parameter]).each do |p|
|
20
|
-
return
|
29
|
+
return params[p] if params[p]
|
21
30
|
end
|
22
31
|
nil
|
23
32
|
end
|
@@ -25,9 +34,7 @@ module Grape::Middleware::Auth
|
|
25
34
|
def token_header
|
26
35
|
return false unless authorization_header
|
27
36
|
Array(options[:header]).each do |regexp|
|
28
|
-
if authorization_header =~ regexp
|
29
|
-
return $1
|
30
|
-
end
|
37
|
+
return $1 if authorization_header =~ regexp
|
31
38
|
end
|
32
39
|
nil
|
33
40
|
end
|
@@ -38,15 +45,16 @@ module Grape::Middleware::Auth
|
|
38
45
|
end
|
39
46
|
nil
|
40
47
|
end
|
41
|
-
|
48
|
+
|
42
49
|
def token_class
|
43
|
-
@klass ||= eval(options[:token_class])
|
50
|
+
@klass ||= eval(options[:token_class]) # rubocop:disable Eval
|
44
51
|
end
|
45
|
-
|
52
|
+
|
46
53
|
def verify_token(token)
|
47
|
-
|
54
|
+
token = token_class.verify(token)
|
55
|
+
if token
|
48
56
|
if token.respond_to?(:expired?) && token.expired?
|
49
|
-
error_out(401, '
|
57
|
+
error_out(401, 'invalid_grant')
|
50
58
|
else
|
51
59
|
if !token.respond_to?(:permission_for?) || token.permission_for?(env)
|
52
60
|
env['api.token'] = token
|
@@ -54,19 +62,18 @@ module Grape::Middleware::Auth
|
|
54
62
|
error_out(403, 'insufficient_scope')
|
55
63
|
end
|
56
64
|
end
|
57
|
-
|
58
|
-
error_out(401, '
|
65
|
+
elsif !!options[:required]
|
66
|
+
error_out(401, 'invalid_grant')
|
59
67
|
end
|
60
68
|
end
|
61
|
-
|
69
|
+
|
62
70
|
def error_out(status, error)
|
63
71
|
throw :error,
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
72
|
+
message: error,
|
73
|
+
status: status,
|
74
|
+
headers: {
|
75
|
+
'WWW-Authenticate' => "OAuth realm='#{options[:realm]}', error='#{error}'"
|
76
|
+
}
|
69
77
|
end
|
70
78
|
end
|
71
79
|
end
|
72
|
-
|
@@ -27,14 +27,13 @@ module Grape
|
|
27
27
|
|
28
28
|
# @abstract
|
29
29
|
# Called before the application is called in the middleware lifecycle.
|
30
|
-
def before
|
30
|
+
def before
|
31
|
+
end
|
32
|
+
|
31
33
|
# @abstract
|
32
34
|
# Called after the application is called in the middleware lifecycle.
|
33
35
|
# @return [Response, nil] a Rack SPEC response or nil to call the application afterwards.
|
34
|
-
def after
|
35
|
-
|
36
|
-
def request
|
37
|
-
Rack::Request.new(self.env)
|
36
|
+
def after
|
38
37
|
end
|
39
38
|
|
40
39
|
def response
|
@@ -54,9 +53,10 @@ module Grape
|
|
54
53
|
end
|
55
54
|
|
56
55
|
def mime_types
|
57
|
-
content_types.
|
56
|
+
content_types.each_with_object({}) { |(k, v), types_without_params|
|
57
|
+
types_without_params[k] = v.split(';').first
|
58
|
+
}.invert
|
58
59
|
end
|
59
|
-
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|