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,324 @@
1
+ require 'spec_helper'
2
+ require 'grape_entity'
3
+
4
+ describe Grape::Entity do
5
+ subject { Class.new(Grape::API) }
6
+
7
+ def app
8
+ subject
9
+ end
10
+
11
+ describe '#present' do
12
+ it 'sets the object as the body if no options are provided' do
13
+ subject.get '/example' do
14
+ present(abc: 'def')
15
+ body.should == { abc: 'def' }
16
+ end
17
+ get '/example'
18
+ end
19
+
20
+ it 'calls through to the provided entity class if one is given' do
21
+ subject.get '/example' do
22
+ entity_mock = Object.new
23
+ entity_mock.should_receive(:represent)
24
+ present Object.new, with: entity_mock
25
+ end
26
+ get '/example'
27
+ end
28
+
29
+ it 'pulls a representation from the class options if it exists' do
30
+ entity = Class.new(Grape::Entity)
31
+ allow(entity).to receive(:represent).and_return("Hiya")
32
+
33
+ subject.represent Object, with: entity
34
+ subject.get '/example' do
35
+ present Object.new
36
+ end
37
+ get '/example'
38
+ expect(last_response.body).to eq('Hiya')
39
+ end
40
+
41
+ it 'pulls a representation from the class options if the presented object is a collection of objects' do
42
+ entity = Class.new(Grape::Entity)
43
+ allow(entity).to receive(:represent).and_return("Hiya")
44
+
45
+ class TestObject
46
+ end
47
+
48
+ class FakeCollection
49
+ def first
50
+ TestObject.new
51
+ end
52
+ end
53
+
54
+ subject.represent TestObject, with: entity
55
+ subject.get '/example' do
56
+ present [TestObject.new]
57
+ end
58
+
59
+ subject.get '/example2' do
60
+ present FakeCollection.new
61
+ end
62
+
63
+ get '/example'
64
+ expect(last_response.body).to eq("Hiya")
65
+
66
+ get '/example2'
67
+ expect(last_response.body).to eq("Hiya")
68
+ end
69
+
70
+ it 'pulls a representation from the class ancestor if it exists' do
71
+ entity = Class.new(Grape::Entity)
72
+ allow(entity).to receive(:represent).and_return("Hiya")
73
+
74
+ subclass = Class.new(Object)
75
+
76
+ subject.represent Object, with: entity
77
+ subject.get '/example' do
78
+ present subclass.new
79
+ end
80
+ get '/example'
81
+ expect(last_response.body).to eq('Hiya')
82
+ end
83
+
84
+ it 'automatically uses Klass::Entity if that exists' do
85
+ some_model = Class.new
86
+ entity = Class.new(Grape::Entity)
87
+ allow(entity).to receive(:represent).and_return("Auto-detect!")
88
+
89
+ some_model.const_set :Entity, entity
90
+
91
+ subject.get '/example' do
92
+ present some_model.new
93
+ end
94
+ get '/example'
95
+ expect(last_response.body).to eq('Auto-detect!')
96
+ end
97
+
98
+ it 'automatically uses Klass::Entity based on the first object in the collection being presented' do
99
+ some_model = Class.new
100
+ entity = Class.new(Grape::Entity)
101
+ allow(entity).to receive(:represent).and_return("Auto-detect!")
102
+
103
+ some_model.const_set :Entity, entity
104
+
105
+ subject.get '/example' do
106
+ present [some_model.new]
107
+ end
108
+ get '/example'
109
+ expect(last_response.body).to eq('Auto-detect!')
110
+ end
111
+
112
+ it 'does not run autodetection for Entity when explicitely provided' do
113
+ entity = Class.new(Grape::Entity)
114
+ some_array = Array.new
115
+
116
+ subject.get '/example' do
117
+ present some_array, with: entity
118
+ end
119
+
120
+ expect(some_array).not_to receive(:first)
121
+ get '/example'
122
+ end
123
+
124
+ it 'does not use #first method on ActiveRecord::Relation to prevent needless sql query' do
125
+ entity = Class.new(Grape::Entity)
126
+ some_relation = Class.new
127
+ some_model = Class.new
128
+
129
+ allow(entity).to receive(:represent).and_return("Auto-detect!")
130
+ allow(some_relation).to receive(:first)
131
+ allow(some_relation).to receive(:klass).and_return(some_model)
132
+
133
+ some_model.const_set :Entity, entity
134
+
135
+ subject.get '/example' do
136
+ present some_relation
137
+ end
138
+
139
+ expect(some_relation).not_to receive(:first)
140
+ get '/example'
141
+ expect(last_response.body).to eq('Auto-detect!')
142
+ end
143
+
144
+ it 'autodetection does not use Entity if it is not a presenter' do
145
+ some_model = Class.new
146
+ entity = Class.new
147
+
148
+ some_model.class.const_set :Entity, entity
149
+
150
+ subject.get '/example' do
151
+ present some_model
152
+ end
153
+ get '/example'
154
+ expect(entity).not_to receive(:represent)
155
+ end
156
+
157
+ it 'adds a root key to the output if one is given' do
158
+ subject.get '/example' do
159
+ present({ abc: 'def' }, root: :root)
160
+ body.should == { root: { abc: 'def' } }
161
+ end
162
+ get '/example'
163
+ end
164
+
165
+ [:json, :serializable_hash].each do |format|
166
+
167
+ it 'presents with #{format}' do
168
+ entity = Class.new(Grape::Entity)
169
+ entity.root "examples", "example"
170
+ entity.expose :id
171
+
172
+ subject.format format
173
+ subject.get '/example' do
174
+ c = Class.new do
175
+ attr_reader :id
176
+ def initialize(id)
177
+ @id = id
178
+ end
179
+ end
180
+ present c.new(1), with: entity
181
+ end
182
+
183
+ get '/example'
184
+ expect(last_response.status).to eq(200)
185
+ expect(last_response.body).to eq('{"example":{"id":1}}')
186
+ end
187
+
188
+ it 'presents with #{format} collection' do
189
+ entity = Class.new(Grape::Entity)
190
+ entity.root "examples", "example"
191
+ entity.expose :id
192
+
193
+ subject.format format
194
+ subject.get '/examples' do
195
+ c = Class.new do
196
+ attr_reader :id
197
+ def initialize(id)
198
+ @id = id
199
+ end
200
+ end
201
+ examples = [c.new(1), c.new(2)]
202
+ present examples, with: entity
203
+ end
204
+
205
+ get '/examples'
206
+ expect(last_response.status).to eq(200)
207
+ expect(last_response.body).to eq('{"examples":[{"id":1},{"id":2}]}')
208
+ end
209
+
210
+ end
211
+
212
+ it 'presents with xml' do
213
+ entity = Class.new(Grape::Entity)
214
+ entity.root "examples", "example"
215
+ entity.expose :name
216
+
217
+ subject.format :xml
218
+
219
+ subject.get '/example' do
220
+ c = Class.new do
221
+ attr_reader :name
222
+ def initialize(args)
223
+ @name = args[:name] || "no name set"
224
+ end
225
+ end
226
+ present c.new(name: "johnnyiller"), with: entity
227
+ end
228
+ get '/example'
229
+ expect(last_response.status).to eq(200)
230
+ expect(last_response.headers['Content-type']).to eq("application/xml")
231
+ expect(last_response.body).to eq <<-XML
232
+ <?xml version="1.0" encoding="UTF-8"?>
233
+ <hash>
234
+ <example>
235
+ <name>johnnyiller</name>
236
+ </example>
237
+ </hash>
238
+ XML
239
+ end
240
+
241
+ it 'presents with json' do
242
+ entity = Class.new(Grape::Entity)
243
+ entity.root "examples", "example"
244
+ entity.expose :name
245
+
246
+ subject.format :json
247
+
248
+ subject.get '/example' do
249
+ c = Class.new do
250
+ attr_reader :name
251
+ def initialize(args)
252
+ @name = args[:name] || "no name set"
253
+ end
254
+ end
255
+ present c.new(name: "johnnyiller"), with: entity
256
+ end
257
+ get '/example'
258
+ expect(last_response.status).to eq(200)
259
+ expect(last_response.headers['Content-type']).to eq("application/json")
260
+ expect(last_response.body).to eq('{"example":{"name":"johnnyiller"}}')
261
+ end
262
+
263
+ it 'presents with jsonp utilising Rack::JSONP' do
264
+ require 'rack/contrib'
265
+
266
+ # Include JSONP middleware
267
+ subject.use Rack::JSONP
268
+
269
+ entity = Class.new(Grape::Entity)
270
+ entity.root "examples", "example"
271
+ entity.expose :name
272
+
273
+ # Rack::JSONP expects a standard JSON response
274
+ subject.format :json
275
+
276
+ subject.get '/example' do
277
+ c = Class.new do
278
+ attr_reader :name
279
+ def initialize(args)
280
+ @name = args[:name] || "no name set"
281
+ end
282
+ end
283
+
284
+ present c.new(name: "johnnyiller"), with: entity
285
+ end
286
+
287
+ get '/example?callback=abcDef'
288
+ expect(last_response.status).to eq(200)
289
+ expect(last_response.headers['Content-type']).to eq("application/javascript")
290
+ expect(last_response.body).to eq('abcDef({"example":{"name":"johnnyiller"}})')
291
+ end
292
+
293
+ context "present with multiple entities" do
294
+ it "present with multiple entities using optional symbol" do
295
+ user = Class.new do
296
+ attr_reader :name
297
+ def initialize(args)
298
+ @name = args[:name] || "no name set"
299
+ end
300
+ end
301
+ user1 = user.new(name: 'user1')
302
+ user2 = user.new(name: 'user2')
303
+
304
+ entity = Class.new(Grape::Entity)
305
+ entity.expose :name
306
+
307
+ subject.format :json
308
+ subject.get '/example' do
309
+ present :page, 1
310
+ present :user1, user1, with: entity
311
+ present :user2, user2, with: entity
312
+ end
313
+ get '/example'
314
+ expect_response_json = {
315
+ "page" => 1,
316
+ "user1" => { "name" => "user1" },
317
+ "user2" => { "name" => "user2" }
318
+ }
319
+ expect(JSON(last_response.body)).to eq(expect_response_json)
320
+ end
321
+
322
+ end
323
+ end
324
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Grape::Exceptions::InvalidFormatter do
5
+ describe "#message" do
6
+ let(:error) do
7
+ described_class.new(String, 'xml')
8
+ end
9
+
10
+ it "contains the problem in the message" do
11
+ expect(error.message).to include(
12
+ "cannot convert String to xml"
13
+ )
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Grape::Exceptions::InvalidVersionerOption do
5
+ describe "#message" do
6
+ let(:error) do
7
+ described_class.new("headers")
8
+ end
9
+
10
+ it "contains the problem in the message" do
11
+ expect(error.message).to include(
12
+ "Unknown :using for versioner: headers"
13
+ )
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Exceptions::MissingMimeType do
4
+ describe "#message" do
5
+
6
+ let(:error) do
7
+ described_class.new("new_json")
8
+ end
9
+
10
+ it "contains the problem in the message" do
11
+ expect(error.message).to include "missing mime type for new_json"
12
+ end
13
+
14
+ it "contains the resolution in the message" do
15
+ expect(error.message).to include "or add your own with content_type :new_json, 'application/new_json' "
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Grape::Exceptions::MissingOption do
5
+ describe "#message" do
6
+ let(:error) do
7
+ described_class.new(:path)
8
+ end
9
+
10
+ it "contains the problem in the message" do
11
+ expect(error.message).to include(
12
+ "You must specify :path options."
13
+ )
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Grape::Exceptions::UnknownOptions do
5
+ describe "#message" do
6
+ let(:error) do
7
+ described_class.new([:a, :b])
8
+ end
9
+
10
+ it "contains the problem in the message" do
11
+ expect(error.message).to include(
12
+ "unknown options: "
13
+ )
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Grape::Exceptions::UnknownValidator do
5
+ describe "#message" do
6
+ let(:error) do
7
+ described_class.new('gt_10')
8
+ end
9
+
10
+ it "contains the problem in the message" do
11
+ expect(error.message).to include(
12
+ "unknown validator: gt_10"
13
+ )
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ describe Grape::Exceptions::ValidationErrors do
5
+ let(:validation_message) { "FooBar is invalid" }
6
+ let(:validation_error) { OpenStruct.new(param: validation_message) }
7
+
8
+ context "message" do
9
+ context "is not repeated" do
10
+ let(:error) do
11
+ described_class.new(errors: [validation_error, validation_error])
12
+ end
13
+ subject(:message) { error.message.split(',').map(&:strip) }
14
+
15
+ it { expect(message).to include validation_message }
16
+ it { expect(message.size).to eq 1 }
17
+ end
18
+ end
19
+ end