grape-security 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +45 -0
- data/.rspec +2 -0
- data/.rubocop.yml +70 -0
- data/.travis.yml +18 -0
- data/.yardopts +2 -0
- data/CHANGELOG.md +314 -0
- data/CONTRIBUTING.md +118 -0
- data/Gemfile +21 -0
- data/Guardfile +14 -0
- data/LICENSE +20 -0
- data/README.md +1777 -0
- data/RELEASING.md +105 -0
- data/Rakefile +69 -0
- data/UPGRADING.md +124 -0
- data/grape-security.gemspec +39 -0
- data/grape.png +0 -0
- data/lib/grape.rb +99 -0
- data/lib/grape/api.rb +646 -0
- data/lib/grape/cookies.rb +39 -0
- data/lib/grape/endpoint.rb +533 -0
- data/lib/grape/error_formatter/base.rb +31 -0
- data/lib/grape/error_formatter/json.rb +15 -0
- data/lib/grape/error_formatter/txt.rb +16 -0
- data/lib/grape/error_formatter/xml.rb +15 -0
- data/lib/grape/exceptions/base.rb +66 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +10 -0
- data/lib/grape/exceptions/invalid_formatter.rb +10 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +10 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +10 -0
- data/lib/grape/exceptions/missing_mime_type.rb +10 -0
- data/lib/grape/exceptions/missing_option.rb +10 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +10 -0
- data/lib/grape/exceptions/unknown_options.rb +10 -0
- data/lib/grape/exceptions/unknown_validator.rb +10 -0
- data/lib/grape/exceptions/validation.rb +26 -0
- data/lib/grape/exceptions/validation_errors.rb +43 -0
- data/lib/grape/formatter/base.rb +31 -0
- data/lib/grape/formatter/json.rb +12 -0
- data/lib/grape/formatter/serializable_hash.rb +35 -0
- data/lib/grape/formatter/txt.rb +11 -0
- data/lib/grape/formatter/xml.rb +12 -0
- data/lib/grape/http/request.rb +26 -0
- data/lib/grape/locale/en.yml +32 -0
- data/lib/grape/middleware/auth/base.rb +30 -0
- data/lib/grape/middleware/auth/basic.rb +13 -0
- data/lib/grape/middleware/auth/digest.rb +13 -0
- data/lib/grape/middleware/auth/oauth2.rb +83 -0
- data/lib/grape/middleware/base.rb +62 -0
- data/lib/grape/middleware/error.rb +89 -0
- data/lib/grape/middleware/filter.rb +17 -0
- data/lib/grape/middleware/formatter.rb +150 -0
- data/lib/grape/middleware/globals.rb +13 -0
- data/lib/grape/middleware/versioner.rb +32 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +67 -0
- data/lib/grape/middleware/versioner/header.rb +132 -0
- data/lib/grape/middleware/versioner/param.rb +42 -0
- data/lib/grape/middleware/versioner/path.rb +52 -0
- data/lib/grape/namespace.rb +23 -0
- data/lib/grape/parser/base.rb +29 -0
- data/lib/grape/parser/json.rb +11 -0
- data/lib/grape/parser/xml.rb +11 -0
- data/lib/grape/path.rb +70 -0
- data/lib/grape/route.rb +27 -0
- data/lib/grape/util/content_types.rb +18 -0
- data/lib/grape/util/deep_merge.rb +23 -0
- data/lib/grape/util/hash_stack.rb +120 -0
- data/lib/grape/validations.rb +322 -0
- data/lib/grape/validations/coerce.rb +63 -0
- data/lib/grape/validations/default.rb +25 -0
- data/lib/grape/validations/exactly_one_of.rb +26 -0
- data/lib/grape/validations/mutual_exclusion.rb +25 -0
- data/lib/grape/validations/presence.rb +16 -0
- data/lib/grape/validations/regexp.rb +12 -0
- data/lib/grape/validations/values.rb +23 -0
- data/lib/grape/version.rb +3 -0
- data/spec/grape/api_spec.rb +2571 -0
- data/spec/grape/endpoint_spec.rb +784 -0
- data/spec/grape/entity_spec.rb +324 -0
- data/spec/grape/exceptions/invalid_formatter_spec.rb +18 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +18 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +18 -0
- data/spec/grape/exceptions/missing_option_spec.rb +18 -0
- data/spec/grape/exceptions/unknown_options_spec.rb +18 -0
- data/spec/grape/exceptions/unknown_validator_spec.rb +18 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
- data/spec/grape/middleware/auth/basic_spec.rb +31 -0
- data/spec/grape/middleware/auth/digest_spec.rb +47 -0
- data/spec/grape/middleware/auth/oauth2_spec.rb +135 -0
- data/spec/grape/middleware/base_spec.rb +58 -0
- data/spec/grape/middleware/error_spec.rb +45 -0
- data/spec/grape/middleware/exception_spec.rb +184 -0
- data/spec/grape/middleware/formatter_spec.rb +258 -0
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +121 -0
- data/spec/grape/middleware/versioner/header_spec.rb +302 -0
- data/spec/grape/middleware/versioner/param_spec.rb +58 -0
- data/spec/grape/middleware/versioner/path_spec.rb +44 -0
- data/spec/grape/middleware/versioner_spec.rb +22 -0
- data/spec/grape/path_spec.rb +229 -0
- data/spec/grape/util/hash_stack_spec.rb +132 -0
- data/spec/grape/validations/coerce_spec.rb +208 -0
- data/spec/grape/validations/default_spec.rb +123 -0
- data/spec/grape/validations/exactly_one_of_spec.rb +71 -0
- data/spec/grape/validations/mutual_exclusion_spec.rb +61 -0
- data/spec/grape/validations/presence_spec.rb +142 -0
- data/spec/grape/validations/regexp_spec.rb +40 -0
- data/spec/grape/validations/values_spec.rb +152 -0
- data/spec/grape/validations/zh-CN.yml +10 -0
- data/spec/grape/validations_spec.rb +994 -0
- data/spec/shared/versioning_examples.rb +121 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/basic_auth_encode_helpers.rb +3 -0
- data/spec/support/content_type_helpers.rb +11 -0
- data/spec/support/versioned_helpers.rb +50 -0
- metadata +421 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
module Grape
|
2
|
+
module ErrorFormatter
|
3
|
+
module Base
|
4
|
+
class << self
|
5
|
+
FORMATTERS = {
|
6
|
+
serializable_hash: Grape::ErrorFormatter::Json,
|
7
|
+
json: Grape::ErrorFormatter::Json,
|
8
|
+
jsonapi: Grape::ErrorFormatter::Json,
|
9
|
+
txt: Grape::ErrorFormatter::Txt,
|
10
|
+
xml: Grape::ErrorFormatter::Xml
|
11
|
+
}
|
12
|
+
|
13
|
+
def formatters(options)
|
14
|
+
FORMATTERS.merge(options[:error_formatters] || {})
|
15
|
+
end
|
16
|
+
|
17
|
+
def formatter_for(api_format, options = {})
|
18
|
+
spec = formatters(options)[api_format]
|
19
|
+
case spec
|
20
|
+
when nil
|
21
|
+
options[:default_error_formatter] || Grape::ErrorFormatter::Txt
|
22
|
+
when Symbol
|
23
|
+
method(spec)
|
24
|
+
else
|
25
|
+
spec
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Grape
|
2
|
+
module ErrorFormatter
|
3
|
+
module Json
|
4
|
+
class << self
|
5
|
+
def call(message, backtrace, options = {}, env = nil)
|
6
|
+
result = message.is_a?(Hash) ? message : { error: message }
|
7
|
+
if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
|
8
|
+
result = result.merge(backtrace: backtrace)
|
9
|
+
end
|
10
|
+
MultiJson.dump(result)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Grape
|
2
|
+
module ErrorFormatter
|
3
|
+
module Txt
|
4
|
+
class << self
|
5
|
+
def call(message, backtrace, options = {}, env = nil)
|
6
|
+
result = message.is_a?(Hash) ? MultiJson.dump(message) : message
|
7
|
+
if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
|
8
|
+
result += "\r\n "
|
9
|
+
result += backtrace.join("\r\n ")
|
10
|
+
end
|
11
|
+
result
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Grape
|
2
|
+
module ErrorFormatter
|
3
|
+
module Xml
|
4
|
+
class << self
|
5
|
+
def call(message, backtrace, options = {}, env = nil)
|
6
|
+
result = message.is_a?(Hash) ? message : { message: message }
|
7
|
+
if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
|
8
|
+
result = result.merge(backtrace: backtrace)
|
9
|
+
end
|
10
|
+
result.respond_to?(:to_xml) ? result.to_xml(root: :error) : result.to_s
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Grape
|
2
|
+
module Exceptions
|
3
|
+
class Base < StandardError
|
4
|
+
BASE_MESSAGES_KEY = 'grape.errors.messages'
|
5
|
+
BASE_ATTRIBUTES_KEY = 'grape.errors.attributes'
|
6
|
+
FALLBACK_LOCALE = :en
|
7
|
+
|
8
|
+
attr_reader :status, :message, :headers
|
9
|
+
|
10
|
+
def initialize(args = {})
|
11
|
+
@status = args[:status] || nil
|
12
|
+
@message = args[:message] || nil
|
13
|
+
@headers = args[:headers] || nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](index)
|
17
|
+
send index
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
# TODO: translate attribute first
|
23
|
+
# if BASE_ATTRIBUTES_KEY.key respond to a string message, then short_message is returned
|
24
|
+
# if BASE_ATTRIBUTES_KEY.key respond to a Hash, means it may have problem , summary and resolution
|
25
|
+
def compose_message(key, attributes = {})
|
26
|
+
short_message = translate_message(key, attributes)
|
27
|
+
if short_message.is_a? Hash
|
28
|
+
@problem = problem(key, attributes)
|
29
|
+
@summary = summary(key, attributes)
|
30
|
+
@resolution = resolution(key, attributes)
|
31
|
+
[["Problem", @problem], ["Summary", @summary], ["Resolution", @resolution]].reduce("") do |message, detail_array|
|
32
|
+
message << "\n#{detail_array[0]}:\n #{detail_array[1]}" unless detail_array[1].blank?
|
33
|
+
message
|
34
|
+
end
|
35
|
+
else
|
36
|
+
short_message
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def problem(key, attributes)
|
41
|
+
translate_message("#{key}.problem", attributes)
|
42
|
+
end
|
43
|
+
|
44
|
+
def summary(key, attributes)
|
45
|
+
translate_message("#{key}.summary", attributes)
|
46
|
+
end
|
47
|
+
|
48
|
+
def resolution(key, attributes)
|
49
|
+
translate_message("#{key}.resolution", attributes)
|
50
|
+
end
|
51
|
+
|
52
|
+
def translate_attribute(key, options = {})
|
53
|
+
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", { default: key }.merge(options))
|
54
|
+
end
|
55
|
+
|
56
|
+
def translate_message(key, options = {})
|
57
|
+
translate("#{BASE_MESSAGES_KEY}.#{key}", { default: '' }.merge(options))
|
58
|
+
end
|
59
|
+
|
60
|
+
def translate(key, options = {})
|
61
|
+
message = ::I18n.translate(key, options)
|
62
|
+
message.present? ? message : ::I18n.translate(key, options.merge(locale: FALLBACK_LOCALE))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
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
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'grape/exceptions/base'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Exceptions
|
5
|
+
class Validation < Grape::Exceptions::Base
|
6
|
+
attr_accessor :param
|
7
|
+
|
8
|
+
def initialize(args = {})
|
9
|
+
raise "Param is missing:" unless args.key? :param
|
10
|
+
@param = args[:param]
|
11
|
+
args[:message] = translate_message(args[:message_key]) if args.key? :message_key
|
12
|
+
super
|
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
|
24
|
+
end
|
25
|
+
end
|
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
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Grape
|
2
|
+
module Formatter
|
3
|
+
module Base
|
4
|
+
class << self
|
5
|
+
FORMATTERS = {
|
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
|
11
|
+
}
|
12
|
+
|
13
|
+
def formatters(options)
|
14
|
+
FORMATTERS.merge(options[:formatters] || {})
|
15
|
+
end
|
16
|
+
|
17
|
+
def formatter_for(api_format, options = {})
|
18
|
+
spec = formatters(options)[api_format]
|
19
|
+
case spec
|
20
|
+
when nil
|
21
|
+
lambda { |obj, env| obj }
|
22
|
+
when Symbol
|
23
|
+
method(spec)
|
24
|
+
else
|
25
|
+
spec
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Grape
|
2
|
+
module Formatter
|
3
|
+
module SerializableHash
|
4
|
+
class << self
|
5
|
+
def call(object, env)
|
6
|
+
return object if object.is_a?(String)
|
7
|
+
return MultiJson.dump(serialize(object)) if serializable?(object)
|
8
|
+
return object.to_json if object.respond_to?(:to_json)
|
9
|
+
MultiJson.dump(object)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
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
|
17
|
+
|
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
|
27
|
+
end
|
28
|
+
else
|
29
|
+
object
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -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
|