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.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +45 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +70 -0
  5. data/.travis.yml +18 -0
  6. data/.yardopts +2 -0
  7. data/CHANGELOG.md +314 -0
  8. data/CONTRIBUTING.md +118 -0
  9. data/Gemfile +21 -0
  10. data/Guardfile +14 -0
  11. data/LICENSE +20 -0
  12. data/README.md +1777 -0
  13. data/RELEASING.md +105 -0
  14. data/Rakefile +69 -0
  15. data/UPGRADING.md +124 -0
  16. data/grape-security.gemspec +39 -0
  17. data/grape.png +0 -0
  18. data/lib/grape.rb +99 -0
  19. data/lib/grape/api.rb +646 -0
  20. data/lib/grape/cookies.rb +39 -0
  21. data/lib/grape/endpoint.rb +533 -0
  22. data/lib/grape/error_formatter/base.rb +31 -0
  23. data/lib/grape/error_formatter/json.rb +15 -0
  24. data/lib/grape/error_formatter/txt.rb +16 -0
  25. data/lib/grape/error_formatter/xml.rb +15 -0
  26. data/lib/grape/exceptions/base.rb +66 -0
  27. data/lib/grape/exceptions/incompatible_option_values.rb +10 -0
  28. data/lib/grape/exceptions/invalid_formatter.rb +10 -0
  29. data/lib/grape/exceptions/invalid_versioner_option.rb +10 -0
  30. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +10 -0
  31. data/lib/grape/exceptions/missing_mime_type.rb +10 -0
  32. data/lib/grape/exceptions/missing_option.rb +10 -0
  33. data/lib/grape/exceptions/missing_vendor_option.rb +10 -0
  34. data/lib/grape/exceptions/unknown_options.rb +10 -0
  35. data/lib/grape/exceptions/unknown_validator.rb +10 -0
  36. data/lib/grape/exceptions/validation.rb +26 -0
  37. data/lib/grape/exceptions/validation_errors.rb +43 -0
  38. data/lib/grape/formatter/base.rb +31 -0
  39. data/lib/grape/formatter/json.rb +12 -0
  40. data/lib/grape/formatter/serializable_hash.rb +35 -0
  41. data/lib/grape/formatter/txt.rb +11 -0
  42. data/lib/grape/formatter/xml.rb +12 -0
  43. data/lib/grape/http/request.rb +26 -0
  44. data/lib/grape/locale/en.yml +32 -0
  45. data/lib/grape/middleware/auth/base.rb +30 -0
  46. data/lib/grape/middleware/auth/basic.rb +13 -0
  47. data/lib/grape/middleware/auth/digest.rb +13 -0
  48. data/lib/grape/middleware/auth/oauth2.rb +83 -0
  49. data/lib/grape/middleware/base.rb +62 -0
  50. data/lib/grape/middleware/error.rb +89 -0
  51. data/lib/grape/middleware/filter.rb +17 -0
  52. data/lib/grape/middleware/formatter.rb +150 -0
  53. data/lib/grape/middleware/globals.rb +13 -0
  54. data/lib/grape/middleware/versioner.rb +32 -0
  55. data/lib/grape/middleware/versioner/accept_version_header.rb +67 -0
  56. data/lib/grape/middleware/versioner/header.rb +132 -0
  57. data/lib/grape/middleware/versioner/param.rb +42 -0
  58. data/lib/grape/middleware/versioner/path.rb +52 -0
  59. data/lib/grape/namespace.rb +23 -0
  60. data/lib/grape/parser/base.rb +29 -0
  61. data/lib/grape/parser/json.rb +11 -0
  62. data/lib/grape/parser/xml.rb +11 -0
  63. data/lib/grape/path.rb +70 -0
  64. data/lib/grape/route.rb +27 -0
  65. data/lib/grape/util/content_types.rb +18 -0
  66. data/lib/grape/util/deep_merge.rb +23 -0
  67. data/lib/grape/util/hash_stack.rb +120 -0
  68. data/lib/grape/validations.rb +322 -0
  69. data/lib/grape/validations/coerce.rb +63 -0
  70. data/lib/grape/validations/default.rb +25 -0
  71. data/lib/grape/validations/exactly_one_of.rb +26 -0
  72. data/lib/grape/validations/mutual_exclusion.rb +25 -0
  73. data/lib/grape/validations/presence.rb +16 -0
  74. data/lib/grape/validations/regexp.rb +12 -0
  75. data/lib/grape/validations/values.rb +23 -0
  76. data/lib/grape/version.rb +3 -0
  77. data/spec/grape/api_spec.rb +2571 -0
  78. data/spec/grape/endpoint_spec.rb +784 -0
  79. data/spec/grape/entity_spec.rb +324 -0
  80. data/spec/grape/exceptions/invalid_formatter_spec.rb +18 -0
  81. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +18 -0
  82. data/spec/grape/exceptions/missing_mime_type_spec.rb +18 -0
  83. data/spec/grape/exceptions/missing_option_spec.rb +18 -0
  84. data/spec/grape/exceptions/unknown_options_spec.rb +18 -0
  85. data/spec/grape/exceptions/unknown_validator_spec.rb +18 -0
  86. data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
  87. data/spec/grape/middleware/auth/basic_spec.rb +31 -0
  88. data/spec/grape/middleware/auth/digest_spec.rb +47 -0
  89. data/spec/grape/middleware/auth/oauth2_spec.rb +135 -0
  90. data/spec/grape/middleware/base_spec.rb +58 -0
  91. data/spec/grape/middleware/error_spec.rb +45 -0
  92. data/spec/grape/middleware/exception_spec.rb +184 -0
  93. data/spec/grape/middleware/formatter_spec.rb +258 -0
  94. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +121 -0
  95. data/spec/grape/middleware/versioner/header_spec.rb +302 -0
  96. data/spec/grape/middleware/versioner/param_spec.rb +58 -0
  97. data/spec/grape/middleware/versioner/path_spec.rb +44 -0
  98. data/spec/grape/middleware/versioner_spec.rb +22 -0
  99. data/spec/grape/path_spec.rb +229 -0
  100. data/spec/grape/util/hash_stack_spec.rb +132 -0
  101. data/spec/grape/validations/coerce_spec.rb +208 -0
  102. data/spec/grape/validations/default_spec.rb +123 -0
  103. data/spec/grape/validations/exactly_one_of_spec.rb +71 -0
  104. data/spec/grape/validations/mutual_exclusion_spec.rb +61 -0
  105. data/spec/grape/validations/presence_spec.rb +142 -0
  106. data/spec/grape/validations/regexp_spec.rb +40 -0
  107. data/spec/grape/validations/values_spec.rb +152 -0
  108. data/spec/grape/validations/zh-CN.yml +10 -0
  109. data/spec/grape/validations_spec.rb +994 -0
  110. data/spec/shared/versioning_examples.rb +121 -0
  111. data/spec/spec_helper.rb +26 -0
  112. data/spec/support/basic_auth_encode_helpers.rb +3 -0
  113. data/spec/support/content_type_helpers.rb +11 -0
  114. data/spec/support/versioned_helpers.rb +50 -0
  115. metadata +421 -0
@@ -0,0 +1,208 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Grape::Validations::CoerceValidator do
5
+ subject do
6
+ Class.new(Grape::API)
7
+ end
8
+
9
+ def app
10
+ subject
11
+ end
12
+
13
+ describe 'coerce' do
14
+
15
+ context "i18n" do
16
+
17
+ after :each do
18
+ I18n.locale = :en
19
+ end
20
+
21
+ it "i18n error on malformed input" do
22
+ I18n.load_path << File.expand_path('../zh-CN.yml', __FILE__)
23
+ I18n.reload!
24
+ I18n.locale = 'zh-CN'.to_sym
25
+ subject.params do
26
+ requires :age, type: Integer
27
+ end
28
+ subject.get '/single' do
29
+ 'int works'
30
+ end
31
+
32
+ get '/single', age: '43a'
33
+ expect(last_response.status).to eq(400)
34
+ expect(last_response.body).to eq('年龄格式不正确')
35
+ end
36
+
37
+ it 'gives an english fallback error when default locale message is blank' do
38
+ I18n.locale = 'pt-BR'.to_sym
39
+ subject.params do
40
+ requires :age, type: Integer
41
+ end
42
+ subject.get '/single' do
43
+ 'int works'
44
+ end
45
+
46
+ get '/single', age: '43a'
47
+ expect(last_response.status).to eq(400)
48
+ expect(last_response.body).to eq('age is invalid')
49
+ end
50
+
51
+ end
52
+
53
+ it 'error on malformed input' do
54
+ subject.params do
55
+ requires :int, type: Integer
56
+ end
57
+ subject.get '/single' do
58
+ 'int works'
59
+ end
60
+
61
+ get '/single', int: '43a'
62
+ expect(last_response.status).to eq(400)
63
+ expect(last_response.body).to eq('int is invalid')
64
+
65
+ get '/single', int: '43'
66
+ expect(last_response.status).to eq(200)
67
+ expect(last_response.body).to eq('int works')
68
+ end
69
+
70
+ it 'error on malformed input (Array)' do
71
+ subject.params do
72
+ requires :ids, type: Array[Integer]
73
+ end
74
+ subject.get '/array' do
75
+ 'array int works'
76
+ end
77
+
78
+ get 'array', ids: ['1', '2', 'az']
79
+ expect(last_response.status).to eq(400)
80
+ expect(last_response.body).to eq('ids is invalid')
81
+
82
+ get 'array', ids: ['1', '2', '890']
83
+ expect(last_response.status).to eq(200)
84
+ expect(last_response.body).to eq('array int works')
85
+ end
86
+
87
+ context 'complex objects' do
88
+ module CoerceValidatorSpec
89
+ class User
90
+ include Virtus.model
91
+ attribute :id, Integer
92
+ attribute :name, String
93
+ end
94
+ end
95
+
96
+ it 'error on malformed input for complex objects' do
97
+ subject.params do
98
+ requires :user, type: CoerceValidatorSpec::User
99
+ end
100
+ subject.get '/user' do
101
+ 'complex works'
102
+ end
103
+
104
+ get '/user', user: "32"
105
+ expect(last_response.status).to eq(400)
106
+ expect(last_response.body).to eq('user is invalid')
107
+
108
+ get '/user', user: { id: 32, name: 'Bob' }
109
+ expect(last_response.status).to eq(200)
110
+ expect(last_response.body).to eq('complex works')
111
+ end
112
+ end
113
+
114
+ context 'coerces' do
115
+ it 'Integer' do
116
+ subject.params do
117
+ requires :int, coerce: Integer
118
+ end
119
+ subject.get '/int' do
120
+ params[:int].class
121
+ end
122
+
123
+ get '/int', int: "45"
124
+ expect(last_response.status).to eq(200)
125
+ expect(last_response.body).to eq('Integer')
126
+ end
127
+
128
+ it 'Array of Integers' do
129
+ subject.params do
130
+ requires :arry, coerce: Array[Integer]
131
+ end
132
+ subject.get '/array' do
133
+ params[:arry][0].class
134
+ end
135
+
136
+ get '/array', arry: ['1', '2', '3']
137
+ expect(last_response.status).to eq(200)
138
+ expect(last_response.body).to eq('Integer')
139
+ end
140
+
141
+ it 'Array of Bools' do
142
+ subject.params do
143
+ requires :arry, coerce: Array[Virtus::Attribute::Boolean]
144
+ end
145
+ subject.get '/array' do
146
+ params[:arry][0].class
147
+ end
148
+
149
+ get 'array', arry: [1, 0]
150
+ expect(last_response.status).to eq(200)
151
+ expect(last_response.body).to eq('TrueClass')
152
+ end
153
+
154
+ it 'Bool' do
155
+ subject.params do
156
+ requires :bool, coerce: Virtus::Attribute::Boolean
157
+ end
158
+ subject.get '/bool' do
159
+ params[:bool].class
160
+ end
161
+
162
+ get '/bool', bool: 1
163
+ expect(last_response.status).to eq(200)
164
+ expect(last_response.body).to eq('TrueClass')
165
+
166
+ get '/bool', bool: 0
167
+ expect(last_response.status).to eq(200)
168
+ expect(last_response.body).to eq('FalseClass')
169
+
170
+ get '/bool', bool: 'false'
171
+ expect(last_response.status).to eq(200)
172
+ expect(last_response.body).to eq('FalseClass')
173
+
174
+ get '/bool', bool: 'true'
175
+ expect(last_response.status).to eq(200)
176
+ expect(last_response.body).to eq('TrueClass')
177
+ end
178
+
179
+ it 'file' do
180
+ subject.params do
181
+ requires :file, coerce: Rack::Multipart::UploadedFile
182
+ end
183
+ subject.post '/upload' do
184
+ params[:file].filename
185
+ end
186
+
187
+ post '/upload', file: Rack::Test::UploadedFile.new(__FILE__)
188
+ expect(last_response.status).to eq(201)
189
+ expect(last_response.body).to eq(File.basename(__FILE__).to_s)
190
+ end
191
+
192
+ it 'Nests integers' do
193
+ subject.params do
194
+ requires :integers, type: Hash do
195
+ requires :int, coerce: Integer
196
+ end
197
+ end
198
+ subject.get '/int' do
199
+ params[:integers][:int].class
200
+ end
201
+
202
+ get '/int', integers: { int: "45" }
203
+ expect(last_response.status).to eq(200)
204
+ expect(last_response.body).to eq('Integer')
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Validations::DefaultValidator do
4
+
5
+ module ValidationsSpec
6
+ module DefaultValidatorSpec
7
+ class API < Grape::API
8
+ default_format :json
9
+
10
+ params do
11
+ optional :id
12
+ optional :type, default: 'default-type'
13
+ end
14
+ get '/' do
15
+ { id: params[:id], type: params[:type] }
16
+ end
17
+
18
+ params do
19
+ optional :type1, default: 'default-type1'
20
+ optional :type2, default: 'default-type2'
21
+ end
22
+ get '/user' do
23
+ { type1: params[:type1], type2: params[:type2] }
24
+ end
25
+
26
+ params do
27
+ requires :id
28
+ optional :type1, default: 'default-type1'
29
+ optional :type2, default: 'default-type2'
30
+ end
31
+
32
+ get '/message' do
33
+ { id: params[:id], type1: params[:type1], type2: params[:type2] }
34
+ end
35
+
36
+ params do
37
+ optional :random, default: -> { Random.rand }
38
+ optional :not_random, default: Random.rand
39
+ end
40
+ get '/numbers' do
41
+ { random_number: params[:random], non_random_number: params[:non_random_number] }
42
+ end
43
+
44
+ params do
45
+ # NOTE: The :foo parameter could be made required with json body
46
+ # params, and then an empty hash would be valid. With query parameters
47
+ # it must be optional if it isn't provided at all, as otherwise
48
+ # the validaton for the Hash itself fails because there is no such
49
+ # thing as an empty hash.
50
+ optional :foo, type: Hash do
51
+ optional :bar, default: 'foo-bar'
52
+ end
53
+ end
54
+ get '/group' do
55
+ { foo_bar: params[:foo][:bar] }
56
+ end
57
+
58
+ params do
59
+ optional :array, type: Array do
60
+ requires :name
61
+ optional :with_default, default: 'default'
62
+ end
63
+ end
64
+ get '/array' do
65
+ { array: params[:array] }
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ def app
72
+ ValidationsSpec::DefaultValidatorSpec::API
73
+ end
74
+
75
+ it 'set default value for optional param' do
76
+ get("/")
77
+ expect(last_response.status).to eq(200)
78
+ expect(last_response.body).to eq({ id: nil, type: 'default-type' }.to_json)
79
+ end
80
+
81
+ it 'set default values for optional params' do
82
+ get("/user")
83
+ expect(last_response.status).to eq(200)
84
+ expect(last_response.body).to eq({ type1: 'default-type1', type2: 'default-type2' }.to_json)
85
+ end
86
+
87
+ it 'set default values for missing params in the request' do
88
+ get("/user?type2=value2")
89
+ expect(last_response.status).to eq(200)
90
+ expect(last_response.body).to eq({ type1: 'default-type1', type2: 'value2' }.to_json)
91
+ end
92
+
93
+ it 'set default values for optional params and allow to use required fields in the same time' do
94
+ get("/message?id=1")
95
+ expect(last_response.status).to eq(200)
96
+ expect(last_response.body).to eq({ id: '1', type1: 'default-type1', type2: 'default-type2' }.to_json)
97
+ end
98
+
99
+ it 'sets lambda based defaults at the time of call' do
100
+ get("/numbers")
101
+ expect(last_response.status).to eq(200)
102
+ before = JSON.parse(last_response.body)
103
+ get("/numbers")
104
+ expect(last_response.status).to eq(200)
105
+ after = JSON.parse(last_response.body)
106
+
107
+ expect(before['non_random_number']).to eq(after['non_random_number'])
108
+ expect(before['random_number']).not_to eq(after['random_number'])
109
+ end
110
+
111
+ it 'set default values for optional grouped params' do
112
+ get('/group')
113
+ expect(last_response.status).to eq(200)
114
+ expect(last_response.body).to eq({ foo_bar: 'foo-bar' }.to_json)
115
+ end
116
+
117
+ it 'sets default values for grouped arrays' do
118
+ get('/array?array[][name]=name&array[][name]=name2&array[][with_default]=bar2')
119
+ expect(last_response.status).to eq(200)
120
+ expect(last_response.body).to eq({ array: [{ name: "name", with_default: "default" }, { name: "name2", with_default: "bar2" }] }.to_json)
121
+ end
122
+
123
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Validations::ExactlyOneOfValidator do
4
+ describe '#validate!' do
5
+ let(:scope) do
6
+ Struct.new(:opts) do
7
+ def params(arg); end
8
+ end
9
+ end
10
+ let(:exactly_one_of_params) { [:beer, :wine, :grapefruit] }
11
+ let(:validator) { described_class.new(exactly_one_of_params, {}, false, scope.new) }
12
+
13
+ context 'when all restricted params are present' do
14
+ let(:params) { { beer: true, wine: true, grapefruit: true } }
15
+
16
+ it 'raises a validation exception' do
17
+ expect {
18
+ validator.validate! params
19
+ }.to raise_error(Grape::Exceptions::Validation)
20
+ end
21
+
22
+ context 'mixed with other params' do
23
+ let(:mixed_params) { params.merge!(other: true, andanother: true) }
24
+
25
+ it 'still raises a validation exception' do
26
+ expect {
27
+ validator.validate! mixed_params
28
+ }.to raise_error(Grape::Exceptions::Validation)
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'when a subset of restricted params are present' do
34
+ let(:params) { { beer: true, grapefruit: true } }
35
+
36
+ it 'raises a validation exception' do
37
+ expect {
38
+ validator.validate! params
39
+ }.to raise_error(Grape::Exceptions::Validation)
40
+ end
41
+ end
42
+
43
+ context 'when params keys come as strings' do
44
+ let(:params) { { 'beer' => true, 'grapefruit' => true } }
45
+
46
+ it 'raises a validation exception' do
47
+ expect {
48
+ validator.validate! params
49
+ }.to raise_error(Grape::Exceptions::Validation)
50
+ end
51
+ end
52
+
53
+ context 'when none of the restricted params is selected' do
54
+ let(:params) { { somethingelse: true } }
55
+
56
+ it 'raises a validation exception' do
57
+ expect {
58
+ validator.validate! params
59
+ }.to raise_error(Grape::Exceptions::Validation)
60
+ end
61
+ end
62
+
63
+ context 'when exactly one of the restricted params is selected' do
64
+ let(:params) { { beer: true, somethingelse: true } }
65
+
66
+ it 'params' do
67
+ expect(validator.validate!(params)).to eql params
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Validations::MutualExclusionValidator do
4
+ describe '#validate!' do
5
+ let(:scope) do
6
+ Struct.new(:opts) do
7
+ def params(arg); end
8
+ end
9
+ end
10
+ let(:mutually_exclusive_params) { [:beer, :wine, :grapefruit] }
11
+ let(:validator) { described_class.new(mutually_exclusive_params, {}, false, scope.new) }
12
+
13
+ context 'when all mutually exclusive params are present' do
14
+ let(:params) { { beer: true, wine: true, grapefruit: true } }
15
+
16
+ it 'raises a validation exception' do
17
+ expect {
18
+ validator.validate! params
19
+ }.to raise_error(Grape::Exceptions::Validation)
20
+ end
21
+
22
+ context 'mixed with other params' do
23
+ let(:mixed_params) { params.merge!(other: true, andanother: true) }
24
+
25
+ it 'still raises a validation exception' do
26
+ expect {
27
+ validator.validate! mixed_params
28
+ }.to raise_error(Grape::Exceptions::Validation)
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'when a subset of mutually exclusive params are present' do
34
+ let(:params) { { beer: true, grapefruit: true } }
35
+
36
+ it 'raises a validation exception' do
37
+ expect {
38
+ validator.validate! params
39
+ }.to raise_error(Grape::Exceptions::Validation)
40
+ end
41
+ end
42
+
43
+ context 'when params keys come as strings' do
44
+ let(:params) { { 'beer' => true, 'grapefruit' => true } }
45
+
46
+ it 'raises a validation exception' do
47
+ expect {
48
+ validator.validate! params
49
+ }.to raise_error(Grape::Exceptions::Validation)
50
+ end
51
+ end
52
+
53
+ context 'when no mutually exclusive params are present' do
54
+ let(:params) { { beer: true, somethingelse: true } }
55
+
56
+ it 'params' do
57
+ expect(validator.validate!(params)).to eql params
58
+ end
59
+ end
60
+ end
61
+ end