grape-security 0.8.0
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.
- 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
|