grape-security 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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