source: subversion/sites/rails_port/test/functional/node_controller_test.rb @ 16893

Last change on this file since 16893 was 16893, checked in by zere, 10 years ago

Made XML parsing routines raise an exception if the document is valid XML, but not valid as an OSM document. This is now the same behaviour as when the document isn't valid XML.

File size: 19.1 KB
Line 
1require File.dirname(__FILE__) + '/../test_helper'
2
3class NodeControllerTest < ActionController::TestCase
4  api_fixtures
5
6  def test_create
7    # cannot read password from fixture as it is stored as MD5 digest
8    ## First try with no auth
9   
10    # create a node with random lat/lon
11    lat = rand(100)-50 + rand
12    lon = rand(100)-50 + rand
13    # normal user has a changeset open, so we'll use that.
14    changeset = changesets(:normal_user_first_change)
15    # create a minimal xml file
16    content("<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>")
17    assert_difference('OldNode.count', 0) do
18      put :create
19    end
20    # hope for unauthorized
21    assert_response :unauthorized, "node upload did not return unauthorized status"
22
23   
24   
25    ## Now try with the user which doesn't have their data public
26    basic_authorization(users(:normal_user).email, "test")
27   
28    # create a node with random lat/lon
29    lat = rand(100)-50 + rand
30    lon = rand(100)-50 + rand
31    # normal user has a changeset open, so we'll use that.
32    changeset = changesets(:normal_user_first_change)
33    # create a minimal xml file
34    content("<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>")
35    assert_difference('Node.count', 0) do
36      put :create
37    end
38    # hope for success
39    assert_require_public_data "node create did not return forbidden status"
40
41
42   
43    ## Now try with the user that has the public data
44    basic_authorization(users(:public_user).email, "test")
45   
46    # create a node with random lat/lon
47    lat = rand(100)-50 + rand
48    lon = rand(100)-50 + rand
49    # normal user has a changeset open, so we'll use that.
50    changeset = changesets(:public_user_first_change)
51    # create a minimal xml file
52    content("<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>")
53    put :create
54    # hope for success
55    assert_response :success, "node upload did not return success status"
56
57    # read id of created node and search for it
58    nodeid = @response.body
59    checknode = Node.find(nodeid)
60    assert_not_nil checknode, "uploaded node not found in data base after upload"
61    # compare values
62    assert_in_delta lat * 10000000, checknode.latitude, 1, "saved node does not match requested latitude"
63    assert_in_delta lon * 10000000, checknode.longitude, 1, "saved node does not match requested longitude"
64    assert_equal changesets(:public_user_first_change).id, checknode.changeset_id, "saved node does not belong to changeset that it was created in"
65    assert_equal true, checknode.visible, "saved node is not visible"
66  end
67
68  def test_create_invalid_xml
69    ## Only test public user here, as test_create should cover what's the forbiddens
70    ## that would occur here
71    # Initial setup
72    basic_authorization(users(:public_user).email, "test")
73    # normal user has a changeset open, so we'll use that.
74    changeset = changesets(:public_user_first_change)
75    lat = 3.434
76    lon = 3.23
77   
78    # test that the upload is rejected when xml is valid, but osm doc isn't
79    content("<create/>")
80    put :create
81    assert_response :bad_request, "node upload did not return bad_request status"
82    assert_equal "Cannot parse valid node from xml string <create/>. XML doesn't contain an osm/node element.", @response.body
83
84    # test that the upload is rejected when no lat is supplied
85    # create a minimal xml file
86    content("<osm><node lon='#{lon}' changeset='#{changeset.id}'/></osm>")
87    put :create
88    # hope for success
89    assert_response :bad_request, "node upload did not return bad_request status"
90    assert_equal "Cannot parse valid node from xml string <node lon=\"3.23\" changeset=\"#{changeset.id}\"/>. lat missing", @response.body
91
92    # test that the upload is rejected when no lon is supplied
93    # create a minimal xml file
94    content("<osm><node lat='#{lat}' changeset='#{changeset.id}'/></osm>")
95    put :create
96    # hope for success
97    assert_response :bad_request, "node upload did not return bad_request status"
98    assert_equal "Cannot parse valid node from xml string <node lat=\"3.434\" changeset=\"#{changeset.id}\"/>. lon missing", @response.body
99
100    # test that the upload is rejected when we have a tag which is too long
101    content("<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'><tag k='foo' v='#{'x'*256}'/></node></osm>")
102    put :create
103    assert_response :bad_request, "node upload did not return bad_request status"
104    assert_equal ["NodeTag ", " v: is too long (maximum is 255 characters) (\"#{'x'*256}\")"], @response.body.split(/[0-9]+:/)
105
106  end
107
108  def test_read
109    # check that a visible node is returned properly
110    get :read, :id => current_nodes(:visible_node).id
111    assert_response :success
112
113    # check that an invisible node is not returned
114    get :read, :id => current_nodes(:invisible_node).id
115    assert_response :gone
116
117    # check chat a non-existent node is not returned
118    get :read, :id => 0
119    assert_response :not_found
120  end
121
122  # this tests deletion restrictions - basic deletion is tested in the unit
123  # tests for node!
124  def test_delete
125    ## first try to delete node without auth
126    delete :delete, :id => current_nodes(:visible_node).id
127    assert_response :unauthorized
128   
129   
130    ## now set auth for the non-data public user
131    basic_authorization(users(:normal_user).email, "test"); 
132
133    # try to delete with an invalid (closed) changeset
134    content update_changeset(current_nodes(:visible_node).to_xml,
135                             changesets(:normal_user_closed_change).id)
136    delete :delete, :id => current_nodes(:visible_node).id
137    assert_require_public_data("non-public user shouldn't be able to delete node")
138
139    # try to delete with an invalid (non-existent) changeset
140    content update_changeset(current_nodes(:visible_node).to_xml,0)
141    delete :delete, :id => current_nodes(:visible_node).id
142    assert_require_public_data("shouldn't be able to delete node, when user's data is private")
143
144    # valid delete now takes a payload
145    content(nodes(:visible_node).to_xml)
146    delete :delete, :id => current_nodes(:visible_node).id
147    assert_require_public_data("shouldn't be able to delete node when user's data isn't public'")
148
149    # this won't work since the node is already deleted
150    content(nodes(:invisible_node).to_xml)
151    delete :delete, :id => current_nodes(:invisible_node).id
152    assert_require_public_data
153
154    # this won't work since the node never existed
155    delete :delete, :id => 0
156    assert_require_public_data
157
158    ## these test whether nodes which are in-use can be deleted:
159    # in a way...
160    content(nodes(:used_node_1).to_xml)
161    delete :delete, :id => current_nodes(:used_node_1).id
162    assert_require_public_data
163       "shouldn't be able to delete a node used in a way (#{@response.body})"
164
165    # in a relation...
166    content(nodes(:node_used_by_relationship).to_xml)
167    delete :delete, :id => current_nodes(:node_used_by_relationship).id
168    assert_require_public_data
169       "shouldn't be able to delete a node used in a relation (#{@response.body})"
170
171   
172
173    ## now set auth for the public data user
174    basic_authorization(users(:public_user).email, "test"); 
175
176    # try to delete with an invalid (closed) changeset
177    content update_changeset(current_nodes(:visible_node).to_xml,
178                             changesets(:normal_user_closed_change).id)
179    delete :delete, :id => current_nodes(:visible_node).id
180    assert_response :conflict
181
182    # try to delete with an invalid (non-existent) changeset
183    content update_changeset(current_nodes(:visible_node).to_xml,0)
184    delete :delete, :id => current_nodes(:visible_node).id
185    assert_response :conflict
186
187    # try to delete a node with a different ID
188    content(nodes(:public_visible_node).to_xml)
189    delete :delete, :id => current_nodes(:visible_node).id
190    assert_response :bad_request, 
191       "should not be able to delete a node with a different ID from the XML"
192
193    # try to delete a node rubbish in the payloads
194    content("<delete/>")
195    delete :delete, :id => current_nodes(:visible_node).id
196    assert_response :bad_request, 
197       "should not be able to delete a node without a valid XML payload"
198
199    # valid delete now takes a payload
200    content(nodes(:public_visible_node).to_xml)
201    delete :delete, :id => current_nodes(:public_visible_node).id
202    assert_response :success
203
204    # valid delete should return the new version number, which should
205    # be greater than the old version number
206    assert @response.body.to_i > current_nodes(:public_visible_node).version,
207       "delete request should return a new version number for node"
208
209    # this won't work since the node is already deleted
210    content(nodes(:invisible_node).to_xml)
211    delete :delete, :id => current_nodes(:invisible_node).id
212    assert_response :gone
213
214    # this won't work since the node never existed
215    delete :delete, :id => 0
216    assert_response :not_found
217
218    ## these test whether nodes which are in-use can be deleted:
219    # in a way...
220    content(nodes(:used_node_1).to_xml)
221    delete :delete, :id => current_nodes(:used_node_1).id
222    assert_response :precondition_failed,
223       "shouldn't be able to delete a node used in a way (#{@response.body})"
224    assert_equal "Precondition failed: Node 3 is still used by way 1.", @response.body
225
226    # in a relation...
227    content(nodes(:node_used_by_relationship).to_xml)
228    delete :delete, :id => current_nodes(:node_used_by_relationship).id
229    assert_response :precondition_failed,
230       "shouldn't be able to delete a node used in a relation (#{@response.body})"
231    assert_match /Precondition failed: Node 5 is still used by relation [13]./, @response.body
232  end
233
234  ##
235  # tests whether the API works and prevents incorrect use while trying
236  # to update nodes.
237  def test_update
238    ## First test with no user credentials
239    # try and update a node without authorisation
240    # first try to delete node without auth
241    content current_nodes(:visible_node).to_xml
242    put :update, :id => current_nodes(:visible_node).id
243    assert_response :unauthorized
244   
245   
246   
247    ## Second test with the private user
248   
249    # setup auth
250    basic_authorization(users(:normal_user).email, "test")
251
252    ## trying to break changesets
253
254    # try and update in someone else's changeset
255    content update_changeset(current_nodes(:visible_node).to_xml,
256                             changesets(:public_user_first_change).id)
257    put :update, :id => current_nodes(:visible_node).id
258    assert_require_public_data "update with other user's changeset should be forbidden when date isn't public"
259
260    # try and update in a closed changeset
261    content update_changeset(current_nodes(:visible_node).to_xml,
262                             changesets(:normal_user_closed_change).id)
263    put :update, :id => current_nodes(:visible_node).id
264    assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
265
266    # try and update in a non-existant changeset
267    content update_changeset(current_nodes(:visible_node).to_xml, 0)
268    put :update, :id => current_nodes(:visible_node).id
269    assert_require_public_data("update with changeset=0 should be forbidden, when data isn't public")
270
271    ## try and submit invalid updates
272    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lat', 91.0);
273    put :update, :id => current_nodes(:visible_node).id
274    assert_require_public_data "node at lat=91 should be forbidden, when data isn't public"
275
276    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lat', -91.0);
277    put :update, :id => current_nodes(:visible_node).id
278    assert_require_public_data "node at lat=-91 should be forbidden, when data isn't public"
279   
280    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lon', 181.0);
281    put :update, :id => current_nodes(:visible_node).id
282    assert_require_public_data "node at lon=181 should be forbidden, when data isn't public"
283
284    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lon', -181.0);
285    put :update, :id => current_nodes(:visible_node).id
286    assert_require_public_data "node at lon=-181 should be forbidden, when data isn't public"
287   
288    ## finally, produce a good request which should work
289    content current_nodes(:visible_node).to_xml
290    put :update, :id => current_nodes(:visible_node).id
291    assert_require_public_data "should have failed with a forbidden when data isn't public"
292   
293    ## Finally test with the public user
294   
295    # try and update a node without authorisation
296    # first try to delete node without auth
297    content current_nodes(:visible_node).to_xml
298    put :update, :id => current_nodes(:visible_node).id
299    assert_response :forbidden
300   
301    # setup auth
302    basic_authorization(users(:public_user).email, "test")
303
304    ## trying to break changesets
305
306    # try and update in someone else's changeset
307    content update_changeset(current_nodes(:visible_node).to_xml,
308                              changesets(:normal_user_first_change).id)
309    put :update, :id => current_nodes(:visible_node).id
310    assert_response :conflict, "update with other user's changeset should be rejected"
311
312    # try and update in a closed changeset
313    content update_changeset(current_nodes(:visible_node).to_xml,
314                             changesets(:normal_user_closed_change).id)
315    put :update, :id => current_nodes(:visible_node).id
316    assert_response :conflict, "update with closed changeset should be rejected"
317
318    # try and update in a non-existant changeset
319    content update_changeset(current_nodes(:visible_node).to_xml, 0)
320    put :update, :id => current_nodes(:visible_node).id
321    assert_response :conflict, "update with changeset=0 should be rejected"
322
323    ## try and submit invalid updates
324    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lat', 91.0);
325    put :update, :id => current_nodes(:visible_node).id
326    assert_response :bad_request, "node at lat=91 should be rejected"
327
328    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lat', -91.0);
329    put :update, :id => current_nodes(:visible_node).id
330    assert_response :bad_request, "node at lat=-91 should be rejected"
331   
332    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lon', 181.0);
333    put :update, :id => current_nodes(:visible_node).id
334    assert_response :bad_request, "node at lon=181 should be rejected"
335
336    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 'lon', -181.0);
337    put :update, :id => current_nodes(:visible_node).id
338    assert_response :bad_request, "node at lon=-181 should be rejected"
339
340    ## next, attack the versioning
341    current_node_version = current_nodes(:visible_node).version
342
343    # try and submit a version behind
344    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 
345                             'version', current_node_version - 1);
346    put :update, :id => current_nodes(:visible_node).id
347    assert_response :conflict, "should have failed on old version number"
348   
349    # try and submit a version ahead
350    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 
351                             'version', current_node_version + 1);
352    put :update, :id => current_nodes(:visible_node).id
353    assert_response :conflict, "should have failed on skipped version number"
354
355    # try and submit total crap in the version field
356    content xml_attr_rewrite(current_nodes(:visible_node).to_xml, 
357                             'version', 'p1r4t3s!');
358    put :update, :id => current_nodes(:visible_node).id
359    assert_response :conflict, 
360       "should not be able to put 'p1r4at3s!' in the version field"
361   
362    ## try an update with the wrong ID
363    content current_nodes(:public_visible_node).to_xml
364    put :update, :id => current_nodes(:visible_node).id
365    assert_response :bad_request, 
366       "should not be able to update a node with a different ID from the XML"
367
368    ## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
369    content "<update/>"
370    put :update, :id => current_nodes(:visible_node).id
371    assert_response :bad_request, 
372       "should not be able to update a node with non-OSM XML doc."
373
374    ## finally, produce a good request which should work
375    content current_nodes(:public_visible_node).to_xml
376    put :update, :id => current_nodes(:public_visible_node).id
377    assert_response :success, "a valid update request failed"
378  end
379
380  ##
381  # test adding tags to a node
382  def test_duplicate_tags
383    # setup auth
384    basic_authorization(users(:public_user).email, "test")
385
386    # add an identical tag to the node
387    tag_xml = XML::Node.new("tag")
388    tag_xml['k'] = current_node_tags(:public_v_t1).k
389    tag_xml['v'] = current_node_tags(:public_v_t1).v
390
391    # add the tag into the existing xml
392    node_xml = current_nodes(:public_visible_node).to_xml
393    node_xml.find("//osm/node").first << tag_xml
394
395    # try and upload it
396    content node_xml
397    put :update, :id => current_nodes(:public_visible_node).id
398    assert_response :bad_request, 
399      "adding duplicate tags to a node should fail with 'bad request'"
400    assert_equal "Element node/#{current_nodes(:public_visible_node).id} has duplicate tags with key #{current_node_tags(:t1).k}", @response.body
401  end
402
403  # test whether string injection is possible
404  def test_string_injection
405    ## First try with the non-data public user
406    basic_authorization(users(:normal_user).email, "test")
407    changeset_id = changesets(:normal_user_first_change).id
408
409    # try and put something into a string that the API might
410    # use unquoted and therefore allow code injection...
411    content "<osm><node lat='0' lon='0' changeset='#{changeset_id}'>" +
412      '<tag k="#{@user.inspect}" v="0"/>' +
413      '</node></osm>'
414    put :create
415    assert_require_public_data "Shouldn't be able to create with non-public user"
416   
417   
418    ## Then try with the public data user
419    basic_authorization(users(:public_user).email, "test")
420    changeset_id = changesets(:public_user_first_change).id
421
422    # try and put something into a string that the API might
423    # use unquoted and therefore allow code injection...
424    content "<osm><node lat='0' lon='0' changeset='#{changeset_id}'>" +
425      '<tag k="#{@user.inspect}" v="0"/>' +
426      '</node></osm>'
427    put :create
428    assert_response :success
429    nodeid = @response.body
430
431    # find the node in the database
432    checknode = Node.find(nodeid)
433    assert_not_nil checknode, "node not found in data base after upload"
434   
435    # and grab it using the api
436    get :read, :id => nodeid
437    assert_response :success
438    apinode = Node.from_xml(@response.body)
439    assert_not_nil apinode, "downloaded node is nil, but shouldn't be"
440   
441    # check the tags are not corrupted
442    assert_equal checknode.tags, apinode.tags
443    assert apinode.tags.include?('#{@user.inspect}')
444  end
445
446  def basic_authorization(user, pass)
447    @request.env["HTTP_AUTHORIZATION"] = "Basic %s" % Base64.encode64("#{user}:#{pass}")
448  end
449
450  def content(c)
451    @request.env["RAW_POST_DATA"] = c.to_s
452  end
453
454  ##
455  # update the changeset_id of a node element
456  def update_changeset(xml, changeset_id)
457    xml_attr_rewrite(xml, 'changeset', changeset_id)
458  end
459
460  ##
461  # update an attribute in the node element
462  def xml_attr_rewrite(xml, name, value)
463    xml.find("//osm/node").first[name] = value.to_s
464    return xml
465  end
466
467  ##
468  # parse some xml
469  def xml_parse(xml)
470    parser = XML::Parser.string(xml)
471    parser.parse
472  end
473end
Note: See TracBrowser for help on using the repository browser.