Merge remote-tracking branch 'upstream/pull/2161'

This commit is contained in:
Tom Hughes 2019-02-28 19:17:40 +00:00
commit 94b59f4403
88 changed files with 12288 additions and 11991 deletions

View file

@ -13,9 +13,10 @@ Lint/AssignmentInCondition:
- 'app/controllers/application_controller.rb'
- 'app/controllers/geocoder_controller.rb'
- 'app/controllers/notes_controller.rb'
- 'app/controllers/api/traces_controller.rb'
- 'app/controllers/traces_controller.rb'
- 'app/controllers/users_controller.rb'
- 'app/controllers/user_preferences_controller.rb'
- 'app/controllers/api/user_preferences_controller.rb'
- 'app/helpers/application_helper.rb'
- 'app/helpers/browse_helper.rb'
- 'app/helpers/browse_tags_helper.rb'
@ -28,7 +29,7 @@ Lint/AssignmentInCondition:
# Offense count: 4
Lint/HandleExceptions:
Exclude:
- 'app/controllers/amf_controller.rb'
- 'app/controllers/api/amf_controller.rb'
- 'app/controllers/users_controller.rb'
# Offense count: 692
@ -178,7 +179,7 @@ Style/FrozenStringLiteralComment:
# Cop supports --auto-correct.
Style/IfUnlessModifier:
Exclude:
- 'app/controllers/ways_controller.rb'
- 'app/controllers/api/ways_controller.rb'
# Offense count: 70
# Cop supports --auto-correct.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
module Api
class ChangesetCommentsController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authorize
before_action :api_deny_access_handler
authorize_resource
before_action :require_public_data, :only => [:create]
before_action :check_api_writable
before_action :check_api_readable, :except => [:create]
around_action :api_call_handle_error
around_action :api_call_timeout
##
# Add a comment to a changeset
def create
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
# Extract the arguments
id = params[:id].to_i
body = params[:text]
# Find the changeset and check it is valid
changeset = Changeset.find(id)
raise OSM::APIChangesetNotYetClosedError, changeset if changeset.is_open?
# Add a comment to the changeset
comment = changeset.comments.create(:changeset => changeset,
:body => body,
:author => current_user)
# Notify current subscribers of the new comment
changeset.subscribers.visible.each do |user|
Notifier.changeset_comment_notification(comment, user).deliver_later if current_user != user
end
# Add the commenter to the subscribers if necessary
changeset.subscribers << current_user unless changeset.subscribers.exists?(current_user.id)
# Return a copy of the updated changeset
render :xml => changeset.to_xml.to_s
end
##
# Sets visible flag on comment to false
def destroy
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
# Find the changeset
comment = ChangesetComment.find(id)
# Hide the comment
comment.update(:visible => false)
# Return a copy of the updated changeset
render :xml => comment.changeset.to_xml.to_s
end
##
# Sets visible flag on comment to true
def restore
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
# Find the changeset
comment = ChangesetComment.find(id)
# Unhide the comment
comment.update(:visible => true)
# Return a copy of the updated changeset
render :xml => comment.changeset.to_xml.to_s
end
end
end

View file

@ -0,0 +1,422 @@
# The ChangesetController is the RESTful interface to Changeset objects
module Api
class ChangesetsController < ApplicationController
layout "site"
require "xml/libxml"
skip_before_action :verify_authenticity_token
before_action :authorize, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe]
before_action :api_deny_access_handler, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe, :expand_bbox]
authorize_resource
before_action :require_public_data, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe]
before_action :check_api_writable, :only => [:create, :update, :upload, :subscribe, :unsubscribe]
before_action :check_api_readable, :except => [:create, :update, :upload, :download, :query, :subscribe, :unsubscribe]
before_action(:only => [:index, :feed]) { |c| c.check_database_readable(true) }
around_action :api_call_handle_error
around_action :api_call_timeout, :except => [:upload]
# Helper methods for checking consistency
include ConsistencyValidations
# Create a changeset from XML.
def create
assert_method :put
cs = Changeset.from_xml(request.raw_post, true)
# Assume that Changeset.from_xml has thrown an exception if there is an error parsing the xml
cs.user = current_user
cs.save_with_tags!
# Subscribe user to changeset comments
cs.subscribers << current_user
render :plain => cs.id.to_s
end
##
# Return XML giving the basic info about the changeset. Does not
# return anything about the nodes, ways and relations in the changeset.
def show
changeset = Changeset.find(params[:id])
render :xml => changeset.to_xml(params[:include_discussion].presence).to_s
end
##
# marks a changeset as closed. this may be called multiple times
# on the same changeset, so is idempotent.
def close
assert_method :put
changeset = Changeset.find(params[:id])
check_changeset_consistency(changeset, current_user)
# to close the changeset, we'll just set its closed_at time to
# now. this might not be enough if there are concurrency issues,
# but we'll have to wait and see.
changeset.set_closed_time_now
changeset.save!
head :ok
end
##
# insert a (set of) points into a changeset bounding box. this can only
# increase the size of the bounding box. this is a hint that clients can
# set either before uploading a large number of changes, or changes that
# the client (but not the server) knows will affect areas further away.
def expand_bbox
# only allow POST requests, because although this method is
# idempotent, there is no "document" to PUT really...
assert_method :post
cs = Changeset.find(params[:id])
check_changeset_consistency(cs, current_user)
# keep an array of lons and lats
lon = []
lat = []
# the request is in pseudo-osm format... this is kind-of an
# abuse, maybe should change to some other format?
doc = XML::Parser.string(request.raw_post, :options => XML::Parser::Options::NOERROR).parse
doc.find("//osm/node").each do |n|
lon << n["lon"].to_f * GeoRecord::SCALE
lat << n["lat"].to_f * GeoRecord::SCALE
end
# add the existing bounding box to the lon-lat array
lon << cs.min_lon unless cs.min_lon.nil?
lat << cs.min_lat unless cs.min_lat.nil?
lon << cs.max_lon unless cs.max_lon.nil?
lat << cs.max_lat unless cs.max_lat.nil?
# collapse the arrays to minimum and maximum
cs.min_lon = lon.min
cs.min_lat = lat.min
cs.max_lon = lon.max
cs.max_lat = lat.max
# save the larger bounding box and return the changeset, which
# will include the bigger bounding box.
cs.save!
render :xml => cs.to_xml.to_s
end
##
# Upload a diff in a single transaction.
#
# This means that each change within the diff must succeed, i.e: that
# each version number mentioned is still current. Otherwise the entire
# transaction *must* be rolled back.
#
# Furthermore, each element in the diff can only reference the current
# changeset.
#
# Returns: a diffResult document, as described in
# http://wiki.openstreetmap.org/wiki/OSM_Protocol_Version_0.6
def upload
# only allow POST requests, as the upload method is most definitely
# not idempotent, as several uploads with placeholder IDs will have
# different side-effects.
# see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2
assert_method :post
changeset = Changeset.find(params[:id])
check_changeset_consistency(changeset, current_user)
diff_reader = DiffReader.new(request.raw_post, changeset)
Changeset.transaction do
result = diff_reader.commit
render :xml => result.to_s
end
end
##
# download the changeset as an osmChange document.
#
# to make it easier to revert diffs it would be better if the osmChange
# format were reversible, i.e: contained both old and new versions of
# modified elements. but it doesn't at the moment...
#
# this method cannot order the database changes fully (i.e: timestamp and
# version number may be too coarse) so the resulting diff may not apply
# to a different database. however since changesets are not atomic this
# behaviour cannot be guaranteed anyway and is the result of a design
# choice.
def download
changeset = Changeset.find(params[:id])
# get all the elements in the changeset which haven't been redacted
# and stick them in a big array.
elements = [changeset.old_nodes.unredacted,
changeset.old_ways.unredacted,
changeset.old_relations.unredacted].flatten
# sort the elements by timestamp and version number, as this is the
# almost sensible ordering available. this would be much nicer if
# global (SVN-style) versioning were used - then that would be
# unambiguous.
elements.sort! do |a, b|
if a.timestamp == b.timestamp
a.version <=> b.version
else
a.timestamp <=> b.timestamp
end
end
# create changeset and user caches
changeset_cache = {}
user_display_name_cache = {}
# create an osmChange document for the output
result = OSM::API.new.get_xml_doc
result.root.name = "osmChange"
# generate an output element for each operation. note: we avoid looking
# at the history because it is simpler - but it would be more correct to
# check these assertions.
elements.each do |elt|
result.root <<
if elt.version == 1
# first version, so it must be newly-created.
created = XML::Node.new "create"
created << elt.to_xml_node(changeset_cache, user_display_name_cache)
elsif elt.visible
# must be a modify
modified = XML::Node.new "modify"
modified << elt.to_xml_node(changeset_cache, user_display_name_cache)
else
# if the element isn't visible then it must have been deleted
deleted = XML::Node.new "delete"
deleted << elt.to_xml_node(changeset_cache, user_display_name_cache)
end
end
render :xml => result.to_s
end
##
# query changesets by bounding box, time, user or open/closed status.
def query
# find any bounding box
bbox = BoundingBox.from_bbox_params(params) if params["bbox"]
# create the conditions that the user asked for. some or all of
# these may be nil.
changesets = Changeset.all
changesets = conditions_bbox(changesets, bbox)
changesets = conditions_user(changesets, params["user"], params["display_name"])
changesets = conditions_time(changesets, params["time"])
changesets = conditions_open(changesets, params["open"])
changesets = conditions_closed(changesets, params["closed"])
changesets = conditions_ids(changesets, params["changesets"])
# sort and limit the changesets
changesets = changesets.order("created_at DESC").limit(100)
# preload users, tags and comments
changesets = changesets.preload(:user, :changeset_tags, :comments)
# create the results document
results = OSM::API.new.get_xml_doc
# add all matching changesets to the XML results document
changesets.order("created_at DESC").limit(100).each do |cs|
results.root << cs.to_xml_node
end
render :xml => results.to_s
end
##
# updates a changeset's tags. none of the changeset's attributes are
# user-modifiable, so they will be ignored.
#
# changesets are not (yet?) versioned, so we don't have to deal with
# history tables here. changesets are locked to a single user, however.
#
# after succesful update, returns the XML of the changeset.
def update
# request *must* be a PUT.
assert_method :put
changeset = Changeset.find(params[:id])
new_changeset = Changeset.from_xml(request.raw_post)
check_changeset_consistency(changeset, current_user)
changeset.update_from(new_changeset, current_user)
render :xml => changeset.to_xml.to_s
end
##
# Adds a subscriber to the changeset
def subscribe
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
# Find the changeset and check it is valid
changeset = Changeset.find(id)
raise OSM::APIChangesetAlreadySubscribedError, changeset if changeset.subscribers.exists?(current_user.id)
# Add the subscriber
changeset.subscribers << current_user
# Return a copy of the updated changeset
render :xml => changeset.to_xml.to_s
end
##
# Removes a subscriber from the changeset
def unsubscribe
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
# Find the changeset and check it is valid
changeset = Changeset.find(id)
raise OSM::APIChangesetNotSubscribedError, changeset unless changeset.subscribers.exists?(current_user.id)
# Remove the subscriber
changeset.subscribers.delete(current_user)
# Return a copy of the updated changeset
render :xml => changeset.to_xml.to_s
end
private
#------------------------------------------------------------
# utility functions below.
#------------------------------------------------------------
##
# if a bounding box was specified do some sanity checks.
# restrict changesets to those enclosed by a bounding box
# we need to return both the changesets and the bounding box
def conditions_bbox(changesets, bbox)
if bbox
bbox.check_boundaries
bbox = bbox.to_scaled
changesets.where("min_lon < ? and max_lon > ? and min_lat < ? and max_lat > ?",
bbox.max_lon.to_i, bbox.min_lon.to_i,
bbox.max_lat.to_i, bbox.min_lat.to_i)
else
changesets
end
end
##
# restrict changesets to those by a particular user
def conditions_user(changesets, user, name)
if user.nil? && name.nil?
changesets
else
# shouldn't provide both name and UID
raise OSM::APIBadUserInput, "provide either the user ID or display name, but not both" if user && name
# use either the name or the UID to find the user which we're selecting on.
u = if name.nil?
# user input checking, we don't have any UIDs < 1
raise OSM::APIBadUserInput, "invalid user ID" if user.to_i < 1
u = User.find(user.to_i)
else
u = User.find_by(:display_name => name)
end
# make sure we found a user
raise OSM::APINotFoundError if u.nil?
# should be able to get changesets of public users only, or
# our own changesets regardless of public-ness.
unless u.data_public?
# get optional user auth stuff so that users can see their own
# changesets if they're non-public
setup_user_auth
raise OSM::APINotFoundError if current_user.nil? || current_user != u
end
changesets.where(:user_id => u.id)
end
end
##
# restrict changes to those closed during a particular time period
def conditions_time(changesets, time)
if time.nil?
changesets
elsif time.count(",") == 1
# if there is a range, i.e: comma separated, then the first is
# low, second is high - same as with bounding boxes.
# check that we actually have 2 elements in the array
times = time.split(/,/)
raise OSM::APIBadUserInput, "bad time range" if times.size != 2
from, to = times.collect { |t| Time.parse(t) }
changesets.where("closed_at >= ? and created_at <= ?", from, to)
else
# if there is no comma, assume its a lower limit on time
changesets.where("closed_at >= ?", Time.parse(time))
end
# stupid Time seems to throw both of these for bad parsing, so
# we have to catch both and ensure the correct code path is taken.
rescue ArgumentError => ex
raise OSM::APIBadUserInput, ex.message.to_s
rescue RuntimeError => ex
raise OSM::APIBadUserInput, ex.message.to_s
end
##
# return changesets which are open (haven't been closed yet)
# we do this by seeing if the 'closed at' time is in the future. Also if we've
# hit the maximum number of changes then it counts as no longer open.
# if parameter 'open' is nill then open and closed changesets are returned
def conditions_open(changesets, open)
if open.nil?
changesets
else
changesets.where("closed_at >= ? and num_changes <= ?",
Time.now.getutc, Changeset::MAX_ELEMENTS)
end
end
##
# query changesets which are closed
# ('closed at' time has passed or changes limit is hit)
def conditions_closed(changesets, closed)
if closed.nil?
changesets
else
changesets.where("closed_at < ? or num_changes > ?",
Time.now.getutc, Changeset::MAX_ELEMENTS)
end
end
##
# query changesets by a list of ids
# (either specified as array or comma-separated string)
def conditions_ids(changesets, ids)
if ids.nil?
changesets
elsif ids.empty?
raise OSM::APIBadUserInput, "No changesets were given to search for"
else
ids = ids.split(",").collect(&:to_i)
changesets.where(:id => ids)
end
end
end
end

View file

@ -0,0 +1,83 @@
# The NodeController is the RESTful interface to Node objects
module Api
class NodesController < ApplicationController
require "xml/libxml"
skip_before_action :verify_authenticity_token
before_action :authorize, :only => [:create, :update, :delete]
before_action :api_deny_access_handler
authorize_resource
before_action :require_public_data, :only => [:create, :update, :delete]
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
around_action :api_call_handle_error, :api_call_timeout
# Create a node from XML.
def create
assert_method :put
node = Node.from_xml(request.raw_post, true)
# Assume that Node.from_xml has thrown an exception if there is an error parsing the xml
node.create_with_history current_user
render :plain => node.id.to_s
end
# Dump the details on a node given in params[:id]
def show
node = Node.find(params[:id])
response.last_modified = node.timestamp
if node.visible
render :xml => node.to_xml.to_s
else
head :gone
end
end
# Update a node from given XML
def update
node = Node.find(params[:id])
new_node = Node.from_xml(request.raw_post)
raise OSM::APIBadUserInput, "The id in the url (#{node.id}) is not the same as provided in the xml (#{new_node.id})" unless new_node && new_node.id == node.id
node.update_from(new_node, current_user)
render :plain => node.version.to_s
end
# Delete a node. Doesn't actually delete it, but retains its history
# in a wiki-like way. We therefore treat it like an update, so the delete
# method returns the new version number.
def delete
node = Node.find(params[:id])
new_node = Node.from_xml(request.raw_post)
raise OSM::APIBadUserInput, "The id in the url (#{node.id}) is not the same as provided in the xml (#{new_node.id})" unless new_node && new_node.id == node.id
node.delete_with_history!(new_node, current_user)
render :plain => node.version.to_s
end
# Dump the details on many nodes whose ids are given in the "nodes" parameter.
def index
raise OSM::APIBadUserInput, "The parameter nodes is required, and must be of the form nodes=id[,id[,id...]]" unless params["nodes"]
ids = params["nodes"].split(",").collect(&:to_i)
raise OSM::APIBadUserInput, "No nodes were given to search for" if ids.empty?
doc = OSM::API.new.get_xml_doc
Node.find(ids).each do |node|
doc.root << node.to_xml_node
end
render :xml => doc.to_s
end
end
end

View file

@ -0,0 +1,373 @@
module Api
class NotesController < ApplicationController
layout "site", :only => [:mine]
skip_before_action :verify_authenticity_token
before_action :check_api_readable
before_action :setup_user_auth, :only => [:create, :comment, :show]
before_action :authorize, :only => [:close, :reopen, :destroy]
before_action :api_deny_access_handler
authorize_resource
before_action :check_api_writable, :only => [:create, :comment, :close, :reopen, :destroy]
before_action :set_locale
around_action :api_call_handle_error, :api_call_timeout
##
# Return a list of notes in a given area
def index
# Figure out the bbox - we prefer a bbox argument but also
# support the old, deprecated, method with four arguments
if params[:bbox]
bbox = BoundingBox.from_bbox_params(params)
else
raise OSM::APIBadUserInput, "No l was given" unless params[:l]
raise OSM::APIBadUserInput, "No r was given" unless params[:r]
raise OSM::APIBadUserInput, "No b was given" unless params[:b]
raise OSM::APIBadUserInput, "No t was given" unless params[:t]
bbox = BoundingBox.from_lrbt_params(params)
end
# Get any conditions that need to be applied
notes = closed_condition(Note.all)
# Check that the boundaries are valid
bbox.check_boundaries
# Check the the bounding box is not too big
bbox.check_size(MAX_NOTE_REQUEST_AREA)
# Find the notes we want to return
@notes = notes.bbox(bbox).order("updated_at DESC").limit(result_limit).preload(:comments)
# Render the result
respond_to do |format|
format.rss
format.xml
format.json
format.gpx
end
end
##
# Create a new note
def create
# Check the ACLs
raise OSM::APIAccessDenied if current_user.nil? && Acl.no_note_comment(request.remote_ip)
# Check the arguments are sane
raise OSM::APIBadUserInput, "No lat was given" unless params[:lat]
raise OSM::APIBadUserInput, "No lon was given" unless params[:lon]
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
# Extract the arguments
lon = OSM.parse_float(params[:lon], OSM::APIBadUserInput, "lon was not a number")
lat = OSM.parse_float(params[:lat], OSM::APIBadUserInput, "lat was not a number")
comment = params[:text]
# Include in a transaction to ensure that there is always a note_comment for every note
Note.transaction do
# Create the note
@note = Note.create(:lat => lat, :lon => lon)
raise OSM::APIBadUserInput, "The note is outside this world" unless @note.in_world?
# Save the note
@note.save!
# Add a comment to the note
add_comment(@note, comment, "opened")
end
# Return a copy of the new note
respond_to do |format|
format.xml { render :action => :show }
format.json { render :action => :show }
end
end
##
# Add a comment to an existing note
def comment
# Check the ACLs
raise OSM::APIAccessDenied if current_user.nil? && Acl.no_note_comment(request.remote_ip)
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
# Extract the arguments
id = params[:id].to_i
comment = params[:text]
# Find the note and check it is valid
@note = Note.find(id)
raise OSM::APINotFoundError unless @note
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
# Add a comment to the note
Note.transaction do
add_comment(@note, comment, "commented")
end
# Return a copy of the updated note
respond_to do |format|
format.xml { render :action => :show }
format.json { render :action => :show }
end
end
##
# Close a note
def close
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
comment = params[:text]
# Find the note and check it is valid
@note = Note.find_by(:id => id)
raise OSM::APINotFoundError unless @note
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
# Close the note and add a comment
Note.transaction do
@note.close
add_comment(@note, comment, "closed")
end
# Return a copy of the updated note
respond_to do |format|
format.xml { render :action => :show }
format.json { render :action => :show }
end
end
##
# Reopen a note
def reopen
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
comment = params[:text]
# Find the note and check it is valid
@note = Note.find_by(:id => id)
raise OSM::APINotFoundError unless @note
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user.moderator?
raise OSM::APINoteAlreadyOpenError, @note unless @note.closed? || !@note.visible?
# Reopen the note and add a comment
Note.transaction do
@note.reopen
add_comment(@note, comment, "reopened")
end
# Return a copy of the updated note
respond_to do |format|
format.xml { render :action => :show }
format.json { render :action => :show }
end
end
##
# Get a feed of recent notes and comments
def feed
# Get any conditions that need to be applied
notes = closed_condition(Note.all)
# Process any bbox
if params[:bbox]
bbox = BoundingBox.from_bbox_params(params)
bbox.check_boundaries
bbox.check_size(MAX_NOTE_REQUEST_AREA)
notes = notes.bbox(bbox)
end
# Find the comments we want to return
@comments = NoteComment.where(:note_id => notes).order("created_at DESC").limit(result_limit).preload(:note)
# Render the result
respond_to do |format|
format.rss
end
end
##
# Read a note
def show
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Find the note and check it is valid
@note = Note.find(params[:id])
raise OSM::APINotFoundError unless @note
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user&.moderator?
# Render the result
respond_to do |format|
format.xml
format.rss
format.json
format.gpx
end
end
##
# Delete (hide) a note
def destroy
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
comment = params[:text]
# Find the note and check it is valid
@note = Note.find(id)
raise OSM::APINotFoundError unless @note
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
# Mark the note as hidden
Note.transaction do
@note.status = "hidden"
@note.save
add_comment(@note, comment, "hidden", false)
end
# Return a copy of the updated note
respond_to do |format|
format.xml { render :action => :show }
format.json { render :action => :show }
end
end
##
# Return a list of notes matching a given string
def search
# Get the initial set of notes
@notes = closed_condition(Note.all)
# Add any user filter
if params[:display_name] || params[:user]
if params[:display_name]
@user = User.find_by(:display_name => params[:display_name])
raise OSM::APIBadUserInput, "User #{params[:display_name]} not known" unless @user
else
@user = User.find_by(:id => params[:user])
raise OSM::APIBadUserInput, "User #{params[:user]} not known" unless @user
end
@notes = @notes.joins(:comments).where(:note_comments => { :author_id => @user })
end
# Add any text filter
@notes = @notes.joins(:comments).where("to_tsvector('english', note_comments.body) @@ plainto_tsquery('english', ?)", params[:q]) if params[:q]
# Add any date filter
if params[:from]
begin
from = Time.parse(params[:from])
rescue ArgumentError
raise OSM::APIBadUserInput, "Date #{params[:from]} is in a wrong format"
end
begin
to = if params[:to]
Time.parse(params[:to])
else
Time.now
end
rescue ArgumentError
raise OSM::APIBadUserInput, "Date #{params[:to]} is in a wrong format"
end
@notes = @notes.where(:created_at => from..to)
end
# Find the notes we want to return
@notes = @notes.order("updated_at DESC").limit(result_limit).preload(:comments)
# Render the result
respond_to do |format|
format.rss { render :action => :index }
format.xml { render :action => :index }
format.json { render :action => :index }
format.gpx { render :action => :index }
end
end
private
#------------------------------------------------------------
# utility functions below.
#------------------------------------------------------------
##
# Get the maximum number of results to return
def result_limit
if params[:limit]
if params[:limit].to_i.positive? && params[:limit].to_i <= 10000
params[:limit].to_i
else
raise OSM::APIBadUserInput, "Note limit must be between 1 and 10000"
end
else
100
end
end
##
# Generate a condition to choose which notes we want based
# on their status and the user's request parameters
def closed_condition(notes)
closed_since = if params[:closed]
params[:closed].to_i
else
7
end
if closed_since.negative?
notes.where.not(:status => "hidden")
elsif closed_since.positive?
notes.where(:status => "open")
.or(notes.where(:status => "closed")
.where(notes.arel_table[:closed_at].gt(Time.now - closed_since.days)))
else
notes.where(:status => "open")
end
end
##
# Add a comment to a note
def add_comment(note, text, event, notify = true)
attributes = { :visible => true, :event => event, :body => text }
if current_user
attributes[:author_id] = current_user.id
else
attributes[:author_ip] = request.remote_ip
end
comment = note.comments.create!(attributes)
note.comments.map(&:author).uniq.each do |user|
Notifier.note_comment_notification(comment, user).deliver_later if notify && user && user != current_user && user.visible?
end
end
end
end

View file

@ -0,0 +1,79 @@
# this class pulls together the logic for all the old_* controllers
# into one place. as it turns out, the API methods for historical
# nodes, ways and relations are basically identical.
module Api
class OldController < ApplicationController
require "xml/libxml"
skip_before_action :verify_authenticity_token
before_action :setup_user_auth, :only => [:history, :version]
before_action :api_deny_access_handler
before_action :authorize, :only => [:redact]
authorize_resource
before_action :check_api_readable
before_action :check_api_writable, :only => [:redact]
around_action :api_call_handle_error, :api_call_timeout
before_action :lookup_old_element, :except => [:history]
before_action :lookup_old_element_versions, :only => [:history]
def history
# the .where() method used in the lookup_old_element_versions
# call won't throw an error if no records are found, so we have
# to do that ourselves.
raise OSM::APINotFoundError if @elements.empty?
doc = OSM::API.new.get_xml_doc
visible_elements = if show_redactions?
@elements
else
@elements.unredacted
end
visible_elements.each do |element|
doc.root << element.to_xml_node
end
render :xml => doc.to_s
end
def version
if @old_element.redacted? && !show_redactions?
head :forbidden
else
response.last_modified = @old_element.timestamp
doc = OSM::API.new.get_xml_doc
doc.root << @old_element.to_xml_node
render :xml => doc.to_s
end
end
def redact
redaction_id = params["redaction"]
if redaction_id.nil?
# if no redaction ID was provided, then this is an unredact
# operation.
@old_element.redact!(nil)
else
# if a redaction ID was specified, then set this element to
# be redacted in that redaction.
redaction = Redaction.find(redaction_id.to_i)
@old_element.redact!(redaction)
end
# just return an empty 200 OK for success
head :ok
end
private
def show_redactions?
current_user&.moderator? && params[:show_redactions] == "true"
end
end
end

View file

@ -0,0 +1,13 @@
module Api
class OldNodesController < OldController
private
def lookup_old_element
@old_element = OldNode.find([params[:id], params[:version]])
end
def lookup_old_element_versions
@elements = OldNode.where(:node_id => params[:id]).order(:version)
end
end
end

View file

@ -0,0 +1,13 @@
module Api
class OldRelationsController < OldController
private
def lookup_old_element
@old_element = OldRelation.find([params[:id], params[:version]])
end
def lookup_old_element_versions
@elements = OldRelation.where(:relation_id => params[:id]).order(:version)
end
end
end

View file

@ -0,0 +1,13 @@
module Api
class OldWaysController < OldController
private
def lookup_old_element
@old_element = OldWay.find([params[:id], params[:version]])
end
def lookup_old_element_versions
@elements = OldWay.where(:way_id => params[:id]).order(:version)
end
end
end

View file

@ -0,0 +1,169 @@
module Api
class RelationsController < ApplicationController
require "xml/libxml"
skip_before_action :verify_authenticity_token
before_action :authorize, :only => [:create, :update, :delete]
before_action :api_deny_access_handler
authorize_resource
before_action :require_public_data, :only => [:create, :update, :delete]
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
around_action :api_call_handle_error, :api_call_timeout
def create
assert_method :put
relation = Relation.from_xml(request.raw_post, true)
# Assume that Relation.from_xml has thrown an exception if there is an error parsing the xml
relation.create_with_history current_user
render :plain => relation.id.to_s
end
def show
relation = Relation.find(params[:id])
response.last_modified = relation.timestamp
if relation.visible
render :xml => relation.to_xml.to_s
else
head :gone
end
end
def update
logger.debug request.raw_post
relation = Relation.find(params[:id])
new_relation = Relation.from_xml(request.raw_post)
raise OSM::APIBadUserInput, "The id in the url (#{relation.id}) is not the same as provided in the xml (#{new_relation.id})" unless new_relation && new_relation.id == relation.id
relation.update_from new_relation, current_user
render :plain => relation.version.to_s
end
def delete
relation = Relation.find(params[:id])
new_relation = Relation.from_xml(request.raw_post)
if new_relation && new_relation.id == relation.id
relation.delete_with_history!(new_relation, current_user)
render :plain => relation.version.to_s
else
head :bad_request
end
end
# -----------------------------------------------------------------
# full
#
# input parameters: id
#
# returns XML representation of one relation object plus all its
# members, plus all nodes part of member ways
# -----------------------------------------------------------------
def full
relation = Relation.find(params[:id])
if relation.visible
# first find the ids of nodes, ways and relations referenced by this
# relation - note that we exclude this relation just in case.
node_ids = relation.members.select { |m| m[0] == "Node" }.map { |m| m[1] }
way_ids = relation.members.select { |m| m[0] == "Way" }.map { |m| m[1] }
relation_ids = relation.members.select { |m| m[0] == "Relation" && m[1] != relation.id }.map { |m| m[1] }
# next load the relations and the ways.
relations = Relation.where(:id => relation_ids).includes(:relation_tags)
ways = Way.where(:id => way_ids).includes(:way_nodes, :way_tags)
# now additionally collect nodes referenced by ways. Note how we
# recursively evaluate ways but NOT relations.
way_node_ids = ways.collect do |way|
way.way_nodes.collect(&:node_id)
end
node_ids += way_node_ids.flatten
nodes = Node.where(:id => node_ids.uniq).includes(:node_tags)
# create XML.
doc = OSM::API.new.get_xml_doc
visible_nodes = {}
changeset_cache = {}
user_display_name_cache = {}
nodes.each do |node|
next unless node.visible? # should be unnecessary if data is consistent.
doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
visible_nodes[node.id] = node
end
ways.each do |way|
next unless way.visible? # should be unnecessary if data is consistent.
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
end
relations.each do |rel|
next unless rel.visible? # should be unnecessary if data is consistent.
doc.root << rel.to_xml_node(changeset_cache, user_display_name_cache)
end
# finally add self and output
doc.root << relation.to_xml_node(changeset_cache, user_display_name_cache)
render :xml => doc.to_s
else
head :gone
end
end
def index
raise OSM::APIBadUserInput, "The parameter relations is required, and must be of the form relations=id[,id[,id...]]" unless params["relations"]
ids = params["relations"].split(",").collect(&:to_i)
raise OSM::APIBadUserInput, "No relations were given to search for" if ids.empty?
doc = OSM::API.new.get_xml_doc
Relation.find(ids).each do |relation|
doc.root << relation.to_xml_node
end
render :xml => doc.to_s
end
def relations_for_way
relations_for_object("Way")
end
def relations_for_node
relations_for_object("Node")
end
def relations_for_relation
relations_for_object("Relation")
end
private
def relations_for_object(objtype)
relationids = RelationMember.where(:member_type => objtype, :member_id => params[:id]).collect(&:relation_id).uniq
doc = OSM::API.new.get_xml_doc
Relation.find(relationids).each do |relation|
doc.root << relation.to_xml_node if relation.visible
end
render :xml => doc.to_s
end
end
end

View file

@ -0,0 +1,102 @@
module Api
class SearchController < ApplicationController
# Support searching for nodes, ways, or all
# Can search by tag k, v, or both (type->k,value->v)
# Can search by name (k=name,v=....)
skip_before_action :verify_authenticity_token
authorize_resource :class => false
def search_all
do_search(true, true, true)
end
def search_ways
do_search(true, false, false)
end
def search_nodes
do_search(false, true, false)
end
def search_relations
do_search(false, false, true)
end
def do_search(do_ways, do_nodes, do_relations)
type = params["type"]
value = params["value"]
unless type || value
name = params["name"]
if name
type = "name"
value = name
end
end
if do_nodes
response.headers["Error"] = "Searching of nodes is currently unavailable"
head :service_unavailable
return false
end
unless value
response.headers["Error"] = "Searching for a key without value is currently unavailable"
head :service_unavailable
return false
end
# Matching for node tags table
if do_nodes
nodes = Node.joins(:node_tags)
nodes = nodes.where(:current_node_tags => { :k => type }) if type
nodes = nodes.where(:current_node_tags => { :v => value }) if value
nodes = nodes.limit(100)
else
nodes = []
end
# Matching for way tags table
if do_ways
ways = Way.joins(:way_tags)
ways = ways.where(:current_way_tags => { :k => type }) if type
ways = ways.where(:current_way_tags => { :v => value }) if value
ways = ways.limit(100)
else
ways = []
end
# Matching for relation tags table
if do_relations
relations = Relation.joins(:relation_tags)
relations = relations.where(:current_relation_tags => { :k => type }) if type
relations = relations.where(:current_relation_tags => { :v => value }) if value
relations = relations.limit(2000)
else
relations = []
end
# Fetch any node needed for our ways (only have matching nodes so far)
nodes += Node.find(ways.collect(&:nds).uniq)
# Print
visible_nodes = {}
changeset_cache = {}
user_display_name_cache = {}
doc = OSM::API.new.get_xml_doc
nodes.each do |node|
doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
visible_nodes[node.id] = node
end
ways.each do |way|
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
end
relations.each do |rel|
doc.root << rel.to_xml_node(changeset_cache, user_display_name_cache)
end
render :xml => doc.to_s
end
end
end

View file

@ -0,0 +1,219 @@
module Api
class SwfController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :check_api_readable
authorize_resource :class => false
# to log:
# RAILS_DEFAULT_LOGGER.error("Args: #{args[0]}, #{args[1]}, #{args[2]}, #{args[3]}")
# $log.puts Time.new.to_s+','+Time.new.usec.to_s+": started GPS script"
# http://localhost:3000/api/0.4/swf/trackpoints?xmin=-2.32402605810577&xmax=-2.18386309423859&ymin=52.1546608755772&ymax=52.2272777906895&baselong=-2.25325793066437&basey=61.3948537948532&masterscale=5825.4222222222
# ====================================================================
# Public methods
# ---- trackpoints compile SWF of trackpoints
def trackpoints
# - Initialise
baselong = params["baselong"].to_f
basey = params["basey"].to_f
masterscale = params["masterscale"].to_f
bbox = BoundingBox.new(params["xmin"], params["ymin"],
params["xmax"], params["ymax"])
start = params["start"].to_i
# - Begin movie
bounds_left = 0
bounds_right = 320 * 20
bounds_bottom = 0
bounds_top = 240 * 20
m = ""
m += swf_record(9, 255.chr + 155.chr + 155.chr) # Background
absx = 0
absy = 0
xl = yb = 9999999
xr = yt = -9999999
# - Send SQL for GPS tracks
b = ""
lasttime = 0
lasttrack = lastfile = "-1"
if params["token"]
user = User.authenticate(:token => params[:token])
sql = "SELECT gps_points.latitude*0.0000001 AS lat,gps_points.longitude*0.0000001 AS lon,gpx_files.id AS fileid," + " EXTRACT(EPOCH FROM gps_points.timestamp) AS ts, gps_points.trackid AS trackid " + " FROM gpx_files,gps_points " + "WHERE gpx_files.id=gpx_id " + " AND gpx_files.user_id=#{user.id} " + " AND " + OSM.sql_for_area(bbox, "gps_points.") + " AND (gps_points.timestamp IS NOT NULL) " + "ORDER BY fileid DESC,ts " + "LIMIT 10000 OFFSET #{start}"
else
sql = "SELECT latitude*0.0000001 AS lat,longitude*0.0000001 AS lon,gpx_id AS fileid," + " EXTRACT(EPOCH FROM timestamp) AS ts, gps_points.trackid AS trackid " + " FROM gps_points " + "WHERE " + OSM.sql_for_area(bbox, "gps_points.") + " AND (gps_points.timestamp IS NOT NULL) " + "ORDER BY fileid DESC,ts " + "LIMIT 10000 OFFSET #{start}"
end
gpslist = ActiveRecord::Base.connection.select_all sql
# - Draw GPS trace lines
r = start_shape
gpslist.each do |row|
xs = (long2coord(row["lon"].to_f, baselong, masterscale) * 20).floor
ys = (lat2coord(row["lat"].to_f, basey, masterscale) * 20).floor
xl = [xs, xl].min
xr = [xs, xr].max
yb = [ys, yb].min
yt = [ys, yt].max
if row["ts"].to_i - lasttime > 180 || row["fileid"] != lastfile || row["trackid"] != lasttrack # or row['ts'].to_i==lasttime
b += start_and_move(xs, ys, "01")
absx = xs.floor
absy = ys.floor
end
b += draw_to(absx, absy, xs, ys)
absx = xs.floor
absy = ys.floor
lasttime = row["ts"].to_i
lastfile = row["fileid"]
lasttrack = row["trackid"]
r += [b.slice!(0...80)].pack("B*") while b.length > 80
end
# (Unwayed segments removed)
# - Write shape
b += end_shape
r += [b].pack("B*")
m += swf_record(2, pack_u16(1) + pack_rect(xl, xr, yb, yt) + r)
m += swf_record(4, pack_u16(1) + pack_u16(1))
# - Create Flash header and write to browser
m += swf_record(1, "") # Show frame
m += swf_record(0, "") # End
m = pack_rect(bounds_left, bounds_right, bounds_bottom, bounds_top) + 0.chr + 12.chr + pack_u16(1) + m
m = "FWS" + 6.chr + pack_u32(m.length + 8) + m
render :body => m, :content_type => "application/x-shockwave-flash"
end
private
# =======================================================================
# SWF functions
# -----------------------------------------------------------------------
# Line-drawing
def start_shape
s = 0.chr # No fill styles
s += 2.chr # Two line styles
s += pack_u16(0) + 0.chr + 255.chr + 255.chr # Width 5, RGB #00FFFF
s += pack_u16(0) + 255.chr + 0.chr + 255.chr # Width 5, RGB #FF00FF
s += 34.chr # 2 fill, 2 line index bits
s
end
def end_shape
"000000"
end
def start_and_move(x, y, col)
d = "001001" # Line style change, moveTo
l = [length_sb(x), length_sb(y)].max
d += format("%05b%0*b%0*b", l, l, x, l, y)
d += col # Select line style
d
end
def draw_to(absx, absy, x, y)
dx = x - absx
dy = y - absy
# Split the line up if there's anything>16383, because
# that would overflow the 4 bits allowed for length
mstep = [dx.abs / 16383, dy.abs / 16383, 1].max.ceil
xstep = dx / mstep
ystep = dy / mstep
d = ""
1.upto(mstep).each do
d += draw_section(x, y, x + xstep, y + ystep)
x += xstep
y += ystep
end
d
end
def draw_section(x1, y1, x2, y2)
d = "11" # TypeFlag, EdgeFlag
dx = x2 - x1
dy = y2 - y1
l = [length_sb(dx), length_sb(dy)].max
d += format("%04b", l - 2)
d += "1" # GeneralLine
d += format("%0*b%0*b", l, dx, l, dy)
d
end
# -----------------------------------------------------------------------
# Specific data types
# SWF data block type
def swf_record(id, r)
if r.length > 62
# Long header: tag id, 0x3F, length
pack_u16((id << 6) + 0x3F) + pack_u32(r.length) + r
else
# Short header: tag id, length
pack_u16((id << 6) + r.length) + r
end
end
# SWF RECT type
def pack_rect(a, b, c, d)
l = [length_sb(a),
length_sb(b),
length_sb(c),
length_sb(d)].max
# create binary string (00111001 etc.) - 5-byte length, then bbox
n = format("%05b%0*b%0*b%0*b%0*b", l, l, a, l, b, l, c, l, d)
# pack into byte string
[n].pack("B*")
end
# -----------------------------------------------------------------------
# Generic pack functions
def pack_u16(n)
[n.floor].pack("v")
end
def pack_u32(n)
[n.floor].pack("V")
end
# Find number of bits required to store arbitrary-length binary
def length_sb(n)
Math.frexp(n + (n.zero? ? 1 : 0))[1] + 1
end
# ====================================================================
# Co-ordinate conversion
# (this is duplicated from amf_controller, should probably share)
def lat2coord(a, basey, masterscale)
-(lat2y(a) - basey) * masterscale
end
def long2coord(a, baselong, masterscale)
(a - baselong) * masterscale
end
def lat2y(a)
180 / Math::PI * Math.log(Math.tan(Math::PI / 4 + a * (Math::PI / 180) / 2))
end
end
end

View file

@ -0,0 +1,169 @@
module Api
class TracesController < ApplicationController
layout "site", :except => :georss
skip_before_action :verify_authenticity_token
before_action :authorize_web
before_action :set_locale
before_action :authorize
before_action :api_deny_access_handler
authorize_resource
before_action :check_database_readable, :except => [:api_read, :api_data]
before_action :check_database_writable, :only => [:api_create, :api_update, :api_delete]
before_action :check_api_readable, :only => [:api_read, :api_data]
before_action :check_api_writable, :only => [:api_create, :api_update, :api_delete]
before_action :offline_redirect, :only => [:api_create, :api_delete, :api_data]
around_action :api_call_handle_error
def api_read
trace = Trace.visible.find(params[:id])
if trace.public? || trace.user == current_user
render :xml => trace.to_xml.to_s
else
head :forbidden
end
end
def api_update
trace = Trace.visible.find(params[:id])
if trace.user == current_user
trace.update_from_xml(request.raw_post)
trace.save!
head :ok
else
head :forbidden
end
end
def api_delete
trace = Trace.visible.find(params[:id])
if trace.user == current_user
trace.visible = false
trace.save!
head :ok
else
head :forbidden
end
end
def api_data
trace = Trace.visible.find(params[:id])
if trace.public? || trace.user == current_user
if request.format == Mime[:xml]
send_data(trace.xml_file.read, :filename => "#{trace.id}.xml", :type => request.format.to_s, :disposition => "attachment")
elsif request.format == Mime[:gpx]
send_data(trace.xml_file.read, :filename => "#{trace.id}.gpx", :type => request.format.to_s, :disposition => "attachment")
else
send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => "attachment")
end
else
head :forbidden
end
end
def api_create
tags = params[:tags] || ""
description = params[:description] || ""
visibility = params[:visibility]
if visibility.nil?
visibility = if params[:public]&.to_i&.nonzero?
"public"
else
"private"
end
end
if params[:file].respond_to?(:read)
trace = do_create(params[:file], tags, description, visibility)
if trace.id
render :plain => trace.id.to_s
elsif trace.valid?
head :internal_server_error
else
head :bad_request
end
else
head :bad_request
end
end
private
def do_create(file, tags, description, visibility)
# Sanitise the user's filename
name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, "_")
# Get a temporary filename...
filename = "/tmp/#{rand}"
# ...and save the uploaded file to that location
File.open(filename, "wb") { |f| f.write(file.read) }
# Create the trace object, falsely marked as already
# inserted to stop the import daemon trying to load it
trace = Trace.new(
:name => name,
:tagstring => tags,
:description => description,
:visibility => visibility,
:inserted => true,
:user => current_user,
:timestamp => Time.now.getutc
)
if trace.valid?
Trace.transaction do
begin
# Save the trace object
trace.save!
# Rename the temporary file to the final name
FileUtils.mv(filename, trace.trace_name)
rescue StandardError
# Remove the file as we have failed to update the database
FileUtils.rm_f(filename)
# Pass the exception on
raise
end
begin
# Clear the inserted flag to make the import daemon load the trace
trace.inserted = false
trace.save!
rescue StandardError
# Remove the file as we have failed to update the database
FileUtils.rm_f(trace.trace_name)
# Pass the exception on
raise
end
end
end
# Finally save the user's preferred privacy level
if pref = current_user.preferences.where(:k => "gps.trace.visibility").first
pref.v = visibility
pref.save
else
current_user.preferences.create(:k => "gps.trace.visibility", :v => visibility)
end
trace
end
def offline_redirect
redirect_to :action => :offline if STATUS == :gpx_offline
end
end
end

View file

@ -0,0 +1,90 @@
# Update and read user preferences, which are arbitrayr key/val pairs
module Api
class UserPreferencesController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authorize
authorize_resource
around_action :api_call_handle_error
##
# return all the preferences as an XML document
def read
doc = OSM::API.new.get_xml_doc
prefs = current_user.preferences
el1 = XML::Node.new "preferences"
prefs.each do |pref|
el1 << pref.to_xml_node
end
doc.root << el1
render :xml => doc.to_s
end
##
# return the value for a single preference
def read_one
pref = UserPreference.find([current_user.id, params[:preference_key]])
render :plain => pref.v.to_s
end
# update the entire set of preferences
def update
old_preferences = current_user.preferences.each_with_object({}) do |preference, preferences|
preferences[preference.k] = preference
end
new_preferences = {}
doc = XML::Parser.string(request.raw_post, :options => XML::Parser::Options::NOERROR).parse
doc.find("//preferences/preference").each do |pt|
if preference = old_preferences.delete(pt["k"])
preference.v = pt["v"]
elsif new_preferences.include?(pt["k"])
raise OSM::APIDuplicatePreferenceError, pt["k"]
else
preference = current_user.preferences.build(:k => pt["k"], :v => pt["v"])
end
new_preferences[preference.k] = preference
end
old_preferences.each_value(&:delete)
new_preferences.each_value(&:save!)
render :plain => ""
end
##
# update the value of a single preference
def update_one
begin
pref = UserPreference.find([current_user.id, params[:preference_key]])
rescue ActiveRecord::RecordNotFound
pref = UserPreference.new
pref.user = current_user
pref.k = params[:preference_key]
end
pref.v = request.raw_post.chomp
pref.save!
render :plain => ""
end
##
# delete a single preference
def delete_one
UserPreference.find([current_user.id, params[:preference_key]]).delete
render :plain => ""
end
end
end

View file

@ -0,0 +1,66 @@
module Api
class UsersController < ApplicationController
layout "site", :except => [:api_details]
skip_before_action :verify_authenticity_token
before_action :disable_terms_redirect, :only => [:api_details]
before_action :authorize, :only => [:api_details, :api_gpx_files]
before_action :api_deny_access_handler
authorize_resource
before_action :check_api_readable
around_action :api_call_handle_error
before_action :lookup_user_by_id, :only => [:api_read]
def api_read
if @user.visible?
render :action => :api_read, :content_type => "text/xml"
else
head :gone
end
end
def api_details
@user = current_user
render :action => :api_read, :content_type => "text/xml"
end
def api_users
raise OSM::APIBadUserInput, "The parameter users is required, and must be of the form users=id[,id[,id...]]" unless params["users"]
ids = params["users"].split(",").collect(&:to_i)
raise OSM::APIBadUserInput, "No users were given to search for" if ids.empty?
@users = User.visible.find(ids)
render :action => :api_users, :content_type => "text/xml"
end
def api_gpx_files
doc = OSM::API.new.get_xml_doc
current_user.traces.reload.each do |trace|
doc.root << trace.to_xml_node
end
render :xml => doc.to_s
end
private
##
# ensure that there is a "user" instance variable
def lookup_user_by_id
@user = User.find(params[:id])
end
##
#
def disable_terms_redirect
# this is necessary otherwise going to the user terms page, when
# having not agreed already would cause an infinite redirect loop.
# it's .now so that this doesn't propagate to other pages.
flash.now[:skip_terms] = true
end
end
end

View file

@ -0,0 +1,120 @@
module Api
class WaysController < ApplicationController
require "xml/libxml"
skip_before_action :verify_authenticity_token
before_action :authorize, :only => [:create, :update, :delete]
before_action :api_deny_access_handler
authorize_resource
before_action :require_public_data, :only => [:create, :update, :delete]
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
around_action :api_call_handle_error, :api_call_timeout
def create
assert_method :put
way = Way.from_xml(request.raw_post, true)
# Assume that Way.from_xml has thrown an exception if there is an error parsing the xml
way.create_with_history current_user
render :plain => way.id.to_s
end
def show
way = Way.find(params[:id])
response.last_modified = way.timestamp
if way.visible
render :xml => way.to_xml.to_s
else
head :gone
end
end
def update
way = Way.find(params[:id])
new_way = Way.from_xml(request.raw_post)
unless new_way && new_way.id == way.id
raise OSM::APIBadUserInput, "The id in the url (#{way.id}) is not the same as provided in the xml (#{new_way.id})"
end
way.update_from(new_way, current_user)
render :plain => way.version.to_s
end
# This is the API call to delete a way
def delete
way = Way.find(params[:id])
new_way = Way.from_xml(request.raw_post)
if new_way && new_way.id == way.id
way.delete_with_history!(new_way, current_user)
render :plain => way.version.to_s
else
head :bad_request
end
end
def full
way = Way.includes(:nodes => :node_tags).find(params[:id])
if way.visible
visible_nodes = {}
changeset_cache = {}
user_display_name_cache = {}
doc = OSM::API.new.get_xml_doc
way.nodes.uniq.each do |node|
if node.visible
doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
visible_nodes[node.id] = node
end
end
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
render :xml => doc.to_s
else
head :gone
end
end
def index
unless params["ways"]
raise OSM::APIBadUserInput, "The parameter ways is required, and must be of the form ways=id[,id[,id...]]"
end
ids = params["ways"].split(",").collect(&:to_i)
raise OSM::APIBadUserInput, "No ways were given to search for" if ids.empty?
doc = OSM::API.new.get_xml_doc
Way.find(ids).each do |way|
doc.root << way.to_xml_node
end
render :xml => doc.to_s
end
##
# returns all the ways which are currently using the node given in the
# :id parameter. note that this used to return deleted ways as well, but
# this seemed not to be the expected behaviour, so it was removed.
def ways_for_node
wayids = WayNode.where(:node_id => params[:id]).collect { |ws| ws.id[0] }.uniq
doc = OSM::API.new.get_xml_doc
Way.find(wayids).each do |way|
doc.root << way.to_xml_node if way.visible
end
render :xml => doc.to_s
end
end
end

View file

@ -1,89 +1,11 @@
class ChangesetCommentsController < ApplicationController
skip_before_action :verify_authenticity_token, :except => [:index]
before_action :authorize_web, :only => [:index]
before_action :set_locale, :only => [:index]
before_action :authorize, :only => [:create, :destroy, :restore]
before_action :api_deny_access_handler, :only => [:create, :destroy, :restore]
before_action :authorize_web
before_action :set_locale
authorize_resource
before_action :require_public_data, :only => [:create]
before_action :check_api_writable, :only => [:create, :destroy, :restore]
before_action :check_api_readable, :except => [:create, :index]
before_action(:only => [:index]) { |c| c.check_database_readable(true) }
around_action :api_call_handle_error, :except => [:index]
around_action :api_call_timeout, :except => [:index]
around_action :web_timeout, :only => [:index]
##
# Add a comment to a changeset
def create
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
# Extract the arguments
id = params[:id].to_i
body = params[:text]
# Find the changeset and check it is valid
changeset = Changeset.find(id)
raise OSM::APIChangesetNotYetClosedError, changeset if changeset.is_open?
# Add a comment to the changeset
comment = changeset.comments.create(:changeset => changeset,
:body => body,
:author => current_user)
# Notify current subscribers of the new comment
changeset.subscribers.visible.each do |user|
Notifier.changeset_comment_notification(comment, user).deliver_later if current_user != user
end
# Add the commenter to the subscribers if necessary
changeset.subscribers << current_user unless changeset.subscribers.exists?(current_user.id)
# Return a copy of the updated changeset
render :xml => changeset.to_xml.to_s
end
##
# Sets visible flag on comment to false
def destroy
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
# Find the changeset
comment = ChangesetComment.find(id)
# Hide the comment
comment.update(:visible => false)
# Return a copy of the updated changeset
render :xml => comment.changeset.to_xml.to_s
end
##
# Sets visible flag on comment to true
def restore
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
# Find the changeset
comment = ChangesetComment.find(id)
# Unhide the comment
comment.update(:visible => true)
# Return a copy of the updated changeset
render :xml => comment.changeset.to_xml.to_s
end
around_action :web_timeout
##
# Get a feed of recent changeset comments

View file

@ -5,256 +5,17 @@ class ChangesetsController < ApplicationController
require "xml/libxml"
skip_before_action :verify_authenticity_token, :except => [:index]
before_action :authorize_web, :only => [:index, :feed]
before_action :set_locale, :only => [:index, :feed]
before_action :authorize, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe]
before_action :api_deny_access_handler, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe, :expand_bbox]
before_action :authorize_web
before_action :set_locale
authorize_resource
before_action :require_public_data, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe]
before_action :check_api_writable, :only => [:create, :update, :upload, :subscribe, :unsubscribe]
before_action :check_api_readable, :except => [:create, :update, :upload, :download, :query, :index, :feed, :subscribe, :unsubscribe]
before_action(:only => [:index, :feed]) { |c| c.check_database_readable(true) }
around_action :api_call_handle_error, :except => [:index, :feed]
around_action :api_call_timeout, :except => [:index, :feed, :upload]
around_action :web_timeout, :only => [:index, :feed]
around_action :web_timeout
# Helper methods for checking consistency
include ConsistencyValidations
# Create a changeset from XML.
def create
assert_method :put
cs = Changeset.from_xml(request.raw_post, true)
# Assume that Changeset.from_xml has thrown an exception if there is an error parsing the xml
cs.user = current_user
cs.save_with_tags!
# Subscribe user to changeset comments
cs.subscribers << current_user
render :plain => cs.id.to_s
end
##
# Return XML giving the basic info about the changeset. Does not
# return anything about the nodes, ways and relations in the changeset.
def show
changeset = Changeset.find(params[:id])
render :xml => changeset.to_xml(params[:include_discussion].presence).to_s
end
##
# marks a changeset as closed. this may be called multiple times
# on the same changeset, so is idempotent.
def close
assert_method :put
changeset = Changeset.find(params[:id])
check_changeset_consistency(changeset, current_user)
# to close the changeset, we'll just set its closed_at time to
# now. this might not be enough if there are concurrency issues,
# but we'll have to wait and see.
changeset.set_closed_time_now
changeset.save!
head :ok
end
##
# insert a (set of) points into a changeset bounding box. this can only
# increase the size of the bounding box. this is a hint that clients can
# set either before uploading a large number of changes, or changes that
# the client (but not the server) knows will affect areas further away.
def expand_bbox
# only allow POST requests, because although this method is
# idempotent, there is no "document" to PUT really...
assert_method :post
cs = Changeset.find(params[:id])
check_changeset_consistency(cs, current_user)
# keep an array of lons and lats
lon = []
lat = []
# the request is in pseudo-osm format... this is kind-of an
# abuse, maybe should change to some other format?
doc = XML::Parser.string(request.raw_post, :options => XML::Parser::Options::NOERROR).parse
doc.find("//osm/node").each do |n|
lon << n["lon"].to_f * GeoRecord::SCALE
lat << n["lat"].to_f * GeoRecord::SCALE
end
# add the existing bounding box to the lon-lat array
lon << cs.min_lon unless cs.min_lon.nil?
lat << cs.min_lat unless cs.min_lat.nil?
lon << cs.max_lon unless cs.max_lon.nil?
lat << cs.max_lat unless cs.max_lat.nil?
# collapse the arrays to minimum and maximum
cs.min_lon = lon.min
cs.min_lat = lat.min
cs.max_lon = lon.max
cs.max_lat = lat.max
# save the larger bounding box and return the changeset, which
# will include the bigger bounding box.
cs.save!
render :xml => cs.to_xml.to_s
end
##
# Upload a diff in a single transaction.
#
# This means that each change within the diff must succeed, i.e: that
# each version number mentioned is still current. Otherwise the entire
# transaction *must* be rolled back.
#
# Furthermore, each element in the diff can only reference the current
# changeset.
#
# Returns: a diffResult document, as described in
# http://wiki.openstreetmap.org/wiki/OSM_Protocol_Version_0.6
def upload
# only allow POST requests, as the upload method is most definitely
# not idempotent, as several uploads with placeholder IDs will have
# different side-effects.
# see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2
assert_method :post
changeset = Changeset.find(params[:id])
check_changeset_consistency(changeset, current_user)
diff_reader = DiffReader.new(request.raw_post, changeset)
Changeset.transaction do
result = diff_reader.commit
render :xml => result.to_s
end
end
##
# download the changeset as an osmChange document.
#
# to make it easier to revert diffs it would be better if the osmChange
# format were reversible, i.e: contained both old and new versions of
# modified elements. but it doesn't at the moment...
#
# this method cannot order the database changes fully (i.e: timestamp and
# version number may be too coarse) so the resulting diff may not apply
# to a different database. however since changesets are not atomic this
# behaviour cannot be guaranteed anyway and is the result of a design
# choice.
def download
changeset = Changeset.find(params[:id])
# get all the elements in the changeset which haven't been redacted
# and stick them in a big array.
elements = [changeset.old_nodes.unredacted,
changeset.old_ways.unredacted,
changeset.old_relations.unredacted].flatten
# sort the elements by timestamp and version number, as this is the
# almost sensible ordering available. this would be much nicer if
# global (SVN-style) versioning were used - then that would be
# unambiguous.
elements.sort! do |a, b|
if a.timestamp == b.timestamp
a.version <=> b.version
else
a.timestamp <=> b.timestamp
end
end
# create changeset and user caches
changeset_cache = {}
user_display_name_cache = {}
# create an osmChange document for the output
result = OSM::API.new.get_xml_doc
result.root.name = "osmChange"
# generate an output element for each operation. note: we avoid looking
# at the history because it is simpler - but it would be more correct to
# check these assertions.
elements.each do |elt|
result.root <<
if elt.version == 1
# first version, so it must be newly-created.
created = XML::Node.new "create"
created << elt.to_xml_node(changeset_cache, user_display_name_cache)
elsif elt.visible
# must be a modify
modified = XML::Node.new "modify"
modified << elt.to_xml_node(changeset_cache, user_display_name_cache)
else
# if the element isn't visible then it must have been deleted
deleted = XML::Node.new "delete"
deleted << elt.to_xml_node(changeset_cache, user_display_name_cache)
end
end
render :xml => result.to_s
end
##
# query changesets by bounding box, time, user or open/closed status.
def query
# find any bounding box
bbox = BoundingBox.from_bbox_params(params) if params["bbox"]
# create the conditions that the user asked for. some or all of
# these may be nil.
changesets = Changeset.all
changesets = conditions_bbox(changesets, bbox)
changesets = conditions_user(changesets, params["user"], params["display_name"])
changesets = conditions_time(changesets, params["time"])
changesets = conditions_open(changesets, params["open"])
changesets = conditions_closed(changesets, params["closed"])
changesets = conditions_ids(changesets, params["changesets"])
# sort and limit the changesets
changesets = changesets.order("created_at DESC").limit(100)
# preload users, tags and comments
changesets = changesets.preload(:user, :changeset_tags, :comments)
# create the results document
results = OSM::API.new.get_xml_doc
# add all matching changesets to the XML results document
changesets.order("created_at DESC").limit(100).each do |cs|
results.root << cs.to_xml_node
end
render :xml => results.to_s
end
##
# updates a changeset's tags. none of the changeset's attributes are
# user-modifiable, so they will be ignored.
#
# changesets are not (yet?) versioned, so we don't have to deal with
# history tables here. changesets are locked to a single user, however.
#
# after succesful update, returns the XML of the changeset.
def update
# request *must* be a PUT.
assert_method :put
changeset = Changeset.find(params[:id])
new_changeset = Changeset.from_xml(request.raw_post)
check_changeset_consistency(changeset, current_user)
changeset.update_from(new_changeset, current_user)
render :xml => changeset.to_xml.to_s
end
##
# list non-empty changesets in reverse chronological order
def index
@ -312,46 +73,6 @@ class ChangesetsController < ApplicationController
index
end
##
# Adds a subscriber to the changeset
def subscribe
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
# Find the changeset and check it is valid
changeset = Changeset.find(id)
raise OSM::APIChangesetAlreadySubscribedError, changeset if changeset.subscribers.exists?(current_user.id)
# Add the subscriber
changeset.subscribers << current_user
# Return a copy of the updated changeset
render :xml => changeset.to_xml.to_s
end
##
# Removes a subscriber from the changeset
def unsubscribe
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
# Find the changeset and check it is valid
changeset = Changeset.find(id)
raise OSM::APIChangesetNotSubscribedError, changeset unless changeset.subscribers.exists?(current_user.id)
# Remove the subscriber
changeset.subscribers.delete(current_user)
# Return a copy of the updated changeset
render :xml => changeset.to_xml.to_s
end
private
#------------------------------------------------------------
@ -375,109 +96,6 @@ class ChangesetsController < ApplicationController
end
end
##
# restrict changesets to those by a particular user
def conditions_user(changesets, user, name)
if user.nil? && name.nil?
changesets
else
# shouldn't provide both name and UID
raise OSM::APIBadUserInput, "provide either the user ID or display name, but not both" if user && name
# use either the name or the UID to find the user which we're selecting on.
u = if name.nil?
# user input checking, we don't have any UIDs < 1
raise OSM::APIBadUserInput, "invalid user ID" if user.to_i < 1
u = User.find(user.to_i)
else
u = User.find_by(:display_name => name)
end
# make sure we found a user
raise OSM::APINotFoundError if u.nil?
# should be able to get changesets of public users only, or
# our own changesets regardless of public-ness.
unless u.data_public?
# get optional user auth stuff so that users can see their own
# changesets if they're non-public
setup_user_auth
raise OSM::APINotFoundError if current_user.nil? || current_user != u
end
changesets.where(:user_id => u.id)
end
end
##
# restrict changes to those closed during a particular time period
def conditions_time(changesets, time)
if time.nil?
changesets
elsif time.count(",") == 1
# if there is a range, i.e: comma separated, then the first is
# low, second is high - same as with bounding boxes.
# check that we actually have 2 elements in the array
times = time.split(/,/)
raise OSM::APIBadUserInput, "bad time range" if times.size != 2
from, to = times.collect { |t| Time.parse(t) }
changesets.where("closed_at >= ? and created_at <= ?", from, to)
else
# if there is no comma, assume its a lower limit on time
changesets.where("closed_at >= ?", Time.parse(time))
end
# stupid Time seems to throw both of these for bad parsing, so
# we have to catch both and ensure the correct code path is taken.
rescue ArgumentError => ex
raise OSM::APIBadUserInput, ex.message.to_s
rescue RuntimeError => ex
raise OSM::APIBadUserInput, ex.message.to_s
end
##
# return changesets which are open (haven't been closed yet)
# we do this by seeing if the 'closed at' time is in the future. Also if we've
# hit the maximum number of changes then it counts as no longer open.
# if parameter 'open' is nill then open and closed changesets are returned
def conditions_open(changesets, open)
if open.nil?
changesets
else
changesets.where("closed_at >= ? and num_changes <= ?",
Time.now.getutc, Changeset::MAX_ELEMENTS)
end
end
##
# query changesets which are closed
# ('closed at' time has passed or changes limit is hit)
def conditions_closed(changesets, closed)
if closed.nil?
changesets
else
changesets.where("closed_at < ? or num_changes > ?",
Time.now.getutc, Changeset::MAX_ELEMENTS)
end
end
##
# query changesets by a list of ids
# (either specified as array or comma-separated string)
def conditions_ids(changesets, ids)
if ids.nil?
changesets
elsif ids.empty?
raise OSM::APIBadUserInput, "No changesets were given to search for"
else
ids = ids.split(",").collect(&:to_i)
changesets.where(:id => ids)
end
end
##
# eliminate empty changesets (where the bbox has not been set)
# this should be applied to all changeset list displays

View file

@ -1,81 +0,0 @@
# The NodeController is the RESTful interface to Node objects
class NodesController < ApplicationController
require "xml/libxml"
skip_before_action :verify_authenticity_token
before_action :authorize, :only => [:create, :update, :delete]
before_action :api_deny_access_handler
authorize_resource
before_action :require_public_data, :only => [:create, :update, :delete]
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
around_action :api_call_handle_error, :api_call_timeout
# Create a node from XML.
def create
assert_method :put
node = Node.from_xml(request.raw_post, true)
# Assume that Node.from_xml has thrown an exception if there is an error parsing the xml
node.create_with_history current_user
render :plain => node.id.to_s
end
# Dump the details on a node given in params[:id]
def show
node = Node.find(params[:id])
response.last_modified = node.timestamp
if node.visible
render :xml => node.to_xml.to_s
else
head :gone
end
end
# Update a node from given XML
def update
node = Node.find(params[:id])
new_node = Node.from_xml(request.raw_post)
raise OSM::APIBadUserInput, "The id in the url (#{node.id}) is not the same as provided in the xml (#{new_node.id})" unless new_node && new_node.id == node.id
node.update_from(new_node, current_user)
render :plain => node.version.to_s
end
# Delete a node. Doesn't actually delete it, but retains its history
# in a wiki-like way. We therefore treat it like an update, so the delete
# method returns the new version number.
def delete
node = Node.find(params[:id])
new_node = Node.from_xml(request.raw_post)
raise OSM::APIBadUserInput, "The id in the url (#{node.id}) is not the same as provided in the xml (#{new_node.id})" unless new_node && new_node.id == node.id
node.delete_with_history!(new_node, current_user)
render :plain => node.version.to_s
end
# Dump the details on many nodes whose ids are given in the "nodes" parameter.
def index
raise OSM::APIBadUserInput, "The parameter nodes is required, and must be of the form nodes=id[,id[,id...]]" unless params["nodes"]
ids = params["nodes"].split(",").collect(&:to_i)
raise OSM::APIBadUserInput, "No nodes were given to search for" if ids.empty?
doc = OSM::API.new.get_xml_doc
Node.find(ids).each do |node|
doc.root << node.to_xml_node
end
render :xml => doc.to_s
end
end

View file

@ -1,316 +1,14 @@
class NotesController < ApplicationController
layout "site", :only => [:mine]
skip_before_action :verify_authenticity_token, :except => [:mine]
before_action :check_api_readable
before_action :authorize_web, :only => [:mine]
before_action :setup_user_auth, :only => [:create, :comment, :show]
before_action :authorize, :only => [:close, :reopen, :destroy]
before_action :api_deny_access_handler, :except => [:mine]
before_action :authorize_web
authorize_resource
before_action :check_api_writable, :only => [:create, :comment, :close, :reopen, :destroy]
before_action :set_locale
around_action :api_call_handle_error, :api_call_timeout
##
# Return a list of notes in a given area
def index
# Figure out the bbox - we prefer a bbox argument but also
# support the old, deprecated, method with four arguments
if params[:bbox]
bbox = BoundingBox.from_bbox_params(params)
else
raise OSM::APIBadUserInput, "No l was given" unless params[:l]
raise OSM::APIBadUserInput, "No r was given" unless params[:r]
raise OSM::APIBadUserInput, "No b was given" unless params[:b]
raise OSM::APIBadUserInput, "No t was given" unless params[:t]
bbox = BoundingBox.from_lrbt_params(params)
end
# Get any conditions that need to be applied
notes = closed_condition(Note.all)
# Check that the boundaries are valid
bbox.check_boundaries
# Check the the bounding box is not too big
bbox.check_size(MAX_NOTE_REQUEST_AREA)
# Find the notes we want to return
@notes = notes.bbox(bbox).order("updated_at DESC").limit(result_limit).preload(:comments)
# Render the result
respond_to do |format|
format.rss
format.xml
format.json
format.gpx
end
end
##
# Create a new note
def create
# Check the ACLs
raise OSM::APIAccessDenied if current_user.nil? && Acl.no_note_comment(request.remote_ip)
# Check the arguments are sane
raise OSM::APIBadUserInput, "No lat was given" unless params[:lat]
raise OSM::APIBadUserInput, "No lon was given" unless params[:lon]
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
# Extract the arguments
lon = OSM.parse_float(params[:lon], OSM::APIBadUserInput, "lon was not a number")
lat = OSM.parse_float(params[:lat], OSM::APIBadUserInput, "lat was not a number")
comment = params[:text]
# Include in a transaction to ensure that there is always a note_comment for every note
Note.transaction do
# Create the note
@note = Note.create(:lat => lat, :lon => lon)
raise OSM::APIBadUserInput, "The note is outside this world" unless @note.in_world?
# Save the note
@note.save!
# Add a comment to the note
add_comment(@note, comment, "opened")
end
# Return a copy of the new note
respond_to do |format|
format.xml { render :action => :show }
format.json { render :action => :show }
end
end
##
# Add a comment to an existing note
def comment
# Check the ACLs
raise OSM::APIAccessDenied if current_user.nil? && Acl.no_note_comment(request.remote_ip)
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
# Extract the arguments
id = params[:id].to_i
comment = params[:text]
# Find the note and check it is valid
@note = Note.find(id)
raise OSM::APINotFoundError unless @note
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
# Add a comment to the note
Note.transaction do
add_comment(@note, comment, "commented")
end
# Return a copy of the updated note
respond_to do |format|
format.xml { render :action => :show }
format.json { render :action => :show }
end
end
##
# Close a note
def close
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
comment = params[:text]
# Find the note and check it is valid
@note = Note.find_by(:id => id)
raise OSM::APINotFoundError unless @note
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
# Close the note and add a comment
Note.transaction do
@note.close
add_comment(@note, comment, "closed")
end
# Return a copy of the updated note
respond_to do |format|
format.xml { render :action => :show }
format.json { render :action => :show }
end
end
##
# Reopen a note
def reopen
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
comment = params[:text]
# Find the note and check it is valid
@note = Note.find_by(:id => id)
raise OSM::APINotFoundError unless @note
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user.moderator?
raise OSM::APINoteAlreadyOpenError, @note unless @note.closed? || !@note.visible?
# Reopen the note and add a comment
Note.transaction do
@note.reopen
add_comment(@note, comment, "reopened")
end
# Return a copy of the updated note
respond_to do |format|
format.xml { render :action => :show }
format.json { render :action => :show }
end
end
##
# Get a feed of recent notes and comments
def feed
# Get any conditions that need to be applied
notes = closed_condition(Note.all)
# Process any bbox
if params[:bbox]
bbox = BoundingBox.from_bbox_params(params)
bbox.check_boundaries
bbox.check_size(MAX_NOTE_REQUEST_AREA)
notes = notes.bbox(bbox)
end
# Find the comments we want to return
@comments = NoteComment.where(:note_id => notes).order("created_at DESC").limit(result_limit).preload(:note)
# Render the result
respond_to do |format|
format.rss
end
end
##
# Read a note
def show
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Find the note and check it is valid
@note = Note.find(params[:id])
raise OSM::APINotFoundError unless @note
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user&.moderator?
# Render the result
respond_to do |format|
format.xml
format.rss
format.json
format.gpx
end
end
##
# Delete (hide) a note
def destroy
# Check the arguments are sane
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
# Extract the arguments
id = params[:id].to_i
comment = params[:text]
# Find the note and check it is valid
@note = Note.find(id)
raise OSM::APINotFoundError unless @note
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
# Mark the note as hidden
Note.transaction do
@note.status = "hidden"
@note.save
add_comment(@note, comment, "hidden", false)
end
# Return a copy of the updated note
respond_to do |format|
format.xml { render :action => :show }
format.json { render :action => :show }
end
end
##
# Return a list of notes matching a given string
def search
# Get the initial set of notes
@notes = closed_condition(Note.all)
# Add any user filter
if params[:display_name] || params[:user]
if params[:display_name]
@user = User.find_by(:display_name => params[:display_name])
raise OSM::APIBadUserInput, "User #{params[:display_name]} not known" unless @user
else
@user = User.find_by(:id => params[:user])
raise OSM::APIBadUserInput, "User #{params[:user]} not known" unless @user
end
@notes = @notes.joins(:comments).where(:note_comments => { :author_id => @user })
end
# Add any text filter
@notes = @notes.joins(:comments).where("to_tsvector('english', note_comments.body) @@ plainto_tsquery('english', ?)", params[:q]) if params[:q]
# Add any date filter
if params[:from]
begin
from = Time.parse(params[:from])
rescue ArgumentError
raise OSM::APIBadUserInput, "Date #{params[:from]} is in a wrong format"
end
begin
to = if params[:to]
Time.parse(params[:to])
else
Time.now
end
rescue ArgumentError
raise OSM::APIBadUserInput, "Date #{params[:to]} is in a wrong format"
end
@notes = @notes.where(:created_at => from..to)
end
# Find the notes we want to return
@notes = @notes.order("updated_at DESC").limit(result_limit).preload(:comments)
# Render the result
respond_to do |format|
format.rss { render :action => :index }
format.xml { render :action => :index }
format.json { render :action => :index }
format.gpx { render :action => :index }
end
end
##
# Display a list of notes by a specified user
def mine
@ -333,63 +31,4 @@ class NotesController < ApplicationController
end
end
end
private
#------------------------------------------------------------
# utility functions below.
#------------------------------------------------------------
##
# Get the maximum number of results to return
def result_limit
if params[:limit]
if params[:limit].to_i.positive? && params[:limit].to_i <= 10000
params[:limit].to_i
else
raise OSM::APIBadUserInput, "Note limit must be between 1 and 10000"
end
else
100
end
end
##
# Generate a condition to choose which notes we want based
# on their status and the user's request parameters
def closed_condition(notes)
closed_since = if params[:closed]
params[:closed].to_i
else
7
end
if closed_since.negative?
notes.where.not(:status => "hidden")
elsif closed_since.positive?
notes.where(:status => "open")
.or(notes.where(:status => "closed")
.where(notes.arel_table[:closed_at].gt(Time.now - closed_since.days)))
else
notes.where(:status => "open")
end
end
##
# Add a comment to a note
def add_comment(note, text, event, notify = true)
attributes = { :visible => true, :event => event, :body => text }
if current_user
attributes[:author_id] = current_user.id
else
attributes[:author_ip] = request.remote_ip
end
comment = note.comments.create!(attributes)
note.comments.map(&:author).uniq.each do |user|
Notifier.note_comment_notification(comment, user).deliver_later if notify && user && user != current_user && user.visible?
end
end
end

View file

@ -1,77 +0,0 @@
# this class pulls together the logic for all the old_* controllers
# into one place. as it turns out, the API methods for historical
# nodes, ways and relations are basically identical.
class OldController < ApplicationController
require "xml/libxml"
skip_before_action :verify_authenticity_token
before_action :setup_user_auth, :only => [:history, :version]
before_action :api_deny_access_handler
before_action :authorize, :only => [:redact]
authorize_resource
before_action :check_api_readable
before_action :check_api_writable, :only => [:redact]
around_action :api_call_handle_error, :api_call_timeout
before_action :lookup_old_element, :except => [:history]
before_action :lookup_old_element_versions, :only => [:history]
def history
# the .where() method used in the lookup_old_element_versions
# call won't throw an error if no records are found, so we have
# to do that ourselves.
raise OSM::APINotFoundError if @elements.empty?
doc = OSM::API.new.get_xml_doc
visible_elements = if show_redactions?
@elements
else
@elements.unredacted
end
visible_elements.each do |element|
doc.root << element.to_xml_node
end
render :xml => doc.to_s
end
def version
if @old_element.redacted? && !show_redactions?
head :forbidden
else
response.last_modified = @old_element.timestamp
doc = OSM::API.new.get_xml_doc
doc.root << @old_element.to_xml_node
render :xml => doc.to_s
end
end
def redact
redaction_id = params["redaction"]
if redaction_id.nil?
# if no redaction ID was provided, then this is an unredact
# operation.
@old_element.redact!(nil)
else
# if a redaction ID was specified, then set this element to
# be redacted in that redaction.
redaction = Redaction.find(redaction_id.to_i)
@old_element.redact!(redaction)
end
# just return an empty 200 OK for success
head :ok
end
private
def show_redactions?
current_user&.moderator? && params[:show_redactions] == "true"
end
end

View file

@ -1,11 +0,0 @@
class OldNodesController < OldController
private
def lookup_old_element
@old_element = OldNode.find([params[:id], params[:version]])
end
def lookup_old_element_versions
@elements = OldNode.where(:node_id => params[:id]).order(:version)
end
end

View file

@ -1,11 +0,0 @@
class OldRelationsController < OldController
private
def lookup_old_element
@old_element = OldRelation.find([params[:id], params[:version]])
end
def lookup_old_element_versions
@elements = OldRelation.where(:relation_id => params[:id]).order(:version)
end
end

View file

@ -1,11 +0,0 @@
class OldWaysController < OldController
private
def lookup_old_element
@old_element = OldWay.find([params[:id], params[:version]])
end
def lookup_old_element_versions
@elements = OldWay.where(:way_id => params[:id]).order(:version)
end
end

View file

@ -1,167 +0,0 @@
class RelationsController < ApplicationController
require "xml/libxml"
skip_before_action :verify_authenticity_token
before_action :authorize, :only => [:create, :update, :delete]
before_action :api_deny_access_handler
authorize_resource
before_action :require_public_data, :only => [:create, :update, :delete]
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
around_action :api_call_handle_error, :api_call_timeout
def create
assert_method :put
relation = Relation.from_xml(request.raw_post, true)
# Assume that Relation.from_xml has thrown an exception if there is an error parsing the xml
relation.create_with_history current_user
render :plain => relation.id.to_s
end
def show
relation = Relation.find(params[:id])
response.last_modified = relation.timestamp
if relation.visible
render :xml => relation.to_xml.to_s
else
head :gone
end
end
def update
logger.debug request.raw_post
relation = Relation.find(params[:id])
new_relation = Relation.from_xml(request.raw_post)
raise OSM::APIBadUserInput, "The id in the url (#{relation.id}) is not the same as provided in the xml (#{new_relation.id})" unless new_relation && new_relation.id == relation.id
relation.update_from new_relation, current_user
render :plain => relation.version.to_s
end
def delete
relation = Relation.find(params[:id])
new_relation = Relation.from_xml(request.raw_post)
if new_relation && new_relation.id == relation.id
relation.delete_with_history!(new_relation, current_user)
render :plain => relation.version.to_s
else
head :bad_request
end
end
# -----------------------------------------------------------------
# full
#
# input parameters: id
#
# returns XML representation of one relation object plus all its
# members, plus all nodes part of member ways
# -----------------------------------------------------------------
def full
relation = Relation.find(params[:id])
if relation.visible
# first find the ids of nodes, ways and relations referenced by this
# relation - note that we exclude this relation just in case.
node_ids = relation.members.select { |m| m[0] == "Node" }.map { |m| m[1] }
way_ids = relation.members.select { |m| m[0] == "Way" }.map { |m| m[1] }
relation_ids = relation.members.select { |m| m[0] == "Relation" && m[1] != relation.id }.map { |m| m[1] }
# next load the relations and the ways.
relations = Relation.where(:id => relation_ids).includes(:relation_tags)
ways = Way.where(:id => way_ids).includes(:way_nodes, :way_tags)
# now additionally collect nodes referenced by ways. Note how we
# recursively evaluate ways but NOT relations.
way_node_ids = ways.collect do |way|
way.way_nodes.collect(&:node_id)
end
node_ids += way_node_ids.flatten
nodes = Node.where(:id => node_ids.uniq).includes(:node_tags)
# create XML.
doc = OSM::API.new.get_xml_doc
visible_nodes = {}
changeset_cache = {}
user_display_name_cache = {}
nodes.each do |node|
next unless node.visible? # should be unnecessary if data is consistent.
doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
visible_nodes[node.id] = node
end
ways.each do |way|
next unless way.visible? # should be unnecessary if data is consistent.
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
end
relations.each do |rel|
next unless rel.visible? # should be unnecessary if data is consistent.
doc.root << rel.to_xml_node(changeset_cache, user_display_name_cache)
end
# finally add self and output
doc.root << relation.to_xml_node(changeset_cache, user_display_name_cache)
render :xml => doc.to_s
else
head :gone
end
end
def index
raise OSM::APIBadUserInput, "The parameter relations is required, and must be of the form relations=id[,id[,id...]]" unless params["relations"]
ids = params["relations"].split(",").collect(&:to_i)
raise OSM::APIBadUserInput, "No relations were given to search for" if ids.empty?
doc = OSM::API.new.get_xml_doc
Relation.find(ids).each do |relation|
doc.root << relation.to_xml_node
end
render :xml => doc.to_s
end
def relations_for_way
relations_for_object("Way")
end
def relations_for_node
relations_for_object("Node")
end
def relations_for_relation
relations_for_object("Relation")
end
private
def relations_for_object(objtype)
relationids = RelationMember.where(:member_type => objtype, :member_id => params[:id]).collect(&:relation_id).uniq
doc = OSM::API.new.get_xml_doc
Relation.find(relationids).each do |relation|
doc.root << relation.to_xml_node if relation.visible
end
render :xml => doc.to_s
end
end

View file

@ -1,100 +0,0 @@
class SearchController < ApplicationController
# Support searching for nodes, ways, or all
# Can search by tag k, v, or both (type->k,value->v)
# Can search by name (k=name,v=....)
skip_before_action :verify_authenticity_token
authorize_resource :class => false
def search_all
do_search(true, true, true)
end
def search_ways
do_search(true, false, false)
end
def search_nodes
do_search(false, true, false)
end
def search_relations
do_search(false, false, true)
end
def do_search(do_ways, do_nodes, do_relations)
type = params["type"]
value = params["value"]
unless type || value
name = params["name"]
if name
type = "name"
value = name
end
end
if do_nodes
response.headers["Error"] = "Searching of nodes is currently unavailable"
head :service_unavailable
return false
end
unless value
response.headers["Error"] = "Searching for a key without value is currently unavailable"
head :service_unavailable
return false
end
# Matching for node tags table
if do_nodes
nodes = Node.joins(:node_tags)
nodes = nodes.where(:current_node_tags => { :k => type }) if type
nodes = nodes.where(:current_node_tags => { :v => value }) if value
nodes = nodes.limit(100)
else
nodes = []
end
# Matching for way tags table
if do_ways
ways = Way.joins(:way_tags)
ways = ways.where(:current_way_tags => { :k => type }) if type
ways = ways.where(:current_way_tags => { :v => value }) if value
ways = ways.limit(100)
else
ways = []
end
# Matching for relation tags table
if do_relations
relations = Relation.joins(:relation_tags)
relations = relations.where(:current_relation_tags => { :k => type }) if type
relations = relations.where(:current_relation_tags => { :v => value }) if value
relations = relations.limit(2000)
else
relations = []
end
# Fetch any node needed for our ways (only have matching nodes so far)
nodes += Node.find(ways.collect(&:nds).uniq)
# Print
visible_nodes = {}
changeset_cache = {}
user_display_name_cache = {}
doc = OSM::API.new.get_xml_doc
nodes.each do |node|
doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
visible_nodes[node.id] = node
end
ways.each do |way|
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
end
relations.each do |rel|
doc.root << rel.to_xml_node(changeset_cache, user_display_name_cache)
end
render :xml => doc.to_s
end
end

View file

@ -1,217 +0,0 @@
class SwfController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :check_api_readable
authorize_resource :class => false
# to log:
# RAILS_DEFAULT_LOGGER.error("Args: #{args[0]}, #{args[1]}, #{args[2]}, #{args[3]}")
# $log.puts Time.new.to_s+','+Time.new.usec.to_s+": started GPS script"
# http://localhost:3000/api/0.4/swf/trackpoints?xmin=-2.32402605810577&xmax=-2.18386309423859&ymin=52.1546608755772&ymax=52.2272777906895&baselong=-2.25325793066437&basey=61.3948537948532&masterscale=5825.4222222222
# ====================================================================
# Public methods
# ---- trackpoints compile SWF of trackpoints
def trackpoints
# - Initialise
baselong = params["baselong"].to_f
basey = params["basey"].to_f
masterscale = params["masterscale"].to_f
bbox = BoundingBox.new(params["xmin"], params["ymin"],
params["xmax"], params["ymax"])
start = params["start"].to_i
# - Begin movie
bounds_left = 0
bounds_right = 320 * 20
bounds_bottom = 0
bounds_top = 240 * 20
m = ""
m += swf_record(9, 255.chr + 155.chr + 155.chr) # Background
absx = 0
absy = 0
xl = yb = 9999999
xr = yt = -9999999
# - Send SQL for GPS tracks
b = ""
lasttime = 0
lasttrack = lastfile = "-1"
if params["token"]
user = User.authenticate(:token => params[:token])
sql = "SELECT gps_points.latitude*0.0000001 AS lat,gps_points.longitude*0.0000001 AS lon,gpx_files.id AS fileid," + " EXTRACT(EPOCH FROM gps_points.timestamp) AS ts, gps_points.trackid AS trackid " + " FROM gpx_files,gps_points " + "WHERE gpx_files.id=gpx_id " + " AND gpx_files.user_id=#{user.id} " + " AND " + OSM.sql_for_area(bbox, "gps_points.") + " AND (gps_points.timestamp IS NOT NULL) " + "ORDER BY fileid DESC,ts " + "LIMIT 10000 OFFSET #{start}"
else
sql = "SELECT latitude*0.0000001 AS lat,longitude*0.0000001 AS lon,gpx_id AS fileid," + " EXTRACT(EPOCH FROM timestamp) AS ts, gps_points.trackid AS trackid " + " FROM gps_points " + "WHERE " + OSM.sql_for_area(bbox, "gps_points.") + " AND (gps_points.timestamp IS NOT NULL) " + "ORDER BY fileid DESC,ts " + "LIMIT 10000 OFFSET #{start}"
end
gpslist = ActiveRecord::Base.connection.select_all sql
# - Draw GPS trace lines
r = start_shape
gpslist.each do |row|
xs = (long2coord(row["lon"].to_f, baselong, masterscale) * 20).floor
ys = (lat2coord(row["lat"].to_f, basey, masterscale) * 20).floor
xl = [xs, xl].min
xr = [xs, xr].max
yb = [ys, yb].min
yt = [ys, yt].max
if row["ts"].to_i - lasttime > 180 || row["fileid"] != lastfile || row["trackid"] != lasttrack # or row['ts'].to_i==lasttime
b += start_and_move(xs, ys, "01")
absx = xs.floor
absy = ys.floor
end
b += draw_to(absx, absy, xs, ys)
absx = xs.floor
absy = ys.floor
lasttime = row["ts"].to_i
lastfile = row["fileid"]
lasttrack = row["trackid"]
r += [b.slice!(0...80)].pack("B*") while b.length > 80
end
# (Unwayed segments removed)
# - Write shape
b += end_shape
r += [b].pack("B*")
m += swf_record(2, pack_u16(1) + pack_rect(xl, xr, yb, yt) + r)
m += swf_record(4, pack_u16(1) + pack_u16(1))
# - Create Flash header and write to browser
m += swf_record(1, "") # Show frame
m += swf_record(0, "") # End
m = pack_rect(bounds_left, bounds_right, bounds_bottom, bounds_top) + 0.chr + 12.chr + pack_u16(1) + m
m = "FWS" + 6.chr + pack_u32(m.length + 8) + m
render :body => m, :content_type => "application/x-shockwave-flash"
end
private
# =======================================================================
# SWF functions
# -----------------------------------------------------------------------
# Line-drawing
def start_shape
s = 0.chr # No fill styles
s += 2.chr # Two line styles
s += pack_u16(0) + 0.chr + 255.chr + 255.chr # Width 5, RGB #00FFFF
s += pack_u16(0) + 255.chr + 0.chr + 255.chr # Width 5, RGB #FF00FF
s += 34.chr # 2 fill, 2 line index bits
s
end
def end_shape
"000000"
end
def start_and_move(x, y, col)
d = "001001" # Line style change, moveTo
l = [length_sb(x), length_sb(y)].max
d += format("%05b%0*b%0*b", l, l, x, l, y)
d += col # Select line style
d
end
def draw_to(absx, absy, x, y)
dx = x - absx
dy = y - absy
# Split the line up if there's anything>16383, because
# that would overflow the 4 bits allowed for length
mstep = [dx.abs / 16383, dy.abs / 16383, 1].max.ceil
xstep = dx / mstep
ystep = dy / mstep
d = ""
1.upto(mstep).each do
d += draw_section(x, y, x + xstep, y + ystep)
x += xstep
y += ystep
end
d
end
def draw_section(x1, y1, x2, y2)
d = "11" # TypeFlag, EdgeFlag
dx = x2 - x1
dy = y2 - y1
l = [length_sb(dx), length_sb(dy)].max
d += format("%04b", l - 2)
d += "1" # GeneralLine
d += format("%0*b%0*b", l, dx, l, dy)
d
end
# -----------------------------------------------------------------------
# Specific data types
# SWF data block type
def swf_record(id, r)
if r.length > 62
# Long header: tag id, 0x3F, length
pack_u16((id << 6) + 0x3F) + pack_u32(r.length) + r
else
# Short header: tag id, length
pack_u16((id << 6) + r.length) + r
end
end
# SWF RECT type
def pack_rect(a, b, c, d)
l = [length_sb(a),
length_sb(b),
length_sb(c),
length_sb(d)].max
# create binary string (00111001 etc.) - 5-byte length, then bbox
n = format("%05b%0*b%0*b%0*b%0*b", l, l, a, l, b, l, c, l, d)
# pack into byte string
[n].pack("B*")
end
# -----------------------------------------------------------------------
# Generic pack functions
def pack_u16(n)
[n.floor].pack("v")
end
def pack_u32(n)
[n.floor].pack("V")
end
# Find number of bits required to store arbitrary-length binary
def length_sb(n)
Math.frexp(n + (n.zero? ? 1 : 0))[1] + 1
end
# ====================================================================
# Co-ordinate conversion
# (this is duplicated from amf_controller, should probably share)
def lat2coord(a, basey, masterscale)
-(lat2y(a) - basey) * masterscale
end
def long2coord(a, baselong, masterscale)
(a - baselong) * masterscale
end
def lat2y(a)
180 / Math::PI * Math.log(Math.tan(Math::PI / 4 + a * (Math::PI / 180) / 2))
end
end

View file

@ -1,21 +1,15 @@
class TracesController < ApplicationController
layout "site", :except => :georss
skip_before_action :verify_authenticity_token, :only => [:api_create, :api_read, :api_update, :api_delete, :api_data]
before_action :authorize_web
before_action :set_locale
before_action :authorize, :only => [:api_create, :api_read, :api_update, :api_delete, :api_data]
before_action :api_deny_access_handler, :only => [:api_create, :api_read, :api_update, :api_delete, :api_data]
authorize_resource
before_action :check_database_readable, :except => [:api_read, :api_data]
before_action :check_database_writable, :only => [:new, :create, :edit, :delete, :api_create, :api_update, :api_delete]
before_action :check_api_readable, :only => [:api_read, :api_data]
before_action :check_api_writable, :only => [:api_create, :api_update, :api_delete]
before_action :check_database_readable
before_action :check_database_writable, :only => [:new, :create, :edit, :delete]
before_action :offline_warning, :only => [:mine, :show]
before_action :offline_redirect, :only => [:new, :create, :edit, :delete, :data, :api_create, :api_delete, :api_data]
around_action :api_call_handle_error, :only => [:api_create, :api_read, :api_update, :api_delete, :api_data]
before_action :offline_redirect, :only => [:new, :create, :edit, :delete, :data]
# Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
# target_user - if set, specifies the user to fetch traces for. if not set will fetch all traces
@ -263,86 +257,6 @@ class TracesController < ApplicationController
head :not_found
end
def api_read
trace = Trace.visible.find(params[:id])
if trace.public? || trace.user == current_user
render :xml => trace.to_xml.to_s
else
head :forbidden
end
end
def api_update
trace = Trace.visible.find(params[:id])
if trace.user == current_user
trace.update_from_xml(request.raw_post)
trace.save!
head :ok
else
head :forbidden
end
end
def api_delete
trace = Trace.visible.find(params[:id])
if trace.user == current_user
trace.visible = false
trace.save!
head :ok
else
head :forbidden
end
end
def api_data
trace = Trace.visible.find(params[:id])
if trace.public? || trace.user == current_user
if request.format == Mime[:xml]
send_data(trace.xml_file.read, :filename => "#{trace.id}.xml", :type => request.format.to_s, :disposition => "attachment")
elsif request.format == Mime[:gpx]
send_data(trace.xml_file.read, :filename => "#{trace.id}.gpx", :type => request.format.to_s, :disposition => "attachment")
else
send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => "attachment")
end
else
head :forbidden
end
end
def api_create
tags = params[:tags] || ""
description = params[:description] || ""
visibility = params[:visibility]
if visibility.nil?
visibility = if params[:public]&.to_i&.nonzero?
"public"
else
"private"
end
end
if params[:file].respond_to?(:read)
trace = do_create(params[:file], tags, description, visibility)
if trace.id
render :plain => trace.id.to_s
elsif trace.valid?
head :internal_server_error
else
head :bad_request
end
else
head :bad_request
end
end
private
def do_create(file, tags, description, visibility)

View file

@ -1,88 +0,0 @@
# Update and read user preferences, which are arbitrayr key/val pairs
class UserPreferencesController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authorize
authorize_resource
around_action :api_call_handle_error
##
# return all the preferences as an XML document
def read
doc = OSM::API.new.get_xml_doc
prefs = current_user.preferences
el1 = XML::Node.new "preferences"
prefs.each do |pref|
el1 << pref.to_xml_node
end
doc.root << el1
render :xml => doc.to_s
end
##
# return the value for a single preference
def read_one
pref = UserPreference.find([current_user.id, params[:preference_key]])
render :plain => pref.v.to_s
end
# update the entire set of preferences
def update
old_preferences = current_user.preferences.each_with_object({}) do |preference, preferences|
preferences[preference.k] = preference
end
new_preferences = {}
doc = XML::Parser.string(request.raw_post, :options => XML::Parser::Options::NOERROR).parse
doc.find("//preferences/preference").each do |pt|
if preference = old_preferences.delete(pt["k"])
preference.v = pt["v"]
elsif new_preferences.include?(pt["k"])
raise OSM::APIDuplicatePreferenceError, pt["k"]
else
preference = current_user.preferences.build(:k => pt["k"], :v => pt["v"])
end
new_preferences[preference.k] = preference
end
old_preferences.each_value(&:delete)
new_preferences.each_value(&:save!)
render :plain => ""
end
##
# update the value of a single preference
def update_one
begin
pref = UserPreference.find([current_user.id, params[:preference_key]])
rescue ActiveRecord::RecordNotFound
pref = UserPreference.new
pref.user = current_user
pref.k = params[:preference_key]
end
pref.v = request.raw_post.chomp
pref.save!
render :plain => ""
end
##
# delete a single preference
def delete_one
UserPreference.find([current_user.id, params[:preference_key]]).delete
render :plain => ""
end
end

View file

@ -1,22 +1,17 @@
class UsersController < ApplicationController
layout "site", :except => [:api_details]
layout "site"
skip_before_action :verify_authenticity_token, :only => [:api_read, :api_users, :api_details, :api_gpx_files, :auth_success]
before_action :disable_terms_redirect, :only => [:terms, :save, :logout, :api_details]
before_action :authorize, :only => [:api_details, :api_gpx_files]
before_action :authorize_web, :except => [:api_read, :api_users, :api_details, :api_gpx_files]
before_action :set_locale, :except => [:api_read, :api_users, :api_details, :api_gpx_files]
before_action :api_deny_access_handler, :only => [:api_read, :api_users, :api_details, :api_gpx_files]
skip_before_action :verify_authenticity_token, :only => [:auth_success]
before_action :disable_terms_redirect, :only => [:terms, :save, :logout]
before_action :authorize_web
before_action :set_locale
authorize_resource
before_action :require_self, :only => [:account]
before_action :check_database_readable, :except => [:login, :api_read, :api_users, :api_details, :api_gpx_files]
before_action :check_database_readable, :except => [:login]
before_action :check_database_writable, :only => [:new, :account, :confirm, :confirm_email, :lost_password, :reset_password, :go_public, :make_friend, :remove_friend]
before_action :check_api_readable, :only => [:api_read, :api_users, :api_details, :api_gpx_files]
before_action :require_cookies, :only => [:new, :login, :confirm]
around_action :api_call_handle_error, :only => [:api_read, :api_users, :api_details, :api_gpx_files]
before_action :lookup_user_by_id, :only => [:api_read]
before_action :lookup_user_by_name, :only => [:set_status, :delete]
before_action :allow_thirdparty_images, :only => [:show, :account]
@ -373,39 +368,6 @@ class UsersController < ApplicationController
end
end
def api_read
if @user.visible?
render :action => :api_read, :content_type => "text/xml"
else
head :gone
end
end
def api_details
@user = current_user
render :action => :api_read, :content_type => "text/xml"
end
def api_users
raise OSM::APIBadUserInput, "The parameter users is required, and must be of the form users=id[,id[,id...]]" unless params["users"]
ids = params["users"].split(",").collect(&:to_i)
raise OSM::APIBadUserInput, "No users were given to search for" if ids.empty?
@users = User.visible.find(ids)
render :action => :api_users, :content_type => "text/xml"
end
def api_gpx_files
doc = OSM::API.new.get_xml_doc
current_user.traces.reload.each do |trace|
doc.root << trace.to_xml_node
end
render :xml => doc.to_s
end
def show
@user = User.find_by(:display_name => params[:display_name])
@ -752,12 +714,6 @@ class UsersController < ApplicationController
head :forbidden if params[:display_name] != current_user.display_name
end
##
# ensure that there is a "user" instance variable
def lookup_user_by_id
@user = User.find(params[:id])
end
##
# ensure that there is a "user" instance variable
def lookup_user_by_name

View file

@ -1,118 +0,0 @@
class WaysController < ApplicationController
require "xml/libxml"
skip_before_action :verify_authenticity_token
before_action :authorize, :only => [:create, :update, :delete]
before_action :api_deny_access_handler
authorize_resource
before_action :require_public_data, :only => [:create, :update, :delete]
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
around_action :api_call_handle_error, :api_call_timeout
def create
assert_method :put
way = Way.from_xml(request.raw_post, true)
# Assume that Way.from_xml has thrown an exception if there is an error parsing the xml
way.create_with_history current_user
render :plain => way.id.to_s
end
def show
way = Way.find(params[:id])
response.last_modified = way.timestamp
if way.visible
render :xml => way.to_xml.to_s
else
head :gone
end
end
def update
way = Way.find(params[:id])
new_way = Way.from_xml(request.raw_post)
unless new_way && new_way.id == way.id
raise OSM::APIBadUserInput, "The id in the url (#{way.id}) is not the same as provided in the xml (#{new_way.id})"
end
way.update_from(new_way, current_user)
render :plain => way.version.to_s
end
# This is the API call to delete a way
def delete
way = Way.find(params[:id])
new_way = Way.from_xml(request.raw_post)
if new_way && new_way.id == way.id
way.delete_with_history!(new_way, current_user)
render :plain => way.version.to_s
else
head :bad_request
end
end
def full
way = Way.includes(:nodes => :node_tags).find(params[:id])
if way.visible
visible_nodes = {}
changeset_cache = {}
user_display_name_cache = {}
doc = OSM::API.new.get_xml_doc
way.nodes.uniq.each do |node|
if node.visible
doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
visible_nodes[node.id] = node
end
end
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
render :xml => doc.to_s
else
head :gone
end
end
def index
unless params["ways"]
raise OSM::APIBadUserInput, "The parameter ways is required, and must be of the form ways=id[,id[,id...]]"
end
ids = params["ways"].split(",").collect(&:to_i)
raise OSM::APIBadUserInput, "No ways were given to search for" if ids.empty?
doc = OSM::API.new.get_xml_doc
Way.find(ids).each do |way|
doc.root << way.to_xml_node
end
render :xml => doc.to_s
end
##
# returns all the ways which are currently using the node given in the
# :id parameter. note that this used to return deleted ways as well, but
# this seemed not to be the expected behaviour, so it was removed.
def ways_for_node
wayids = WayNode.where(:node_id => params[:id]).collect { |ws| ws.id[0] }.uniq
doc = OSM::API.new.get_xml_doc
Way.find(wayids).each do |way|
doc.root << way.to_xml_node if way.visible
end
render :xml => doc.to_s
end
end

View file

@ -18,7 +18,7 @@ module NoteHelper
elsif author.status == "deleted"
t("users.no_such_user.deleted")
else
link_to h(author.display_name), link_options.merge(:controller => "users", :action => "show", :display_name => author.display_name)
link_to h(author.display_name), link_options.merge(:controller => "/users", :action => "show", :display_name => author.display_name)
end
end
end

View file

@ -2,11 +2,11 @@ xml.item do
location = describe_location(note.lat, note.lon, 14, locale)
if note.closed?
xml.title t("notes.rss.closed", :place => location)
xml.title t("api.notes.rss.closed", :place => location)
elsif note.comments.length > 1
xml.title t("notes.rss.commented", :place => location)
xml.title t("api.notes.rss.commented", :place => location)
else
xml.title t("notes.rss.opened", :place => location)
xml.title t("api.notes.rss.opened", :place => location)
end
xml.link browse_note_url(note)

View file

@ -5,18 +5,18 @@ xml.rss("version" => "2.0",
"xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
"xmlns:georss" => "http://www.georss.org/georss") do
xml.channel do
xml.title t("notes.rss.title")
xml.description t("notes.rss.description_area", :min_lat => @min_lat, :min_lon => @min_lon, :max_lat => @max_lat, :max_lon => @max_lon)
xml.link url_for(:controller => "site", :action => "index", :only_path => false)
xml.title t("api.notes.rss.title")
xml.description t("api.notes.rss.description_area", :min_lat => @min_lat, :min_lon => @min_lon, :max_lat => @max_lat, :max_lon => @max_lon)
xml.link url_for(:controller => "/site", :action => "index", :only_path => false)
@comments.each do |comment|
location = describe_location(comment.note.lat, comment.note.lon, 14, locale)
xml.item do
xml.title t("notes.rss.#{comment.event}", :place => location)
xml.title t("api.notes.rss.#{comment.event}", :place => location)
xml.link url_for(:controller => "browse", :action => "note", :id => comment.note.id, :anchor => "c#{comment.id}", :only_path => false)
xml.guid url_for(:controller => "browse", :action => "note", :id => comment.note.id, :anchor => "c#{comment.id}", :only_path => false)
xml.link url_for(:controller => "/browse", :action => "note", :id => comment.note.id, :anchor => "c#{comment.id}", :only_path => false)
xml.guid url_for(:controller => "/browse", :action => "note", :id => comment.note.id, :anchor => "c#{comment.id}", :only_path => false)
xml.description do
xml.cdata! render(:partial => "entry", :object => comment, :formats => [:html])

View file

@ -5,9 +5,9 @@ xml.rss("version" => "2.0",
"xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
"xmlns:georss" => "http://www.georss.org/georss") do
xml.channel do
xml.title t("notes.rss.title")
xml.description t("notes.rss.description_area", :min_lat => @min_lat, :min_lon => @min_lon, :max_lat => @max_lat, :max_lon => @max_lon)
xml.link url_for(:controller => "site", :action => "index", :only_path => false)
xml.title t("api.notes.rss.title")
xml.description t("api.notes.rss.description_area", :min_lat => @min_lat, :min_lon => @min_lon, :max_lat => @max_lat, :max_lon => @max_lon)
xml.link url_for(:controller => "/site", :action => "index", :only_path => false)
xml << (render(:partial => "note", :collection => @notes) || "")
end

View file

@ -4,9 +4,9 @@ xml.rss("version" => "2.0",
"xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
"xmlns:georss" => "http://www.georss.org/georss") do
xml.channel do
xml.title t("notes.rss.title")
xml.description t("notes.rss.description_item", :id => @note.id)
xml.link url_for(:controller => "site", :action => "index", :only_path => false)
xml.title t("api.notes.rss.title")
xml.description t("api.notes.rss.description_item", :id => @note.id)
xml.link url_for(:controller => "/site", :action => "index", :only_path => false)
xml << render(:partial => "note", :object => @note)
end

View file

@ -137,7 +137,7 @@
<% end %>
<div class='secondary-actions'>
<%= link_to(t('.changesetxml'), :controller => "changesets", :action => "show") %>
<%= link_to(t('.changesetxml'), :controller => "api/changesets", :action => "show") %>
&middot;
<%= link_to(t('.osmchangexml'), :controller => "changesets", :action => "download") %>
<%= link_to(t('.osmchangexml'), :controller => "api/changesets", :action => "download") %>
</div>

View file

@ -8,7 +8,7 @@
<%= render :partial => @type, :object => @feature %>
<div class='secondary-actions'>
<%= link_to(t('browse.download_xml'), :controller => @type.pluralize, :action => :show) %>
<%= link_to(t('browse.download_xml'), :controller => "api/#{@type.pluralize}", :action => :show) %>
&middot;
<%= link_to(t('browse.view_history'), :action => "#{@type}_history") %>
</div>

View file

@ -8,7 +8,7 @@
<%= render :partial => @type, :collection => @feature.send("old_#{@type}s").reverse %>
<div class='secondary-actions'>
<%= link_to(t('browse.download_xml'), :controller => "old_#{@type.pluralize}", :action => "history") %>
<%= link_to(t('browse.download_xml'), :controller => "api/old_#{@type.pluralize}", :action => "history") %>
&middot;
<%= link_to(t('browse.view_details'), :action => @type) %>
</div>

View file

@ -98,6 +98,28 @@ en:
remote:
name: "Remote Control"
description: "Remote Control (JOSM or Merkaartor)"
api:
notes:
comment:
opened_at_html: "Created %{when} ago"
opened_at_by_html: "Created %{when} ago by %{user}"
commented_at_html: "Updated %{when} ago"
commented_at_by_html: "Updated %{when} ago by %{user}"
closed_at_html: "Resolved %{when} ago"
closed_at_by_html: "Resolved %{when} ago by %{user}"
reopened_at_html: "Reactivated %{when} ago"
reopened_at_by_html: "Reactivated %{when} ago by %{user}"
rss:
title: "OpenStreetMap Notes"
description_area: "A list of notes, reported, commented on or closed in your area [(%{min_lat}|%{min_lon}) -- (%{max_lat}|%{max_lon})]"
description_item: "An rss feed for note %{id}"
opened: "new note (near %{place})"
commented: "new comment (near %{place})"
closed: "closed note (near %{place})"
reopened: "reactivated note (near %{place})"
entry:
comment: Comment
full: Full note
browse:
created: "Created"
closed: "Closed"
@ -2331,26 +2353,6 @@ en:
next: "Next »"
previous: "« Previous"
notes:
comment:
opened_at_html: "Created %{when} ago"
opened_at_by_html: "Created %{when} ago by %{user}"
commented_at_html: "Updated %{when} ago"
commented_at_by_html: "Updated %{when} ago by %{user}"
closed_at_html: "Resolved %{when} ago"
closed_at_by_html: "Resolved %{when} ago by %{user}"
reopened_at_html: "Reactivated %{when} ago"
reopened_at_by_html: "Reactivated %{when} ago by %{user}"
rss:
title: "OpenStreetMap Notes"
description_area: "A list of notes, reported, commented on or closed in your area [(%{min_lat}|%{min_lon}) -- (%{max_lat}|%{max_lon})]"
description_item: "An rss feed for note %{id}"
opened: "new note (near %{place})"
commented: "new comment (near %{place})"
closed: "closed note (near %{place})"
reopened: "reactivated note (near %{place})"
entry:
comment: Comment
full: Full note
mine:
title: "Notes submitted or commented on by %{user}"
heading: "%{user}'s notes"

View file

@ -8,52 +8,52 @@ OpenStreetMap::Application.routes.draw do
get "capabilities" => "api/capabilities#show"
get "permissions" => "api/permissions#show"
put "changeset/create" => "changesets#create"
post "changeset/:id/upload" => "changesets#upload", :id => /\d+/
get "changeset/:id/download" => "changesets#download", :as => :changeset_download, :id => /\d+/
post "changeset/:id/expand_bbox" => "changesets#expand_bbox", :id => /\d+/
get "changeset/:id" => "changesets#show", :as => :changeset_show, :id => /\d+/
post "changeset/:id/subscribe" => "changesets#subscribe", :as => :changeset_subscribe, :id => /\d+/
post "changeset/:id/unsubscribe" => "changesets#unsubscribe", :as => :changeset_unsubscribe, :id => /\d+/
put "changeset/:id" => "changesets#update", :id => /\d+/
put "changeset/:id/close" => "changesets#close", :id => /\d+/
get "changesets" => "changesets#query"
post "changeset/:id/comment" => "changeset_comments#create", :as => :changeset_comment, :id => /\d+/
post "changeset/comment/:id/hide" => "changeset_comments#destroy", :as => :changeset_comment_hide, :id => /\d+/
post "changeset/comment/:id/unhide" => "changeset_comments#restore", :as => :changeset_comment_unhide, :id => /\d+/
put "changeset/create" => "api/changesets#create"
post "changeset/:id/upload" => "api/changesets#upload", :id => /\d+/
get "changeset/:id/download" => "api/changesets#download", :as => :changeset_download, :id => /\d+/
post "changeset/:id/expand_bbox" => "api/changesets#expand_bbox", :id => /\d+/
get "changeset/:id" => "api/changesets#show", :as => :changeset_show, :id => /\d+/
post "changeset/:id/subscribe" => "api/changesets#subscribe", :as => :changeset_subscribe, :id => /\d+/
post "changeset/:id/unsubscribe" => "api/changesets#unsubscribe", :as => :changeset_unsubscribe, :id => /\d+/
put "changeset/:id" => "api/changesets#update", :id => /\d+/
put "changeset/:id/close" => "api/changesets#close", :id => /\d+/
get "changesets" => "api/changesets#query"
post "changeset/:id/comment" => "api/changeset_comments#create", :as => :changeset_comment, :id => /\d+/
post "changeset/comment/:id/hide" => "api/changeset_comments#destroy", :as => :changeset_comment_hide, :id => /\d+/
post "changeset/comment/:id/unhide" => "api/changeset_comments#restore", :as => :changeset_comment_unhide, :id => /\d+/
put "node/create" => "nodes#create"
get "node/:id/ways" => "ways#ways_for_node", :id => /\d+/
get "node/:id/relations" => "relations#relations_for_node", :id => /\d+/
get "node/:id/history" => "old_nodes#history", :id => /\d+/
post "node/:id/:version/redact" => "old_nodes#redact", :version => /\d+/, :id => /\d+/
get "node/:id/:version" => "old_nodes#version", :id => /\d+/, :version => /\d+/
get "node/:id" => "nodes#show", :id => /\d+/
put "node/:id" => "nodes#update", :id => /\d+/
delete "node/:id" => "nodes#delete", :id => /\d+/
get "nodes" => "nodes#index"
put "node/create" => "api/nodes#create"
get "node/:id/ways" => "api/ways#ways_for_node", :id => /\d+/
get "node/:id/relations" => "api/relations#relations_for_node", :id => /\d+/
get "node/:id/history" => "api/old_nodes#history", :id => /\d+/
post "node/:id/:version/redact" => "api/old_nodes#redact", :version => /\d+/, :id => /\d+/
get "node/:id/:version" => "api/old_nodes#version", :id => /\d+/, :version => /\d+/
get "node/:id" => "api/nodes#show", :id => /\d+/
put "node/:id" => "api/nodes#update", :id => /\d+/
delete "node/:id" => "api/nodes#delete", :id => /\d+/
get "nodes" => "api/nodes#index"
put "way/create" => "ways#create"
get "way/:id/history" => "old_ways#history", :id => /\d+/
get "way/:id/full" => "ways#full", :id => /\d+/
get "way/:id/relations" => "relations#relations_for_way", :id => /\d+/
post "way/:id/:version/redact" => "old_ways#redact", :version => /\d+/, :id => /\d+/
get "way/:id/:version" => "old_ways#version", :id => /\d+/, :version => /\d+/
get "way/:id" => "ways#show", :id => /\d+/
put "way/:id" => "ways#update", :id => /\d+/
delete "way/:id" => "ways#delete", :id => /\d+/
get "ways" => "ways#index"
put "way/create" => "api/ways#create"
get "way/:id/history" => "api/old_ways#history", :id => /\d+/
get "way/:id/full" => "api/ways#full", :id => /\d+/
get "way/:id/relations" => "api/relations#relations_for_way", :id => /\d+/
post "way/:id/:version/redact" => "api/old_ways#redact", :version => /\d+/, :id => /\d+/
get "way/:id/:version" => "api/old_ways#version", :id => /\d+/, :version => /\d+/
get "way/:id" => "api/ways#show", :id => /\d+/
put "way/:id" => "api/ways#update", :id => /\d+/
delete "way/:id" => "api/ways#delete", :id => /\d+/
get "ways" => "api/ways#index"
put "relation/create" => "relations#create"
get "relation/:id/relations" => "relations#relations_for_relation", :id => /\d+/
get "relation/:id/history" => "old_relations#history", :id => /\d+/
get "relation/:id/full" => "relations#full", :id => /\d+/
post "relation/:id/:version/redact" => "old_relations#redact", :version => /\d+/, :id => /\d+/
get "relation/:id/:version" => "old_relations#version", :id => /\d+/, :version => /\d+/
get "relation/:id" => "relations#show", :id => /\d+/
put "relation/:id" => "relations#update", :id => /\d+/
delete "relation/:id" => "relations#delete", :id => /\d+/
get "relations" => "relations#index"
put "relation/create" => "api/relations#create"
get "relation/:id/relations" => "api/relations#relations_for_relation", :id => /\d+/
get "relation/:id/history" => "api/old_relations#history", :id => /\d+/
get "relation/:id/full" => "api/relations#full", :id => /\d+/
post "relation/:id/:version/redact" => "api/old_relations#redact", :version => /\d+/, :id => /\d+/
get "relation/:id/:version" => "api/old_relations#version", :id => /\d+/, :version => /\d+/
get "relation/:id" => "api/relations#show", :id => /\d+/
put "relation/:id" => "api/relations#update", :id => /\d+/
delete "relation/:id" => "api/relations#delete", :id => /\d+/
get "relations" => "api/relations#index"
get "map" => "api/map#index"
@ -61,36 +61,36 @@ OpenStreetMap::Application.routes.draw do
get "changes" => "api/changes#index"
get "search" => "search#search_all", :as => "api_search"
get "ways/search" => "search#search_ways"
get "relations/search" => "search#search_relations"
get "nodes/search" => "search#search_nodes"
get "search" => "api/search#search_all", :as => "api_search"
get "ways/search" => "api/search#search_ways"
get "relations/search" => "api/search#search_relations"
get "nodes/search" => "api/search#search_nodes"
get "user/:id" => "users#api_read", :id => /\d+/
get "user/details" => "users#api_details"
get "user/gpx_files" => "users#api_gpx_files"
get "users" => "users#api_users", :as => :api_users
get "user/:id" => "api/users#api_read", :id => /\d+/
get "user/details" => "api/users#api_details"
get "user/gpx_files" => "api/users#api_gpx_files"
get "users" => "api/users#api_users", :as => :api_users
get "user/preferences" => "user_preferences#read"
get "user/preferences/:preference_key" => "user_preferences#read_one"
put "user/preferences" => "user_preferences#update"
put "user/preferences/:preference_key" => "user_preferences#update_one"
delete "user/preferences/:preference_key" => "user_preferences#delete_one"
get "user/preferences" => "api/user_preferences#read"
get "user/preferences/:preference_key" => "api/user_preferences#read_one"
put "user/preferences" => "api/user_preferences#update"
put "user/preferences/:preference_key" => "api/user_preferences#update_one"
delete "user/preferences/:preference_key" => "api/user_preferences#delete_one"
post "gpx/create" => "traces#api_create"
get "gpx/:id" => "traces#api_read", :id => /\d+/
put "gpx/:id" => "traces#api_update", :id => /\d+/
delete "gpx/:id" => "traces#api_delete", :id => /\d+/
get "gpx/:id/details" => "traces#api_read", :id => /\d+/
get "gpx/:id/data" => "traces#api_data"
post "gpx/create" => "api/traces#api_create"
get "gpx/:id" => "api/traces#api_read", :id => /\d+/
put "gpx/:id" => "api/traces#api_update", :id => /\d+/
delete "gpx/:id" => "api/traces#api_delete", :id => /\d+/
get "gpx/:id/details" => "api/traces#api_read", :id => /\d+/
get "gpx/:id/data" => "api/traces#api_data"
# AMF (ActionScript) API
post "amf/read" => "amf#amf_read"
post "amf/write" => "amf#amf_write"
get "swf/trackpoints" => "swf#trackpoints"
post "amf/read" => "api/amf#amf_read"
post "amf/write" => "api/amf#amf_write"
get "swf/trackpoints" => "api/swf#trackpoints"
# Map notes API
resources :notes, :except => [:new, :edit, :update], :constraints => { :id => /\d+/ }, :defaults => { :format => "xml" } do
resources :notes, :except => [:new, :edit, :update], :constraints => { :id => /\d+/ }, :defaults => { :format => "xml" }, :controller => "api/notes" do
collection do
get "search"
get "feed", :defaults => { :format => "rss" }
@ -103,11 +103,11 @@ OpenStreetMap::Application.routes.draw do
end
end
post "notes/addPOIexec" => "notes#create"
post "notes/closePOIexec" => "notes#close"
post "notes/editPOIexec" => "notes#comment"
get "notes/getGPX" => "notes#index", :format => "gpx"
get "notes/getRSSfeed" => "notes#feed", :format => "rss"
post "notes/addPOIexec" => "api/notes#create"
post "notes/closePOIexec" => "api/notes#close"
post "notes/editPOIexec" => "api/notes#comment"
get "notes/getGPX" => "api/notes#index", :format => "gpx"
get "notes/getRSSfeed" => "api/notes#feed", :format => "rss"
end
# Data browsing

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,254 @@
require "test_helper"
module Api
class ChangesetCommentsControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/changeset/1/comment", :method => :post },
{ :controller => "api/changeset_comments", :action => "create", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/changeset/comment/1/hide", :method => :post },
{ :controller => "api/changeset_comments", :action => "destroy", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/changeset/comment/1/unhide", :method => :post },
{ :controller => "api/changeset_comments", :action => "restore", :id => "1" }
)
end
##
# create comment success
def test_create_comment_success
user = create(:user)
user2 = create(:user)
private_user = create(:user, :data_public => false)
suspended_user = create(:user, :suspended)
deleted_user = create(:user, :deleted)
private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
basic_authorization user.email, "test"
assert_difference "ChangesetComment.count", 1 do
assert_no_difference "ActionMailer::Base.deliveries.size" do
perform_enqueued_jobs do
post :create, :params => { :id => private_user_closed_changeset.id, :text => "This is a comment" }
end
end
end
assert_response :success
changeset = create(:changeset, :closed, :user => private_user)
changeset.subscribers.push(private_user)
changeset.subscribers.push(user)
changeset.subscribers.push(suspended_user)
changeset.subscribers.push(deleted_user)
assert_difference "ChangesetComment.count", 1 do
assert_difference "ActionMailer::Base.deliveries.size", 1 do
perform_enqueued_jobs do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
end
end
assert_response :success
email = ActionMailer::Base.deliveries.first
assert_equal 1, email.to.length
assert_equal "[OpenStreetMap] #{user.display_name} has commented on one of your changesets", email.subject
assert_equal private_user.email, email.to.first
ActionMailer::Base.deliveries.clear
basic_authorization user2.email, "test"
assert_difference "ChangesetComment.count", 1 do
assert_difference "ActionMailer::Base.deliveries.size", 2 do
perform_enqueued_jobs do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
end
end
assert_response :success
email = ActionMailer::Base.deliveries.find { |e| e.to.first == private_user.email }
assert_not_nil email
assert_equal 1, email.to.length
assert_equal "[OpenStreetMap] #{user2.display_name} has commented on one of your changesets", email.subject
email = ActionMailer::Base.deliveries.find { |e| e.to.first == user.email }
assert_not_nil email
assert_equal 1, email.to.length
assert_equal "[OpenStreetMap] #{user2.display_name} has commented on a changeset you are interested in", email.subject
ActionMailer::Base.deliveries.clear
end
##
# create comment fail
def test_create_comment_fail
# unauthorized
post :create, :params => { :id => create(:changeset, :closed).id, :text => "This is a comment" }
assert_response :unauthorized
basic_authorization create(:user).email, "test"
# bad changeset id
assert_no_difference "ChangesetComment.count" do
post :create, :params => { :id => 999111, :text => "This is a comment" }
end
assert_response :not_found
# not closed changeset
assert_no_difference "ChangesetComment.count" do
post :create, :params => { :id => create(:changeset).id, :text => "This is a comment" }
end
assert_response :conflict
# no text
assert_no_difference "ChangesetComment.count" do
post :create, :params => { :id => create(:changeset, :closed).id }
end
assert_response :bad_request
# empty text
assert_no_difference "ChangesetComment.count" do
post :create, :params => { :id => create(:changeset, :closed).id, :text => "" }
end
assert_response :bad_request
end
##
# test hide comment fail
def test_destroy_comment_fail
# unauthorized
comment = create(:changeset_comment)
assert_equal true, comment.visible
post :destroy, :params => { :id => comment.id }
assert_response :unauthorized
assert_equal true, comment.reload.visible
basic_authorization create(:user).email, "test"
# not a moderator
post :destroy, :params => { :id => comment.id }
assert_response :forbidden
assert_equal true, comment.reload.visible
basic_authorization create(:moderator_user).email, "test"
# bad comment id
post :destroy, :params => { :id => 999111 }
assert_response :not_found
assert_equal true, comment.reload.visible
end
##
# test hide comment succes
def test_hide_comment_success
comment = create(:changeset_comment)
assert_equal true, comment.visible
basic_authorization create(:moderator_user).email, "test"
post :destroy, :params => { :id => comment.id }
assert_response :success
assert_equal false, comment.reload.visible
end
##
# test unhide comment fail
def test_restore_comment_fail
# unauthorized
comment = create(:changeset_comment, :visible => false)
assert_equal false, comment.visible
post :restore, :params => { :id => comment.id }
assert_response :unauthorized
assert_equal false, comment.reload.visible
basic_authorization create(:user).email, "test"
# not a moderator
post :restore, :params => { :id => comment.id }
assert_response :forbidden
assert_equal false, comment.reload.visible
basic_authorization create(:moderator_user).email, "test"
# bad comment id
post :restore, :params => { :id => 999111 }
assert_response :not_found
assert_equal false, comment.reload.visible
end
##
# test unhide comment succes
def test_unhide_comment_success
comment = create(:changeset_comment, :visible => false)
assert_equal false, comment.visible
basic_authorization create(:moderator_user).email, "test"
post :restore, :params => { :id => comment.id }
assert_response :success
assert_equal true, comment.reload.visible
end
# This test ensures that token capabilities behave correctly for a method that
# requires the terms to have been agreed.
# (This would be better as an integration or system testcase, since the changeset_comment
# create method is simply a stand-in for any method that requires terms agreement.
# But writing oauth tests is hard, and so it's easier to put in a controller test.)
def test_api_write_and_terms_agreed_via_token
user = create(:user, :terms_agreed => nil)
token = create(:access_token, :user => user, :allow_write_api => true)
changeset = create(:changeset, :closed)
# Hack together an oauth request - an alternative would be to sign the request properly
@request.env["oauth.version"] = 1
@request.env["oauth.strategies"] = [:token]
@request.env["oauth.token"] = token
assert_difference "ChangesetComment.count", 0 do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
assert_response :forbidden
# Try again, after agreement with the terms
user.terms_agreed = Time.now
user.save!
assert_difference "ChangesetComment.count", 1 do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
assert_response :success
end
# This test does the same as above, but with basic auth, to similarly test that the
# abilities take into account terms agreement too.
def test_api_write_and_terms_agreed_via_basic_auth
user = create(:user, :terms_agreed => nil)
changeset = create(:changeset, :closed)
basic_authorization user.email, "test"
assert_difference "ChangesetComment.count", 0 do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
assert_response :forbidden
# Try again, after agreement with the terms
user.terms_agreed = Time.now
user.save!
assert_difference "ChangesetComment.count", 1 do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
assert_response :success
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,552 @@
require "test_helper"
module Api
class NodesControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/node/create", :method => :put },
{ :controller => "api/nodes", :action => "create" }
)
assert_routing(
{ :path => "/api/0.6/node/1", :method => :get },
{ :controller => "api/nodes", :action => "show", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/node/1", :method => :put },
{ :controller => "api/nodes", :action => "update", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/node/1", :method => :delete },
{ :controller => "api/nodes", :action => "delete", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/nodes", :method => :get },
{ :controller => "api/nodes", :action => "index" }
)
end
def test_create
private_user = create(:user, :data_public => false)
private_changeset = create(:changeset, :user => private_user)
user = create(:user)
changeset = create(:changeset, :user => user)
# create a node with random lat/lon
lat = rand(-50..50) + rand
lon = rand(-50..50) + rand
## First try with no auth
# create a minimal xml file
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
assert_difference("OldNode.count", 0) do
put :create, :body => xml
end
# hope for unauthorized
assert_response :unauthorized, "node upload did not return unauthorized status"
## Now try with the user which doesn't have their data public
basic_authorization private_user.email, "test"
# create a minimal xml file
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{private_changeset.id}'/></osm>"
assert_difference("Node.count", 0) do
put :create, :body => xml
end
# hope for success
assert_require_public_data "node create did not return forbidden status"
## Now try with the user that has the public data
basic_authorization user.email, "test"
# create a minimal xml file
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
put :create, :body => xml
# hope for success
assert_response :success, "node upload did not return success status"
# read id of created node and search for it
nodeid = @response.body
checknode = Node.find(nodeid)
assert_not_nil checknode, "uploaded node not found in data base after upload"
# compare values
assert_in_delta lat * 10000000, checknode.latitude, 1, "saved node does not match requested latitude"
assert_in_delta lon * 10000000, checknode.longitude, 1, "saved node does not match requested longitude"
assert_equal changeset.id, checknode.changeset_id, "saved node does not belong to changeset that it was created in"
assert_equal true, checknode.visible, "saved node is not visible"
end
def test_create_invalid_xml
## Only test public user here, as test_create should cover what's the forbiddens
## that would occur here
user = create(:user)
changeset = create(:changeset, :user => user)
basic_authorization user.email, "test"
lat = 3.434
lon = 3.23
# test that the upload is rejected when xml is valid, but osm doc isn't
xml = "<create/>"
put :create, :body => xml
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal "Cannot parse valid node from xml string <create/>. XML doesn't contain an osm/node element.", @response.body
# test that the upload is rejected when no lat is supplied
# create a minimal xml file
xml = "<osm><node lon='#{lon}' changeset='#{changeset.id}'/></osm>"
put :create, :body => xml
# hope for success
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal "Cannot parse valid node from xml string <node lon=\"3.23\" changeset=\"#{changeset.id}\"/>. lat missing", @response.body
# test that the upload is rejected when no lon is supplied
# create a minimal xml file
xml = "<osm><node lat='#{lat}' changeset='#{changeset.id}'/></osm>"
put :create, :body => xml
# hope for success
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal "Cannot parse valid node from xml string <node lat=\"3.434\" changeset=\"#{changeset.id}\"/>. lon missing", @response.body
# test that the upload is rejected when lat is non-numeric
# create a minimal xml file
xml = "<osm><node lat='abc' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
put :create, :body => xml
# hope for success
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal "Cannot parse valid node from xml string <node lat=\"abc\" lon=\"#{lon}\" changeset=\"#{changeset.id}\"/>. lat not a number", @response.body
# test that the upload is rejected when lon is non-numeric
# create a minimal xml file
xml = "<osm><node lat='#{lat}' lon='abc' changeset='#{changeset.id}'/></osm>"
put :create, :body => xml
# hope for success
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal "Cannot parse valid node from xml string <node lat=\"#{lat}\" lon=\"abc\" changeset=\"#{changeset.id}\"/>. lon not a number", @response.body
# test that the upload is rejected when we have a tag which is too long
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'><tag k='foo' v='#{'x' * 256}'/></node></osm>"
put :create, :body => xml
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal ["NodeTag ", " v: is too long (maximum is 255 characters) (\"#{'x' * 256}\")"], @response.body.split(/[0-9]+,foo:/)
end
def test_show
# check that a visible node is returned properly
get :show, :params => { :id => create(:node).id }
assert_response :success
# check that an deleted node is not returned
get :show, :params => { :id => create(:node, :deleted).id }
assert_response :gone
# check chat a non-existent node is not returned
get :show, :params => { :id => 0 }
assert_response :not_found
end
# this tests deletion restrictions - basic deletion is tested in the unit
# tests for node!
def test_delete
private_user = create(:user, :data_public => false)
private_user_changeset = create(:changeset, :user => private_user)
private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
private_node = create(:node, :changeset => private_user_changeset)
private_deleted_node = create(:node, :deleted, :changeset => private_user_changeset)
## first try to delete node without auth
delete :delete, :params => { :id => private_node.id }
assert_response :unauthorized
## now set auth for the non-data public user
basic_authorization private_user.email, "test"
# try to delete with an invalid (closed) changeset
xml = update_changeset(private_node.to_xml, private_user_closed_changeset.id)
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data("non-public user shouldn't be able to delete node")
# try to delete with an invalid (non-existent) changeset
xml = update_changeset(private_node.to_xml, 0)
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data("shouldn't be able to delete node, when user's data is private")
# valid delete now takes a payload
xml = private_node.to_xml
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data("shouldn't be able to delete node when user's data isn't public'")
# this won't work since the node is already deleted
xml = private_deleted_node.to_xml
delete :delete, :params => { :id => private_deleted_node.id }, :body => xml.to_s
assert_require_public_data
# this won't work since the node never existed
delete :delete, :params => { :id => 0 }
assert_require_public_data
## these test whether nodes which are in-use can be deleted:
# in a way...
private_used_node = create(:node, :changeset => private_user_changeset)
create(:way_node, :node => private_used_node)
xml = private_used_node.to_xml
delete :delete, :params => { :id => private_used_node.id }, :body => xml.to_s
assert_require_public_data "shouldn't be able to delete a node used in a way (#{@response.body})"
# in a relation...
private_used_node2 = create(:node, :changeset => private_user_changeset)
create(:relation_member, :member => private_used_node2)
xml = private_used_node2.to_xml
delete :delete, :params => { :id => private_used_node2.id }, :body => xml.to_s
assert_require_public_data "shouldn't be able to delete a node used in a relation (#{@response.body})"
## now setup for the public data user
user = create(:user, :data_public => true)
changeset = create(:changeset, :user => user)
closed_changeset = create(:changeset, :closed, :user => user)
node = create(:node, :changeset => changeset)
basic_authorization user.email, "test"
# try to delete with an invalid (closed) changeset
xml = update_changeset(node.to_xml, closed_changeset.id)
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict
# try to delete with an invalid (non-existent) changeset
xml = update_changeset(node.to_xml, 0)
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict
# try to delete a node with a different ID
other_node = create(:node)
xml = other_node.to_xml
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to delete a node with a different ID from the XML"
# try to delete a node rubbish in the payloads
xml = "<delete/>"
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to delete a node without a valid XML payload"
# valid delete now takes a payload
xml = node.to_xml
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :success
# valid delete should return the new version number, which should
# be greater than the old version number
assert @response.body.to_i > node.version,
"delete request should return a new version number for node"
# deleting the same node twice doesn't work
xml = node.to_xml
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :gone
# this won't work since the node never existed
delete :delete, :params => { :id => 0 }
assert_response :not_found
## these test whether nodes which are in-use can be deleted:
# in a way...
used_node = create(:node, :changeset => create(:changeset, :user => user))
way_node = create(:way_node, :node => used_node)
way_node2 = create(:way_node, :node => used_node)
xml = used_node.to_xml
delete :delete, :params => { :id => used_node.id }, :body => xml.to_s
assert_response :precondition_failed,
"shouldn't be able to delete a node used in a way (#{@response.body})"
assert_equal "Precondition failed: Node #{used_node.id} is still used by ways #{way_node.way.id},#{way_node2.way.id}.", @response.body
# in a relation...
used_node2 = create(:node, :changeset => create(:changeset, :user => user))
relation_member = create(:relation_member, :member => used_node2)
relation_member2 = create(:relation_member, :member => used_node2)
xml = used_node2.to_xml
delete :delete, :params => { :id => used_node2.id }, :body => xml.to_s
assert_response :precondition_failed,
"shouldn't be able to delete a node used in a relation (#{@response.body})"
assert_equal "Precondition failed: Node #{used_node2.id} is still used by relations #{relation_member.relation.id},#{relation_member2.relation.id}.", @response.body
end
##
# tests whether the API works and prevents incorrect use while trying
# to update nodes.
def test_update
## First test with no user credentials
# try and update a node without authorisation
# first try to delete node without auth
private_user = create(:user, :data_public => false)
private_node = create(:node, :changeset => create(:changeset, :user => private_user))
user = create(:user)
node = create(:node, :changeset => create(:changeset, :user => user))
xml = node.to_xml
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :unauthorized
## Second test with the private user
# setup auth
basic_authorization private_user.email, "test"
## trying to break changesets
# try and update in someone else's changeset
xml = update_changeset(private_node.to_xml,
create(:changeset).id)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "update with other user's changeset should be forbidden when data isn't public"
# try and update in a closed changeset
xml = update_changeset(private_node.to_xml,
create(:changeset, :closed, :user => private_user).id)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
# try and update in a non-existant changeset
xml = update_changeset(private_node.to_xml, 0)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "update with changeset=0 should be forbidden, when data isn't public"
## try and submit invalid updates
xml = xml_attr_rewrite(private_node.to_xml, "lat", 91.0)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "node at lat=91 should be forbidden, when data isn't public"
xml = xml_attr_rewrite(private_node.to_xml, "lat", -91.0)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "node at lat=-91 should be forbidden, when data isn't public"
xml = xml_attr_rewrite(private_node.to_xml, "lon", 181.0)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "node at lon=181 should be forbidden, when data isn't public"
xml = xml_attr_rewrite(private_node.to_xml, "lon", -181.0)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "node at lon=-181 should be forbidden, when data isn't public"
## finally, produce a good request which still won't work
xml = private_node.to_xml
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "should have failed with a forbidden when data isn't public"
## Finally test with the public user
# try and update a node without authorisation
# first try to update node without auth
xml = node.to_xml
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :forbidden
# setup auth
basic_authorization user.email, "test"
## trying to break changesets
# try and update in someone else's changeset
xml = update_changeset(node.to_xml,
create(:changeset).id)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict, "update with other user's changeset should be rejected"
# try and update in a closed changeset
xml = update_changeset(node.to_xml,
create(:changeset, :closed, :user => user).id)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict, "update with closed changeset should be rejected"
# try and update in a non-existant changeset
xml = update_changeset(node.to_xml, 0)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict, "update with changeset=0 should be rejected"
## try and submit invalid updates
xml = xml_attr_rewrite(node.to_xml, "lat", 91.0)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request, "node at lat=91 should be rejected"
xml = xml_attr_rewrite(node.to_xml, "lat", -91.0)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request, "node at lat=-91 should be rejected"
xml = xml_attr_rewrite(node.to_xml, "lon", 181.0)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request, "node at lon=181 should be rejected"
xml = xml_attr_rewrite(node.to_xml, "lon", -181.0)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request, "node at lon=-181 should be rejected"
## next, attack the versioning
current_node_version = node.version
# try and submit a version behind
xml = xml_attr_rewrite(node.to_xml,
"version", current_node_version - 1)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict, "should have failed on old version number"
# try and submit a version ahead
xml = xml_attr_rewrite(node.to_xml,
"version", current_node_version + 1)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict, "should have failed on skipped version number"
# try and submit total crap in the version field
xml = xml_attr_rewrite(node.to_xml,
"version", "p1r4t3s!")
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict,
"should not be able to put 'p1r4at3s!' in the version field"
## try an update with the wrong ID
xml = create(:node).to_xml
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to update a node with a different ID from the XML"
## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
xml = "<update/>"
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to update a node with non-OSM XML doc."
## finally, produce a good request which should work
xml = node.to_xml
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :success, "a valid update request failed"
end
##
# test fetching multiple nodes
def test_index
node1 = create(:node)
node2 = create(:node, :deleted)
node3 = create(:node)
node4 = create(:node, :with_history, :version => 2)
node5 = create(:node, :deleted, :with_history, :version => 2)
# check error when no parameter provided
get :index
assert_response :bad_request
# check error when no parameter value provided
get :index, :params => { :nodes => "" }
assert_response :bad_request
# test a working call
get :index, :params => { :nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id}" }
assert_response :success
assert_select "osm" do
assert_select "node", :count => 5
assert_select "node[id='#{node1.id}'][visible='true']", :count => 1
assert_select "node[id='#{node2.id}'][visible='false']", :count => 1
assert_select "node[id='#{node3.id}'][visible='true']", :count => 1
assert_select "node[id='#{node4.id}'][visible='true']", :count => 1
assert_select "node[id='#{node5.id}'][visible='false']", :count => 1
end
# check error when a non-existent node is included
get :index, :params => { :nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id},0" }
assert_response :not_found
end
##
# test adding tags to a node
def test_duplicate_tags
existing_tag = create(:node_tag)
assert_equal true, existing_tag.node.changeset.user.data_public
# setup auth
basic_authorization existing_tag.node.changeset.user.email, "test"
# add an identical tag to the node
tag_xml = XML::Node.new("tag")
tag_xml["k"] = existing_tag.k
tag_xml["v"] = existing_tag.v
# add the tag into the existing xml
node_xml = existing_tag.node.to_xml
node_xml.find("//osm/node").first << tag_xml
# try and upload it
put :update, :params => { :id => existing_tag.node.id }, :body => node_xml.to_s
assert_response :bad_request,
"adding duplicate tags to a node should fail with 'bad request'"
assert_equal "Element node/#{existing_tag.node.id} has duplicate tags with key #{existing_tag.k}", @response.body
end
# test whether string injection is possible
def test_string_injection
private_user = create(:user, :data_public => false)
private_changeset = create(:changeset, :user => private_user)
user = create(:user)
changeset = create(:changeset, :user => user)
## First try with the non-data public user
basic_authorization private_user.email, "test"
# try and put something into a string that the API might
# use unquoted and therefore allow code injection...
xml = "<osm><node lat='0' lon='0' changeset='#{private_changeset.id}'>" \
'<tag k="#{@user.inspect}" v="0"/>' \
"</node></osm>"
put :create, :body => xml
assert_require_public_data "Shouldn't be able to create with non-public user"
## Then try with the public data user
basic_authorization user.email, "test"
# try and put something into a string that the API might
# use unquoted and therefore allow code injection...
xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'>" \
'<tag k="#{@user.inspect}" v="0"/>' \
"</node></osm>"
put :create, :body => xml
assert_response :success
nodeid = @response.body
# find the node in the database
checknode = Node.find(nodeid)
assert_not_nil checknode, "node not found in data base after upload"
# and grab it using the api
get :show, :params => { :id => nodeid }
assert_response :success
apinode = Node.from_xml(@response.body)
assert_not_nil apinode, "downloaded node is nil, but shouldn't be"
# check the tags are not corrupted
assert_equal checknode.tags, apinode.tags
assert apinode.tags.include?("\#{@user.inspect}")
end
##
# update the changeset_id of a node element
def update_changeset(xml, changeset_id)
xml_attr_rewrite(xml, "changeset", changeset_id)
end
##
# update an attribute in the node element
def xml_attr_rewrite(xml, name, value)
xml.find("//osm/node").first[name] = value.to_s
xml
end
##
# parse some xml
def xml_parse(xml)
parser = XML::Parser.string(xml)
parser.parse
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,430 @@
require "test_helper"
module Api
class OldNodesControllerTest < ActionController::TestCase
#
# TODO: test history
#
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/node/1/history", :method => :get },
{ :controller => "api/old_nodes", :action => "history", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/node/1/2", :method => :get },
{ :controller => "api/old_nodes", :action => "version", :id => "1", :version => "2" }
)
assert_routing(
{ :path => "/api/0.6/node/1/2/redact", :method => :post },
{ :controller => "api/old_nodes", :action => "redact", :id => "1", :version => "2" }
)
end
##
# test the version call by submitting several revisions of a new node
# to the API and ensuring that later calls to version return the
# matching versions of the object.
#
##
# FIXME: Move this test to being an integration test since it spans multiple controllers
def test_version
private_user = create(:user, :data_public => false)
private_node = create(:node, :with_history, :version => 4, :changeset => create(:changeset, :user => private_user))
user = create(:user)
node = create(:node, :with_history, :version => 4, :changeset => create(:changeset, :user => user))
create_list(:node_tag, 2, :node => node)
# Ensure that the current tags are propagated to the history too
propagate_tags(node, node.old_nodes.last)
## First try this with a non-public user
basic_authorization private_user.email, "test"
# setup a simple XML node
xml_doc = private_node.to_xml
xml_node = xml_doc.find("//osm/node").first
nodeid = private_node.id
# keep a hash of the versions => string, as we'll need something
# to test against later
versions = {}
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
# randomly move the node about
3.times do
# move the node somewhere else
xml_node["lat"] = precision(rand * 180 - 90).to_s
xml_node["lon"] = precision(rand * 360 - 180).to_s
with_controller(NodesController.new) do
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
assert_response :forbidden, "Should have rejected node update"
xml_node["version"] = @response.body.to_s
end
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
end
# add a bunch of random tags
3.times do
xml_tag = XML::Node.new("tag")
xml_tag["k"] = random_string
xml_tag["v"] = random_string
xml_node << xml_tag
with_controller(NodesController.new) do
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
assert_response :forbidden,
"should have rejected node #{nodeid} (#{@response.body}) with forbidden"
xml_node["version"] = @response.body.to_s
end
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
end
# probably should check that they didn't get written to the database
## Now do it with the public user
basic_authorization user.email, "test"
# setup a simple XML node
xml_doc = node.to_xml
xml_node = xml_doc.find("//osm/node").first
nodeid = node.id
# keep a hash of the versions => string, as we'll need something
# to test against later
versions = {}
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
# randomly move the node about
3.times do
# move the node somewhere else
xml_node["lat"] = precision(rand * 180 - 90).to_s
xml_node["lon"] = precision(rand * 360 - 180).to_s
with_controller(NodesController.new) do
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
assert_response :success
xml_node["version"] = @response.body.to_s
end
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
end
# add a bunch of random tags
3.times do
xml_tag = XML::Node.new("tag")
xml_tag["k"] = random_string
xml_tag["v"] = random_string
xml_node << xml_tag
with_controller(NodesController.new) do
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
assert_response :success,
"couldn't update node #{nodeid} (#{@response.body})"
xml_node["version"] = @response.body.to_s
end
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
end
# check all the versions
versions.each_key do |key|
get :version, :params => { :id => nodeid, :version => key.to_i }
assert_response :success,
"couldn't get version #{key.to_i} of node #{nodeid}"
check_node = Node.from_xml(versions[key])
api_node = Node.from_xml(@response.body.to_s)
assert_nodes_are_equal check_node, api_node
end
end
def test_not_found_version
check_not_found_id_version(70000, 312344)
check_not_found_id_version(-1, -13)
check_not_found_id_version(create(:node).id, 24354)
check_not_found_id_version(24356, create(:node).version)
end
def check_not_found_id_version(id, version)
get :version, :params => { :id => id, :version => version }
assert_response :not_found
rescue ActionController::UrlGenerationError => ex
assert_match(/No route matches/, ex.to_s)
end
##
# Test that getting the current version is identical to picking
# that version with the version URI call.
def test_current_version
node = create(:node, :with_history)
used_node = create(:node, :with_history)
create(:way_node, :node => used_node)
node_used_by_relationship = create(:node, :with_history)
create(:relation_member, :member => node_used_by_relationship)
node_with_versions = create(:node, :with_history, :version => 4)
create(:node_tag, :node => node)
create(:node_tag, :node => used_node)
create(:node_tag, :node => node_used_by_relationship)
create(:node_tag, :node => node_with_versions)
propagate_tags(node, node.old_nodes.last)
propagate_tags(used_node, used_node.old_nodes.last)
propagate_tags(node_used_by_relationship, node_used_by_relationship.old_nodes.last)
propagate_tags(node_with_versions, node_with_versions.old_nodes.last)
check_current_version(node)
check_current_version(used_node)
check_current_version(node_used_by_relationship)
check_current_version(node_with_versions)
end
##
# test the redaction of an old version of a node, while not being
# authorised.
def test_redact_node_unauthorised
node = create(:node, :with_history, :version => 4)
node_v3 = node.old_nodes.find_by(:version => 3)
do_redact_node(node_v3,
create(:redaction))
assert_response :unauthorized, "should need to be authenticated to redact."
end
##
# test the redaction of an old version of a node, while being
# authorised as a normal user.
def test_redact_node_normal_user
basic_authorization create(:user).email, "test"
node = create(:node, :with_history, :version => 4)
node_v3 = node.old_nodes.find_by(:version => 3)
do_redact_node(node_v3,
create(:redaction))
assert_response :forbidden, "should need to be moderator to redact."
end
##
# test that, even as moderator, the current version of a node
# can't be redacted.
def test_redact_node_current_version
basic_authorization create(:moderator_user).email, "test"
node = create(:node, :with_history, :version => 4)
node_v4 = node.old_nodes.find_by(:version => 4)
do_redact_node(node_v4,
create(:redaction))
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
end
##
# test that redacted nodes aren't visible, regardless of
# authorisation except as moderator...
def test_version_redacted
node = create(:node, :with_history, :version => 2)
node_v1 = node.old_nodes.find_by(:version => 1)
node_v1.redact!(create(:redaction))
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :forbidden, "Redacted node shouldn't be visible via the version API, even when logged in."
end
##
# test that redacted nodes aren't visible in the history
def test_history_redacted
node = create(:node, :with_history, :version => 2)
node_v1 = node.old_nodes.find_by(:version => 1)
node_v1.redact!(create(:redaction))
get :history, :params => { :id => node_v1.node_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 0, "redacted node #{node_v1.node_id} version #{node_v1.version} shouldn't be present in the history."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :history, :params => { :id => node_v1.node_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 0, "redacted node #{node_v1.node_id} version #{node_v1.version} shouldn't be present in the history, even when logged in."
end
##
# test the redaction of an old version of a node, while being
# authorised as a moderator.
def test_redact_node_moderator
node = create(:node, :with_history, :version => 4)
node_v3 = node.old_nodes.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_node(node_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# check moderator can still see the redacted data, when passing
# the appropriate flag
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version }
assert_response :forbidden, "After redaction, node should be gone for moderator, when flag not passed."
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version, :show_redactions => "true" }
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
# and when accessed via history
get :history, :params => { :id => node_v3.node_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 0, "node #{node_v3.node_id} version #{node_v3.version} should not be present in the history for moderators when not passing flag."
get :history, :params => { :id => node_v3.node_id, :show_redactions => "true" }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 1, "node #{node_v3.node_id} version #{node_v3.version} should still be present in the history for moderators when passing flag."
end
# testing that if the moderator drops auth, he can't see the
# redacted stuff any more.
def test_redact_node_is_redacted
node = create(:node, :with_history, :version => 4)
node_v3 = node.old_nodes.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_node(node_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# re-auth as non-moderator
basic_authorization create(:user).email, "test"
# check can't see the redacted data
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version }
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
# and when accessed via history
get :history, :params => { :id => node_v3.node_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 0, "redacted node #{node_v3.node_id} version #{node_v3.version} shouldn't be present in the history."
end
##
# test the unredaction of an old version of a node, while not being
# authorised.
def test_unredact_node_unauthorised
node = create(:node, :with_history, :version => 2)
node_v1 = node.old_nodes.find_by(:version => 1)
node_v1.redact!(create(:redaction))
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :unauthorized, "should need to be authenticated to unredact."
end
##
# test the unredaction of an old version of a node, while being
# authorised as a normal user.
def test_unredact_node_normal_user
user = create(:user)
node = create(:node, :with_history, :version => 2)
node_v1 = node.old_nodes.find_by(:version => 1)
node_v1.redact!(create(:redaction))
basic_authorization user.email, "test"
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :forbidden, "should need to be moderator to unredact."
end
##
# test the unredaction of an old version of a node, while being
# authorised as a moderator.
def test_unredact_node_moderator
moderator_user = create(:moderator_user)
node = create(:node, :with_history, :version => 2)
node_v1 = node.old_nodes.find_by(:version => 1)
node_v1.redact!(create(:redaction))
basic_authorization moderator_user.email, "test"
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :success, "should be OK to unredact old version as moderator."
# check moderator can now see the redacted data, when not
# passing the aspecial flag
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :success, "After unredaction, node should not be gone for moderator."
# and when accessed via history
get :history, :params => { :id => node_v1.node_id }
assert_response :success, "Unredaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 1, "node #{node_v1.node_id} version #{node_v1.version} should now be present in the history for moderators without passing flag."
basic_authorization create(:user).email, "test"
# check normal user can now see the redacted data
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :success, "After unredaction, node should be visible to normal users."
# and when accessed via history
get :history, :params => { :id => node_v1.node_id }
assert_response :success, "Unredaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 1, "node #{node_v1.node_id} version #{node_v1.version} should now be present in the history for normal users without passing flag."
end
private
def do_redact_node(node, redaction)
get :version, :params => { :id => node.node_id, :version => node.version }
assert_response :success, "should be able to get version #{node.version} of node #{node.node_id}."
# now redact it
post :redact, :params => { :id => node.node_id, :version => node.version, :redaction => redaction.id }
end
def check_current_version(node_id)
# get the current version of the node
current_node = with_controller(NodesController.new) do
get :show, :params => { :id => node_id }
assert_response :success, "cant get current node #{node_id}"
Node.from_xml(@response.body)
end
assert_not_nil current_node, "getting node #{node_id} returned nil"
# get the "old" version of the node from the old_node interface
get :version, :params => { :id => node_id, :version => current_node.version }
assert_response :success, "cant get old node #{node_id}, v#{current_node.version}"
old_node = Node.from_xml(@response.body)
# check the nodes are the same
assert_nodes_are_equal current_node, old_node
end
##
# returns a 16 character long string with some nasty characters in it.
# this ought to stress-test the tag handling as well as the versioning.
def random_string
letters = [["!", '"', "$", "&", ";", "@"],
("a".."z").to_a,
("A".."Z").to_a,
("0".."9").to_a].flatten
(1..16).map { |_i| letters[rand(letters.length)] }.join
end
##
# truncate a floating point number to the scale that it is stored in
# the database. otherwise rounding errors can produce failing unit
# tests when they shouldn't.
def precision(f)
(f * GeoRecord::SCALE).round.to_f / GeoRecord::SCALE
end
def propagate_tags(node, old_node)
node.tags.each do |k, v|
create(:old_node_tag, :old_node => old_node, :k => k, :v => v)
end
end
end
end

View file

@ -0,0 +1,274 @@
require "test_helper"
module Api
class OldRelationsControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/relation/1/history", :method => :get },
{ :controller => "api/old_relations", :action => "history", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/relation/1/2", :method => :get },
{ :controller => "api/old_relations", :action => "version", :id => "1", :version => "2" }
)
assert_routing(
{ :path => "/api/0.6/relation/1/2/redact", :method => :post },
{ :controller => "api/old_relations", :action => "redact", :id => "1", :version => "2" }
)
end
# -------------------------------------
# Test reading old relations.
# -------------------------------------
def test_history
# check that a visible relations is returned properly
get :history, :params => { :id => create(:relation, :with_history).id }
assert_response :success
# check chat a non-existent relations is not returned
get :history, :params => { :id => 0 }
assert_response :not_found
end
##
# test the redaction of an old version of a relation, while not being
# authorised.
def test_redact_relation_unauthorised
relation = create(:relation, :with_history, :version => 4)
relation_v3 = relation.old_relations.find_by(:version => 3)
do_redact_relation(relation_v3, create(:redaction))
assert_response :unauthorized, "should need to be authenticated to redact."
end
##
# test the redaction of an old version of a relation, while being
# authorised as a normal user.
def test_redact_relation_normal_user
relation = create(:relation, :with_history, :version => 4)
relation_v3 = relation.old_relations.find_by(:version => 3)
basic_authorization create(:user).email, "test"
do_redact_relation(relation_v3, create(:redaction))
assert_response :forbidden, "should need to be moderator to redact."
end
##
# test that, even as moderator, the current version of a relation
# can't be redacted.
def test_redact_relation_current_version
relation = create(:relation, :with_history, :version => 4)
relation_latest = relation.old_relations.last
basic_authorization create(:moderator_user).email, "test"
do_redact_relation(relation_latest, create(:redaction))
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
end
##
# test that redacted relations aren't visible, regardless of
# authorisation except as moderator...
def test_version_redacted
relation = create(:relation, :with_history, :version => 2)
relation_v1 = relation.old_relations.find_by(:version => 1)
relation_v1.redact!(create(:redaction))
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API, even when logged in."
end
##
# test that redacted relations aren't visible in the history
def test_history_redacted
relation = create(:relation, :with_history, :version => 2)
relation_v1 = relation.old_relations.find_by(:version => 1)
relation_v1.redact!(create(:redaction))
get :history, :params => { :id => relation_v1.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 0, "redacted relation #{relation_v1.relation_id} version #{relation_v1.version} shouldn't be present in the history."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
get :history, :params => { :id => relation_v1.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 0, "redacted relation #{relation_v1.relation_id} version #{relation_v1.version} shouldn't be present in the history, even when logged in."
end
##
# test the redaction of an old version of a relation, while being
# authorised as a moderator.
def test_redact_relation_moderator
relation = create(:relation, :with_history, :version => 4)
relation_v3 = relation.old_relations.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_relation(relation_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# check moderator can still see the redacted data, when passing
# the appropriate flag
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version }
assert_response :forbidden, "After redaction, relation should be gone for moderator, when flag not passed."
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version, :show_redactions => "true" }
assert_response :success, "After redaction, relation should not be gone for moderator, when flag passed."
# and when accessed via history
get :history, :params => { :id => relation_v3.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 0, "relation #{relation_v3.relation_id} version #{relation_v3.version} should not be present in the history for moderators when not passing flag."
get :history, :params => { :id => relation_v3.relation_id, :show_redactions => "true" }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 1, "relation #{relation_v3.relation_id} version #{relation_v3.version} should still be present in the history for moderators when passing flag."
end
# testing that if the moderator drops auth, he can't see the
# redacted stuff any more.
def test_redact_relation_is_redacted
relation = create(:relation, :with_history, :version => 4)
relation_v3 = relation.old_relations.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_relation(relation_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# re-auth as non-moderator
basic_authorization create(:user).email, "test"
# check can't see the redacted data
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version }
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API."
# and when accessed via history
get :history, :params => { :id => relation_v3.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 0, "redacted relation #{relation_v3.relation_id} version #{relation_v3.version} shouldn't be present in the history."
end
##
# test the unredaction of an old version of a relation, while not being
# authorised.
def test_unredact_relation_unauthorised
relation = create(:relation, :with_history, :version => 2)
relation_v1 = relation.old_relations.find_by(:version => 1)
relation_v1.redact!(create(:redaction))
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :unauthorized, "should need to be authenticated to unredact."
end
##
# test the unredaction of an old version of a relation, while being
# authorised as a normal user.
def test_unredact_relation_normal_user
relation = create(:relation, :with_history, :version => 2)
relation_v1 = relation.old_relations.find_by(:version => 1)
relation_v1.redact!(create(:redaction))
basic_authorization create(:user).email, "test"
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :forbidden, "should need to be moderator to unredact."
end
##
# test the unredaction of an old version of a relation, while being
# authorised as a moderator.
def test_unredact_relation_moderator
relation = create(:relation, :with_history, :version => 2)
relation_v1 = relation.old_relations.find_by(:version => 1)
relation_v1.redact!(create(:redaction))
basic_authorization create(:moderator_user).email, "test"
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :success, "should be OK to unredact old version as moderator."
# check moderator can still see the redacted data, without passing
# the appropriate flag
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :success, "After unredaction, relation should not be gone for moderator."
# and when accessed via history
get :history, :params => { :id => relation_v1.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 1, "relation #{relation_v1.relation_id} version #{relation_v1.version} should still be present in the history for moderators."
basic_authorization create(:user).email, "test"
# check normal user can now see the redacted data
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :success, "After redaction, node should not be gone for normal user."
# and when accessed via history
get :history, :params => { :id => relation_v1.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 1, "relation #{relation_v1.relation_id} version #{relation_v1.version} should still be present in the history for normal users."
end
private
##
# check that the current version of a relation is equivalent to the
# version which we're getting from the versions call.
def check_current_version(relation_id)
# get the current version
current_relation = with_controller(RelationsController.new) do
get :show, :params => { :id => relation_id }
assert_response :success, "can't get current relation #{relation_id}"
Relation.from_xml(@response.body)
end
assert_not_nil current_relation, "getting relation #{relation_id} returned nil"
# get the "old" version of the relation from the version method
get :version, :params => { :id => relation_id, :version => current_relation.version }
assert_response :success, "can't get old relation #{relation_id}, v#{current_relation.version}"
old_relation = Relation.from_xml(@response.body)
# check that the relations are identical
assert_relations_are_equal current_relation, old_relation
end
##
# look at all the versions of the relation in the history and get each version from
# the versions call. check that they're the same.
def check_history_equals_versions(relation_id)
get :history, :params => { :id => relation_id }
assert_response :success, "can't get relation #{relation_id} from API"
history_doc = XML::Parser.string(@response.body).parse
assert_not_nil history_doc, "parsing relation #{relation_id} history failed"
history_doc.find("//osm/relation").each do |relation_doc|
history_relation = Relation.from_xml_node(relation_doc)
assert_not_nil history_relation, "parsing relation #{relation_id} version failed"
get :version, :params => { :id => relation_id, :version => history_relation.version }
assert_response :success, "couldn't get relation #{relation_id}, v#{history_relation.version}"
version_relation = Relation.from_xml(@response.body)
assert_not_nil version_relation, "failed to parse #{relation_id}, v#{history_relation.version}"
assert_relations_are_equal history_relation, version_relation
end
end
def do_redact_relation(relation, redaction)
get :version, :params => { :id => relation.relation_id, :version => relation.version }
assert_response :success, "should be able to get version #{relation.version} of relation #{relation.relation_id}."
# now redact it
post :redact, :params => { :id => relation.relation_id, :version => relation.version, :redaction => redaction.id }
end
end
end

View file

@ -0,0 +1,320 @@
require "test_helper"
module Api
class OldWaysControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/way/1/history", :method => :get },
{ :controller => "api/old_ways", :action => "history", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/way/1/2", :method => :get },
{ :controller => "api/old_ways", :action => "version", :id => "1", :version => "2" }
)
assert_routing(
{ :path => "/api/0.6/way/1/2/redact", :method => :post },
{ :controller => "api/old_ways", :action => "redact", :id => "1", :version => "2" }
)
end
# -------------------------------------
# Test reading old ways.
# -------------------------------------
def test_history_visible
# check that a visible way is returned properly
get :history, :params => { :id => create(:way, :with_history).id }
assert_response :success
end
def test_history_invisible
# check that an invisible way's history is returned properly
get :history, :params => { :id => create(:way, :with_history, :deleted).id }
assert_response :success
end
def test_history_invalid
# check chat a non-existent way is not returned
get :history, :params => { :id => 0 }
assert_response :not_found
end
##
# check that we can retrieve versions of a way
def test_version
way = create(:way, :with_history)
used_way = create(:way, :with_history)
create(:relation_member, :member => used_way)
way_with_versions = create(:way, :with_history, :version => 4)
create(:way_tag, :way => way)
create(:way_tag, :way => used_way)
create(:way_tag, :way => way_with_versions)
propagate_tags(way, way.old_ways.last)
propagate_tags(used_way, used_way.old_ways.last)
propagate_tags(way_with_versions, way_with_versions.old_ways.last)
check_current_version(way.id)
check_current_version(used_way.id)
check_current_version(way_with_versions.id)
end
##
# check that returned history is the same as getting all
# versions of a way from the api.
def test_history_equals_versions
way = create(:way, :with_history)
used_way = create(:way, :with_history)
create(:relation_member, :member => used_way)
way_with_versions = create(:way, :with_history, :version => 4)
check_history_equals_versions(way.id)
check_history_equals_versions(used_way.id)
check_history_equals_versions(way_with_versions.id)
end
##
# test the redaction of an old version of a way, while not being
# authorised.
def test_redact_way_unauthorised
way = create(:way, :with_history, :version => 4)
way_v3 = way.old_ways.find_by(:version => 3)
do_redact_way(way_v3, create(:redaction))
assert_response :unauthorized, "should need to be authenticated to redact."
end
##
# test the redaction of an old version of a way, while being
# authorised as a normal user.
def test_redact_way_normal_user
basic_authorization create(:user).email, "test"
way = create(:way, :with_history, :version => 4)
way_v3 = way.old_ways.find_by(:version => 3)
do_redact_way(way_v3, create(:redaction))
assert_response :forbidden, "should need to be moderator to redact."
end
##
# test that, even as moderator, the current version of a way
# can't be redacted.
def test_redact_way_current_version
basic_authorization create(:moderator_user).email, "test"
way = create(:way, :with_history, :version => 4)
way_latest = way.old_ways.last
do_redact_way(way_latest, create(:redaction))
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
end
##
# test that redacted ways aren't visible, regardless of
# authorisation except as moderator...
def test_version_redacted
way = create(:way, :with_history, :version => 2)
way_v1 = way.old_ways.find_by(:version => 1)
way_v1.redact!(create(:redaction))
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :forbidden, "Redacted way shouldn't be visible via the version API."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :forbidden, "Redacted way shouldn't be visible via the version API, even when logged in."
end
##
# test that redacted ways aren't visible in the history
def test_history_redacted
way = create(:way, :with_history, :version => 2)
way_v1 = way.old_ways.find_by(:version => 1)
way_v1.redact!(create(:redaction))
get :history, :params => { :id => way_v1.way_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 0, "redacted way #{way_v1.way_id} version #{way_v1.version} shouldn't be present in the history."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
get :history, :params => { :id => way_v1.way_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 0, "redacted node #{way_v1.way_id} version #{way_v1.version} shouldn't be present in the history, even when logged in."
end
##
# test the redaction of an old version of a way, while being
# authorised as a moderator.
def test_redact_way_moderator
way = create(:way, :with_history, :version => 4)
way_v3 = way.old_ways.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_way(way_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# check moderator can still see the redacted data, when passing
# the appropriate flag
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version }
assert_response :forbidden, "After redaction, node should be gone for moderator, when flag not passed."
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version, :show_redactions => "true" }
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
# and when accessed via history
get :history, :params => { :id => way_v3.way_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 0, "way #{way_v3.way_id} version #{way_v3.version} should not be present in the history for moderators when not passing flag."
get :history, :params => { :id => way_v3.way_id, :show_redactions => "true" }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 1, "way #{way_v3.way_id} version #{way_v3.version} should still be present in the history for moderators when passing flag."
end
# testing that if the moderator drops auth, he can't see the
# redacted stuff any more.
def test_redact_way_is_redacted
way = create(:way, :with_history, :version => 4)
way_v3 = way.old_ways.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_way(way_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# re-auth as non-moderator
basic_authorization create(:user).email, "test"
# check can't see the redacted data
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version }
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
# and when accessed via history
get :history, :params => { :id => way_v3.way_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 0, "redacted way #{way_v3.way_id} version #{way_v3.version} shouldn't be present in the history."
end
##
# test the unredaction of an old version of a way, while not being
# authorised.
def test_unredact_way_unauthorised
way = create(:way, :with_history, :version => 2)
way_v1 = way.old_ways.find_by(:version => 1)
way_v1.redact!(create(:redaction))
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :unauthorized, "should need to be authenticated to unredact."
end
##
# test the unredaction of an old version of a way, while being
# authorised as a normal user.
def test_unredact_way_normal_user
way = create(:way, :with_history, :version => 2)
way_v1 = way.old_ways.find_by(:version => 1)
way_v1.redact!(create(:redaction))
basic_authorization create(:user).email, "test"
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :forbidden, "should need to be moderator to unredact."
end
##
# test the unredaction of an old version of a way, while being
# authorised as a moderator.
def test_unredact_way_moderator
moderator_user = create(:moderator_user)
way = create(:way, :with_history, :version => 2)
way_v1 = way.old_ways.find_by(:version => 1)
way_v1.redact!(create(:redaction))
basic_authorization moderator_user.email, "test"
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :success, "should be OK to unredact old version as moderator."
# check moderator can still see the unredacted data, without passing
# the appropriate flag
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :success, "After unredaction, node should not be gone for moderator."
# and when accessed via history
get :history, :params => { :id => way_v1.way_id }
assert_response :success, "Unredaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 1, "way #{way_v1.way_id} version #{way_v1.version} should still be present in the history for moderators."
basic_authorization create(:user).email, "test"
# check normal user can now see the unredacted data
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
# and when accessed via history
get :history, :params => { :id => way_v1.way_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 1, "way #{way_v1.way_id} version #{way_v1.version} should still be present in the history for normal users."
end
private
##
# check that the current version of a way is equivalent to the
# version which we're getting from the versions call.
def check_current_version(way_id)
# get the current version
current_way = with_controller(WaysController.new) do
get :show, :params => { :id => way_id }
assert_response :success, "can't get current way #{way_id}"
Way.from_xml(@response.body)
end
assert_not_nil current_way, "getting way #{way_id} returned nil"
# get the "old" version of the way from the version method
get :version, :params => { :id => way_id, :version => current_way.version }
assert_response :success, "can't get old way #{way_id}, v#{current_way.version}"
old_way = Way.from_xml(@response.body)
# check that the ways are identical
assert_ways_are_equal current_way, old_way
end
##
# look at all the versions of the way in the history and get each version from
# the versions call. check that they're the same.
def check_history_equals_versions(way_id)
get :history, :params => { :id => way_id }
assert_response :success, "can't get way #{way_id} from API"
history_doc = XML::Parser.string(@response.body).parse
assert_not_nil history_doc, "parsing way #{way_id} history failed"
history_doc.find("//osm/way").each do |way_doc|
history_way = Way.from_xml_node(way_doc)
assert_not_nil history_way, "parsing way #{way_id} version failed"
get :version, :params => { :id => way_id, :version => history_way.version }
assert_response :success, "couldn't get way #{way_id}, v#{history_way.version}"
version_way = Way.from_xml(@response.body)
assert_not_nil version_way, "failed to parse #{way_id}, v#{history_way.version}"
assert_ways_are_equal history_way, version_way
end
end
def do_redact_way(way, redaction)
get :version, :params => { :id => way.way_id, :version => way.version }
assert_response :success, "should be able to get version #{way.version} of way #{way.way_id}."
# now redact it
post :redact, :params => { :id => way.way_id, :version => way.version, :redaction => redaction.id }
end
def propagate_tags(way, old_way)
way.tags.each do |k, v|
create(:old_way_tag, :old_way => old_way, :k => k, :v => v)
end
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,108 @@
require "test_helper"
module Api
class SearchControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/search", :method => :get },
{ :controller => "api/search", :action => "search_all" }
)
assert_routing(
{ :path => "/api/0.6/nodes/search", :method => :get },
{ :controller => "api/search", :action => "search_nodes" }
)
assert_routing(
{ :path => "/api/0.6/ways/search", :method => :get },
{ :controller => "api/search", :action => "search_ways" }
)
assert_routing(
{ :path => "/api/0.6/relations/search", :method => :get },
{ :controller => "api/search", :action => "search_relations" }
)
end
##
# test searching nodes
def test_search_nodes
get :search_nodes, :params => { :type => "test" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
get :search_nodes, :params => { :type => "test", :value => "yes" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
get :search_nodes, :params => { :name => "Test Node" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
end
##
# test searching ways
def test_search_ways
first_way = create(:way_with_nodes, :nodes_count => 2)
deleted_way = create(:way_with_nodes, :deleted, :nodes_count => 2)
third_way = create(:way_with_nodes, :nodes_count => 2)
[first_way, deleted_way, third_way].each do |way|
create(:way_tag, :way => way, :k => "test", :v => "yes")
end
create(:way_tag, :way => third_way, :k => "name", :v => "Test Way")
get :search_ways, :params => { :type => "test" }
assert_response :service_unavailable
assert_equal "Searching for a key without value is currently unavailable", response.headers["Error"]
get :search_ways, :params => { :type => "test", :value => "yes" }
assert_response :success
assert_select "way", 3
get :search_ways, :params => { :name => "Test Way" }
assert_response :success
assert_select "way", 1
end
##
# test searching relations
def test_search_relations
first_relation = create(:relation)
deleted_relation = create(:relation)
third_relation = create(:relation)
[first_relation, deleted_relation, third_relation].each do |relation|
create(:relation_tag, :relation => relation, :k => "test", :v => "yes")
end
create(:relation_tag, :relation => third_relation, :k => "name", :v => "Test Relation")
get :search_relations, :params => { :type => "test" }
assert_response :service_unavailable
assert_equal "Searching for a key without value is currently unavailable", response.headers["Error"]
get :search_relations, :params => { :type => "test", :value => "yes" }
assert_response :success
assert_select "relation", 3
get :search_relations, :params => { :name => "Test Relation" }
assert_response :success
assert_select "relation", 1
end
##
# test searching nodes, ways and relations
def test_search_all
get :search_all, :params => { :type => "test" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
get :search_all, :params => { :type => "test", :value => "yes" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
get :search_all, :params => { :name => "Test" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
end
end
end

View file

@ -0,0 +1,46 @@
require "test_helper"
module Api
class SwfControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/swf/trackpoints", :method => :get },
{ :controller => "api/swf", :action => "trackpoints" }
)
end
##
# basic test that trackpoints at least returns some sort of flash movie
def test_trackpoints
user = create(:user)
other_user = create(:user)
create(:trace, :visibility => "trackable", :latitude => 51.51, :longitude => -0.14, :user => user) do |trace|
create(:tracepoint, :trace => trace, :trackid => 1, :latitude => (51.510 * GeoRecord::SCALE).to_i, :longitude => (-0.140 * GeoRecord::SCALE).to_i)
create(:tracepoint, :trace => trace, :trackid => 2, :latitude => (51.511 * GeoRecord::SCALE).to_i, :longitude => (-0.141 * GeoRecord::SCALE).to_i)
end
create(:trace, :visibility => "identifiable", :latitude => 51.512, :longitude => 0.142) do |trace|
create(:tracepoint, :trace => trace, :latitude => (51.512 * GeoRecord::SCALE).to_i, :longitude => (0.142 * GeoRecord::SCALE).to_i)
end
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1 }
assert_response :success
assert_equal "application/x-shockwave-flash", response.content_type
assert_match(/^FWS/, response.body)
assert_equal 80, response.body.length
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1, :token => other_user.tokens.create.token }
assert_response :success
assert_equal "application/x-shockwave-flash", response.content_type
assert_match(/^FWS/, response.body)
assert_equal 67, response.body.length
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1, :token => user.tokens.create.token }
assert_response :success
assert_equal "application/x-shockwave-flash", response.content_type
assert_match(/^FWS/, response.body)
assert_equal 74, response.body.length
end
end
end

View file

@ -0,0 +1,357 @@
require "test_helper"
require "minitest/mock"
module Api
class TracesControllerTest < ActionController::TestCase
def setup
@gpx_trace_dir = Object.send("remove_const", "GPX_TRACE_DIR")
Object.const_set("GPX_TRACE_DIR", Rails.root.join("test", "gpx", "traces"))
@gpx_image_dir = Object.send("remove_const", "GPX_IMAGE_DIR")
Object.const_set("GPX_IMAGE_DIR", Rails.root.join("test", "gpx", "images"))
end
def teardown
File.unlink(*Dir.glob(File.join(GPX_TRACE_DIR, "*.gpx")))
File.unlink(*Dir.glob(File.join(GPX_IMAGE_DIR, "*.gif")))
Object.send("remove_const", "GPX_TRACE_DIR")
Object.const_set("GPX_TRACE_DIR", @gpx_trace_dir)
Object.send("remove_const", "GPX_IMAGE_DIR")
Object.const_set("GPX_IMAGE_DIR", @gpx_image_dir)
end
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/gpx/create", :method => :post },
{ :controller => "api/traces", :action => "api_create" }
)
assert_routing(
{ :path => "/api/0.6/gpx/1", :method => :get },
{ :controller => "api/traces", :action => "api_read", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/gpx/1", :method => :put },
{ :controller => "api/traces", :action => "api_update", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/gpx/1", :method => :delete },
{ :controller => "api/traces", :action => "api_delete", :id => "1" }
)
assert_recognizes(
{ :controller => "api/traces", :action => "api_read", :id => "1" },
{ :path => "/api/0.6/gpx/1/details", :method => :get }
)
assert_routing(
{ :path => "/api/0.6/gpx/1/data", :method => :get },
{ :controller => "api/traces", :action => "api_data", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/gpx/1/data.xml", :method => :get },
{ :controller => "api/traces", :action => "api_data", :id => "1", :format => "xml" }
)
end
# Check getting a specific trace through the api
def test_api_read
public_trace_file = create(:trace, :visibility => "public")
# First with no auth
get :api_read, :params => { :id => public_trace_file.id }
assert_response :unauthorized
# Now with some other user, which should work since the trace is public
basic_authorization create(:user).display_name, "test"
get :api_read, :params => { :id => public_trace_file.id }
assert_response :success
# And finally we should be able to do it with the owner of the trace
basic_authorization public_trace_file.user.display_name, "test"
get :api_read, :params => { :id => public_trace_file.id }
assert_response :success
end
# Check an anoymous trace can't be specifically fetched by another user
def test_api_read_anon
anon_trace_file = create(:trace, :visibility => "private")
# First with no auth
get :api_read, :params => { :id => anon_trace_file.id }
assert_response :unauthorized
# Now try with another user, which shouldn't work since the trace is anon
basic_authorization create(:user).display_name, "test"
get :api_read, :params => { :id => anon_trace_file.id }
assert_response :forbidden
# And finally we should be able to get the trace details with the trace owner
basic_authorization anon_trace_file.user.display_name, "test"
get :api_read, :params => { :id => anon_trace_file.id }
assert_response :success
end
# Check the api details for a trace that doesn't exist
def test_api_read_not_found
deleted_trace_file = create(:trace, :deleted)
# Try first with no auth, as it should require it
get :api_read, :params => { :id => 0 }
assert_response :unauthorized
# Login, and try again
basic_authorization deleted_trace_file.user.display_name, "test"
get :api_read, :params => { :id => 0 }
assert_response :not_found
# Now try a trace which did exist but has been deleted
basic_authorization deleted_trace_file.user.display_name, "test"
get :api_read, :params => { :id => deleted_trace_file.id }
assert_response :not_found
end
# Test downloading a trace through the api
def test_api_data
public_trace_file = create(:trace, :visibility => "public", :fixture => "a")
# First with no auth
get :api_data, :params => { :id => public_trace_file.id }
assert_response :unauthorized
# Now with some other user, which should work since the trace is public
basic_authorization create(:user).display_name, "test"
get :api_data, :params => { :id => public_trace_file.id }
check_trace_data public_trace_file, "848caa72f2f456d1bd6a0fdf228aa1b9"
# And finally we should be able to do it with the owner of the trace
basic_authorization public_trace_file.user.display_name, "test"
get :api_data, :params => { :id => public_trace_file.id }
check_trace_data public_trace_file, "848caa72f2f456d1bd6a0fdf228aa1b9"
end
# Test downloading a compressed trace through the api
def test_api_data_compressed
identifiable_trace_file = create(:trace, :visibility => "identifiable", :fixture => "d")
# Authenticate as the owner of the trace we will be using
basic_authorization identifiable_trace_file.user.display_name, "test"
# First get the data as is
get :api_data, :params => { :id => identifiable_trace_file.id }
check_trace_data identifiable_trace_file, "c6422a3d8750faae49ed70e7e8a51b93", "application/x-gzip", "gpx.gz"
# Now ask explicitly for XML format
get :api_data, :params => { :id => identifiable_trace_file.id, :format => "xml" }
check_trace_data identifiable_trace_file, "abd6675fdf3024a84fc0a1deac147c0d", "application/xml", "xml"
# Now ask explicitly for GPX format
get :api_data, :params => { :id => identifiable_trace_file.id, :format => "gpx" }
check_trace_data identifiable_trace_file, "abd6675fdf3024a84fc0a1deac147c0d"
end
# Check an anonymous trace can't be downloaded by another user through the api
def test_api_data_anon
anon_trace_file = create(:trace, :visibility => "private", :fixture => "b")
# First with no auth
get :api_data, :params => { :id => anon_trace_file.id }
assert_response :unauthorized
# Now with some other user, which shouldn't work since the trace is anon
basic_authorization create(:user).display_name, "test"
get :api_data, :params => { :id => anon_trace_file.id }
assert_response :forbidden
# And finally we should be able to do it with the owner of the trace
basic_authorization anon_trace_file.user.display_name, "test"
get :api_data, :params => { :id => anon_trace_file.id }
check_trace_data anon_trace_file, "66179ca44f1e93d8df62e2b88cbea732"
end
# Test downloading a trace that doesn't exist through the api
def test_api_data_not_found
deleted_trace_file = create(:trace, :deleted)
# Try first with no auth, as it should require it
get :api_data, :params => { :id => 0 }
assert_response :unauthorized
# Login, and try again
basic_authorization create(:user).display_name, "test"
get :api_data, :params => { :id => 0 }
assert_response :not_found
# Now try a trace which did exist but has been deleted
basic_authorization deleted_trace_file.user.display_name, "test"
get :api_data, :params => { :id => deleted_trace_file.id }
assert_response :not_found
end
# Test creating a trace through the api
def test_api_create
# Get file to use
fixture = Rails.root.join("test", "gpx", "fixtures", "a.gpx")
file = Rack::Test::UploadedFile.new(fixture, "application/gpx+xml")
user = create(:user)
# First with no auth
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :visibility => "trackable" }
assert_response :unauthorized
# Rewind the file
file.rewind
# Now authenticated
create(:user_preference, :user => user, :k => "gps.trace.visibility", :v => "identifiable")
assert_not_equal "trackable", user.preferences.where(:k => "gps.trace.visibility").first.v
basic_authorization user.display_name, "test"
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :visibility => "trackable" }
assert_response :success
trace = Trace.find(response.body.to_i)
assert_equal "a.gpx", trace.name
assert_equal "New Trace", trace.description
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
assert_equal "trackable", trace.visibility
assert_equal false, trace.inserted
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
trace.destroy
assert_equal "trackable", user.preferences.where(:k => "gps.trace.visibility").first.v
# Rewind the file
file.rewind
# Now authenticated, with the legacy public flag
assert_not_equal "public", user.preferences.where(:k => "gps.trace.visibility").first.v
basic_authorization user.display_name, "test"
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :public => 1 }
assert_response :success
trace = Trace.find(response.body.to_i)
assert_equal "a.gpx", trace.name
assert_equal "New Trace", trace.description
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
assert_equal "public", trace.visibility
assert_equal false, trace.inserted
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
trace.destroy
assert_equal "public", user.preferences.where(:k => "gps.trace.visibility").first.v
# Rewind the file
file.rewind
# Now authenticated, with the legacy private flag
second_user = create(:user)
assert_nil second_user.preferences.where(:k => "gps.trace.visibility").first
basic_authorization second_user.display_name, "test"
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :public => 0 }
assert_response :success
trace = Trace.find(response.body.to_i)
assert_equal "a.gpx", trace.name
assert_equal "New Trace", trace.description
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
assert_equal "private", trace.visibility
assert_equal false, trace.inserted
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
trace.destroy
assert_equal "private", second_user.preferences.where(:k => "gps.trace.visibility").first.v
end
# Check updating a trace through the api
def test_api_update
public_trace_file = create(:trace, :visibility => "public", :fixture => "a")
deleted_trace_file = create(:trace, :deleted)
anon_trace_file = create(:trace, :visibility => "private")
# First with no auth
put :api_update, :params => { :id => public_trace_file.id }, :body => public_trace_file.to_xml.to_s
assert_response :unauthorized
# Now with some other user, which should fail
basic_authorization create(:user).display_name, "test"
put :api_update, :params => { :id => public_trace_file.id }, :body => public_trace_file.to_xml.to_s
assert_response :forbidden
# Now with a trace which doesn't exist
basic_authorization create(:user).display_name, "test"
put :api_update, :params => { :id => 0 }, :body => public_trace_file.to_xml.to_s
assert_response :not_found
# Now with a trace which did exist but has been deleted
basic_authorization deleted_trace_file.user.display_name, "test"
put :api_update, :params => { :id => deleted_trace_file.id }, :body => deleted_trace_file.to_xml.to_s
assert_response :not_found
# Now try an update with the wrong ID
basic_authorization public_trace_file.user.display_name, "test"
put :api_update, :params => { :id => public_trace_file.id }, :body => anon_trace_file.to_xml.to_s
assert_response :bad_request,
"should not be able to update a trace with a different ID from the XML"
# And finally try an update that should work
basic_authorization public_trace_file.user.display_name, "test"
t = public_trace_file
t.description = "Changed description"
t.visibility = "private"
put :api_update, :params => { :id => t.id }, :body => t.to_xml.to_s
assert_response :success
nt = Trace.find(t.id)
assert_equal nt.description, t.description
assert_equal nt.visibility, t.visibility
end
# Test that updating a trace doesn't duplicate the tags
def test_api_update_tags
tracetag = create(:tracetag)
trace = tracetag.trace
basic_authorization trace.user.display_name, "test"
put :api_update, :params => { :id => trace.id }, :body => trace.to_xml.to_s
assert_response :success
updated = Trace.find(trace.id)
# Ensure there's only one tag in the database after updating
assert_equal Tracetag.count, 1
# The new tag object might have a different id, so check the string representation
assert_equal trace.tagstring, updated.tagstring
end
# Check deleting a trace through the api
def test_api_delete
public_trace_file = create(:trace, :visibility => "public")
# First with no auth
delete :api_delete, :params => { :id => public_trace_file.id }
assert_response :unauthorized
# Now with some other user, which should fail
basic_authorization create(:user).display_name, "test"
delete :api_delete, :params => { :id => public_trace_file.id }
assert_response :forbidden
# Now with a trace which doesn't exist
basic_authorization create(:user).display_name, "test"
delete :api_delete, :params => { :id => 0 }
assert_response :not_found
# And finally we should be able to do it with the owner of the trace
basic_authorization public_trace_file.user.display_name, "test"
delete :api_delete, :params => { :id => public_trace_file.id }
assert_response :success
# Try it a second time, which should fail
basic_authorization public_trace_file.user.display_name, "test"
delete :api_delete, :params => { :id => public_trace_file.id }
assert_response :not_found
end
private
def check_trace_data(trace, digest, content_type = "application/gpx+xml", extension = "gpx")
assert_response :success
assert_equal digest, Digest::MD5.hexdigest(response.body)
assert_equal content_type, response.content_type
assert_equal "attachment; filename=\"#{trace.id}.#{extension}\"", @response.header["Content-Disposition"]
end
end
end

View file

@ -0,0 +1,246 @@
require "test_helper"
module Api
class UserPreferencesControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/user/preferences", :method => :get },
{ :controller => "api/user_preferences", :action => "read" }
)
assert_routing(
{ :path => "/api/0.6/user/preferences", :method => :put },
{ :controller => "api/user_preferences", :action => "update" }
)
assert_routing(
{ :path => "/api/0.6/user/preferences/key", :method => :get },
{ :controller => "api/user_preferences", :action => "read_one", :preference_key => "key" }
)
assert_routing(
{ :path => "/api/0.6/user/preferences/key", :method => :put },
{ :controller => "api/user_preferences", :action => "update_one", :preference_key => "key" }
)
assert_routing(
{ :path => "/api/0.6/user/preferences/key", :method => :delete },
{ :controller => "api/user_preferences", :action => "delete_one", :preference_key => "key" }
)
end
##
# test read action
def test_read
# first try without auth
get :read
assert_response :unauthorized, "should be authenticated"
# authenticate as a user with no preferences
basic_authorization create(:user).email, "test"
# try the read again
get :read
assert_select "osm" do
assert_select "preferences", :count => 1 do
assert_select "preference", :count => 0
end
end
# authenticate as a user with preferences
user = create(:user)
user_preference = create(:user_preference, :user => user)
user_preference2 = create(:user_preference, :user => user)
basic_authorization user.email, "test"
# try the read again
get :read
assert_response :success
assert_equal "application/xml", @response.content_type
assert_select "osm" do
assert_select "preferences", :count => 1 do
assert_select "preference", :count => 2
assert_select "preference[k=\"#{user_preference.k}\"][v=\"#{user_preference.v}\"]", :count => 1
assert_select "preference[k=\"#{user_preference2.k}\"][v=\"#{user_preference2.v}\"]", :count => 1
end
end
end
##
# test read_one action
def test_read_one
user = create(:user)
create(:user_preference, :user => user, :k => "key", :v => "value")
# try a read without auth
get :read_one, :params => { :preference_key => "key" }
assert_response :unauthorized, "should be authenticated"
# authenticate as a user with preferences
basic_authorization user.email, "test"
# try the read again
get :read_one, :params => { :preference_key => "key" }
assert_response :success
assert_equal "text/plain", @response.content_type
assert_equal "value", @response.body
# try the read again for a non-existent key
get :read_one, :params => { :preference_key => "unknown_key" }
assert_response :not_found
end
##
# test update action
def test_update
user = create(:user)
create(:user_preference, :user => user, :k => "key", :v => "value")
create(:user_preference, :user => user, :k => "some_key", :v => "some_value")
# try a put without auth
assert_no_difference "UserPreference.count" do
put :update, :body => "<osm><preferences><preference k='key' v='new_value'/><preference k='new_key' v='value'/></preferences></osm>"
end
assert_response :unauthorized, "should be authenticated"
assert_equal "value", UserPreference.find([user.id, "key"]).v
assert_equal "some_value", UserPreference.find([user.id, "some_key"]).v
assert_raises ActiveRecord::RecordNotFound do
UserPreference.find([user.id, "new_key"])
end
# authenticate as a user with preferences
basic_authorization user.email, "test"
# try the put again
assert_no_difference "UserPreference.count" do
put :update, :body => "<osm><preferences><preference k='key' v='new_value'/><preference k='new_key' v='value'/></preferences></osm>"
end
assert_response :success
assert_equal "text/plain", @response.content_type
assert_equal "", @response.body
assert_equal "new_value", UserPreference.find([user.id, "key"]).v
assert_equal "value", UserPreference.find([user.id, "new_key"]).v
assert_raises ActiveRecord::RecordNotFound do
UserPreference.find([user.id, "some_key"])
end
# try a put with duplicate keys
assert_no_difference "UserPreference.count" do
put :update, :body => "<osm><preferences><preference k='key' v='value'/><preference k='key' v='newer_value'/></preferences></osm>"
end
assert_response :bad_request
assert_equal "text/plain", @response.content_type
assert_equal "Duplicate preferences with key key", @response.body
assert_equal "new_value", UserPreference.find([user.id, "key"]).v
# try a put with invalid content
assert_no_difference "UserPreference.count" do
put :update, :body => "nonsense"
end
assert_response :bad_request
end
##
# test update_one action
def test_update_one
user = create(:user)
create(:user_preference, :user => user)
# try a put without auth
assert_no_difference "UserPreference.count" do
put :update_one, :params => { :preference_key => "new_key" }, :body => "new_value"
end
assert_response :unauthorized, "should be authenticated"
assert_raises ActiveRecord::RecordNotFound do
UserPreference.find([user.id, "new_key"])
end
# authenticate as a user with preferences
basic_authorization user.email, "test"
# try adding a new preference
assert_difference "UserPreference.count", 1 do
put :update_one, :params => { :preference_key => "new_key" }, :body => "new_value"
end
assert_response :success
assert_equal "text/plain", @response.content_type
assert_equal "", @response.body
assert_equal "new_value", UserPreference.find([user.id, "new_key"]).v
# try changing the value of a preference
assert_no_difference "UserPreference.count" do
put :update_one, :params => { :preference_key => "new_key" }, :body => "newer_value"
end
assert_response :success
assert_equal "text/plain", @response.content_type
assert_equal "", @response.body
assert_equal "newer_value", UserPreference.find([user.id, "new_key"]).v
end
##
# test delete_one action
def test_delete_one
user = create(:user)
create(:user_preference, :user => user, :k => "key", :v => "value")
# try a delete without auth
assert_no_difference "UserPreference.count" do
delete :delete_one, :params => { :preference_key => "key" }
end
assert_response :unauthorized, "should be authenticated"
assert_equal "value", UserPreference.find([user.id, "key"]).v
# authenticate as a user with preferences
basic_authorization user.email, "test"
# try the delete again
assert_difference "UserPreference.count", -1 do
get :delete_one, :params => { :preference_key => "key" }
end
assert_response :success
assert_equal "text/plain", @response.content_type
assert_equal "", @response.body
assert_raises ActiveRecord::RecordNotFound do
UserPreference.find([user.id, "key"])
end
# try the delete again for the same key
assert_no_difference "UserPreference.count" do
get :delete_one, :params => { :preference_key => "key" }
end
assert_response :not_found
assert_raises ActiveRecord::RecordNotFound do
UserPreference.find([user.id, "key"])
end
end
# Ensure that a valid access token with correct capabilities can be used to
# read preferences
def test_read_one_using_token
user = create(:user)
token = create(:access_token, :user => user, :allow_read_prefs => true)
create(:user_preference, :user => user, :k => "key", :v => "value")
# Hack together an oauth request - an alternative would be to sign the request properly
@request.env["oauth.version"] = 1
@request.env["oauth.strategies"] = [:token]
@request.env["oauth.token"] = token
get :read_one, :params => { :preference_key => "key" }
assert_response :success
end
# Ensure that a valid access token with incorrect capabilities can't be used
# to read preferences even, though the owner of that token could read them
# by other methods.
def test_read_one_using_token_fail
user = create(:user)
token = create(:access_token, :user => user, :allow_read_prefs => false)
create(:user_preference, :user => user, :k => "key", :v => "value")
@request.env["oauth.version"] = 1
@request.env["oauth.strategies"] = [:token]
@request.env["oauth.token"] = token
get :read_one, :params => { :preference_key => "key" }
assert_response :forbidden
end
end
end

View file

@ -0,0 +1,199 @@
require "test_helper"
module Api
class UsersControllerTest < ActionController::TestCase
def setup
stub_hostip_requests
end
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/user/1", :method => :get },
{ :controller => "api/users", :action => "api_read", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/user/details", :method => :get },
{ :controller => "api/users", :action => "api_details" }
)
assert_routing(
{ :path => "/api/0.6/user/gpx_files", :method => :get },
{ :controller => "api/users", :action => "api_gpx_files" }
)
assert_routing(
{ :path => "/api/0.6/users", :method => :get },
{ :controller => "api/users", :action => "api_users" }
)
end
def test_api_read
user = create(:user, :description => "test", :terms_agreed => Date.yesterday)
# check that a visible user is returned properly
get :api_read, :params => { :id => user.id }
assert_response :success
assert_equal "text/xml", response.content_type
# check the data that is returned
assert_select "description", :count => 1, :text => "test"
assert_select "contributor-terms", :count => 1 do
assert_select "[agreed='true']"
end
assert_select "img", :count => 0
assert_select "roles", :count => 1 do
assert_select "role", :count => 0
end
assert_select "changesets", :count => 1 do
assert_select "[count='0']"
end
assert_select "traces", :count => 1 do
assert_select "[count='0']"
end
assert_select "blocks", :count => 1 do
assert_select "received", :count => 1 do
assert_select "[count='0'][active='0']"
end
assert_select "issued", :count => 0
end
# check that we aren't revealing private information
assert_select "contributor-terms[pd]", false
assert_select "home", false
assert_select "languages", false
assert_select "messages", false
# check that a suspended user is not returned
get :api_read, :params => { :id => create(:user, :suspended).id }
assert_response :gone
# check that a deleted user is not returned
get :api_read, :params => { :id => create(:user, :deleted).id }
assert_response :gone
# check that a non-existent user is not returned
get :api_read, :params => { :id => 0 }
assert_response :not_found
end
def test_api_details
user = create(:user, :description => "test", :terms_agreed => Date.yesterday, :home_lat => 12.1, :home_lon => 12.1, :languages => ["en"])
create(:message, :read, :recipient => user)
create(:message, :sender => user)
# check that nothing is returned when not logged in
get :api_details
assert_response :unauthorized
# check that we get a response when logged in
basic_authorization user.email, "test"
get :api_details
assert_response :success
assert_equal "text/xml", response.content_type
# check the data that is returned
assert_select "description", :count => 1, :text => "test"
assert_select "contributor-terms", :count => 1 do
assert_select "[agreed='true'][pd='false']"
end
assert_select "img", :count => 0
assert_select "roles", :count => 1 do
assert_select "role", :count => 0
end
assert_select "changesets", :count => 1 do
assert_select "[count='0']", :count => 1
end
assert_select "traces", :count => 1 do
assert_select "[count='0']", :count => 1
end
assert_select "blocks", :count => 1 do
assert_select "received", :count => 1 do
assert_select "[count='0'][active='0']"
end
assert_select "issued", :count => 0
end
assert_select "home", :count => 1 do
assert_select "[lat='12.1'][lon='12.1'][zoom='3']"
end
assert_select "languages", :count => 1 do
assert_select "lang", :count => 1, :text => "en"
end
assert_select "messages", :count => 1 do
assert_select "received", :count => 1 do
assert_select "[count='1'][unread='0']"
end
assert_select "sent", :count => 1 do
assert_select "[count='1']"
end
end
end
def test_api_users
user1 = create(:user, :description => "test1", :terms_agreed => Date.yesterday)
user2 = create(:user, :description => "test2", :terms_agreed => Date.yesterday)
user3 = create(:user, :description => "test3", :terms_agreed => Date.yesterday)
get :api_users, :params => { :users => user1.id }
assert_response :success
assert_equal "text/xml", response.content_type
assert_select "user", :count => 1 do
assert_select "user[id='#{user1.id}']", :count => 1
assert_select "user[id='#{user2.id}']", :count => 0
assert_select "user[id='#{user3.id}']", :count => 0
end
get :api_users, :params => { :users => user2.id }
assert_response :success
assert_equal "text/xml", response.content_type
assert_select "user", :count => 1 do
assert_select "user[id='#{user1.id}']", :count => 0
assert_select "user[id='#{user2.id}']", :count => 1
assert_select "user[id='#{user3.id}']", :count => 0
end
get :api_users, :params => { :users => "#{user1.id},#{user3.id}" }
assert_response :success
assert_equal "text/xml", response.content_type
assert_select "user", :count => 2 do
assert_select "user[id='#{user1.id}']", :count => 1
assert_select "user[id='#{user2.id}']", :count => 0
assert_select "user[id='#{user3.id}']", :count => 1
end
get :api_users, :params => { :users => create(:user, :suspended).id }
assert_response :not_found
get :api_users, :params => { :users => create(:user, :deleted).id }
assert_response :not_found
get :api_users, :params => { :users => 0 }
assert_response :not_found
end
def test_api_gpx_files
user = create(:user)
trace1 = create(:trace, :user => user) do |trace|
create(:tracetag, :trace => trace, :tag => "London")
end
trace2 = create(:trace, :user => user) do |trace|
create(:tracetag, :trace => trace, :tag => "Birmingham")
end
# check that nothing is returned when not logged in
get :api_gpx_files
assert_response :unauthorized
# check that we get a response when logged in
basic_authorization user.email, "test"
get :api_gpx_files
assert_response :success
assert_equal "application/xml", response.content_type
# check the data that is returned
assert_select "gpx_file[id='#{trace1.id}']", 1 do
assert_select "tag", "London"
end
assert_select "gpx_file[id='#{trace2.id}']", 1 do
assert_select "tag", "Birmingham"
end
end
end
end

View file

@ -0,0 +1,754 @@
require "test_helper"
module Api
class WaysControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/way/create", :method => :put },
{ :controller => "api/ways", :action => "create" }
)
assert_routing(
{ :path => "/api/0.6/way/1/full", :method => :get },
{ :controller => "api/ways", :action => "full", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/way/1", :method => :get },
{ :controller => "api/ways", :action => "show", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/way/1", :method => :put },
{ :controller => "api/ways", :action => "update", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/way/1", :method => :delete },
{ :controller => "api/ways", :action => "delete", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/ways", :method => :get },
{ :controller => "api/ways", :action => "index" }
)
end
# -------------------------------------
# Test showing ways.
# -------------------------------------
def test_show
# check that a visible way is returned properly
get :show, :params => { :id => create(:way).id }
assert_response :success
# check that an invisible way is not returned
get :show, :params => { :id => create(:way, :deleted).id }
assert_response :gone
# check chat a non-existent way is not returned
get :show, :params => { :id => 0 }
assert_response :not_found
end
##
# check the "full" mode
def test_full
Way.all.each do |way|
get :full, :params => { :id => way.id }
# full call should say "gone" for non-visible ways...
unless way.visible
assert_response :gone
next
end
# otherwise it should say success
assert_response :success
# Check the way is correctly returned
assert_select "osm way[id='#{way.id}'][version='#{way.version}'][visible='#{way.visible}']", 1
# check that each node in the way appears once in the output as a
# reference and as the node element.
way.nodes.each do |n|
count = (way.nodes - (way.nodes - [n])).length
assert_select "osm way nd[ref='#{n.id}']", count
assert_select "osm node[id='#{n.id}'][version='#{n.version}'][lat='#{format('%.7f', n.lat)}'][lon='#{format('%.7f', n.lon)}']", 1
end
end
end
##
# test fetching multiple ways
def test_index
way1 = create(:way)
way2 = create(:way, :deleted)
way3 = create(:way)
way4 = create(:way)
# check error when no parameter provided
get :index
assert_response :bad_request
# check error when no parameter value provided
get :index, :params => { :ways => "" }
assert_response :bad_request
# test a working call
get :index, :params => { :ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}" }
assert_response :success
assert_select "osm" do
assert_select "way", :count => 4
assert_select "way[id='#{way1.id}'][visible='true']", :count => 1
assert_select "way[id='#{way2.id}'][visible='false']", :count => 1
assert_select "way[id='#{way3.id}'][visible='true']", :count => 1
assert_select "way[id='#{way4.id}'][visible='true']", :count => 1
end
# check error when a non-existent way is included
get :index, :params => { :ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id},0" }
assert_response :not_found
end
# -------------------------------------
# Test simple way creation.
# -------------------------------------
def test_create
node1 = create(:node)
node2 = create(:node)
private_user = create(:user, :data_public => false)
private_changeset = create(:changeset, :user => private_user)
user = create(:user)
changeset = create(:changeset, :user => user)
## First check that it fails when creating a way using a non-public user
basic_authorization private_user.email, "test"
# use the first user's open changeset
changeset_id = private_changeset.id
# create a way with pre-existing nodes
xml = "<osm><way changeset='#{changeset_id}'>" \
"<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
"<tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# hope for failure
assert_response :forbidden,
"way upload did not return forbidden status"
## Now use a public user
basic_authorization user.email, "test"
# use the first user's open changeset
changeset_id = changeset.id
# create a way with pre-existing nodes
xml = "<osm><way changeset='#{changeset_id}'>" \
"<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
"<tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# hope for success
assert_response :success,
"way upload did not return success status"
# read id of created way and search for it
wayid = @response.body
checkway = Way.find(wayid)
assert_not_nil checkway,
"uploaded way not found in data base after upload"
# compare values
assert_equal checkway.nds.length, 2,
"saved way does not contain exactly one node"
assert_equal checkway.nds[0], node1.id,
"saved way does not contain the right node on pos 0"
assert_equal checkway.nds[1], node2.id,
"saved way does not contain the right node on pos 1"
assert_equal checkway.changeset_id, changeset_id,
"saved way does not belong to the correct changeset"
assert_equal user.id, checkway.changeset.user_id,
"saved way does not belong to user that created it"
assert_equal true, checkway.visible,
"saved way is not visible"
end
# -------------------------------------
# Test creating some invalid ways.
# -------------------------------------
def test_create_invalid
node = create(:node)
private_user = create(:user, :data_public => false)
private_open_changeset = create(:changeset, :user => private_user)
private_closed_changeset = create(:changeset, :closed, :user => private_user)
user = create(:user)
open_changeset = create(:changeset, :user => user)
closed_changeset = create(:changeset, :closed, :user => user)
## First test with a private user to make sure that they are not authorized
basic_authorization private_user.email, "test"
# use the first user's open changeset
# create a way with non-existing node
xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
"<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# expect failure
assert_response :forbidden,
"way upload with invalid node using a private user did not return 'forbidden'"
# create a way with no nodes
xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
"<tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# expect failure
assert_response :forbidden,
"way upload with no node using a private userdid not return 'forbidden'"
# create a way inside a closed changeset
xml = "<osm><way changeset='#{private_closed_changeset.id}'>" \
"<nd ref='#{node.id}'/></way></osm>"
put :create, :body => xml
# expect failure
assert_response :forbidden,
"way upload to closed changeset with a private user did not return 'forbidden'"
## Now test with a public user
basic_authorization user.email, "test"
# use the first user's open changeset
# create a way with non-existing node
xml = "<osm><way changeset='#{open_changeset.id}'>" \
"<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# expect failure
assert_response :precondition_failed,
"way upload with invalid node did not return 'precondition failed'"
assert_equal "Precondition failed: Way requires the nodes with id in (0), which either do not exist, or are not visible.", @response.body
# create a way with no nodes
xml = "<osm><way changeset='#{open_changeset.id}'>" \
"<tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# expect failure
assert_response :precondition_failed,
"way upload with no node did not return 'precondition failed'"
assert_equal "Precondition failed: Cannot create way: data is invalid.", @response.body
# create a way inside a closed changeset
xml = "<osm><way changeset='#{closed_changeset.id}'>" \
"<nd ref='#{node.id}'/></way></osm>"
put :create, :body => xml
# expect failure
assert_response :conflict,
"way upload to closed changeset did not return 'conflict'"
# create a way with a tag which is too long
xml = "<osm><way changeset='#{open_changeset.id}'>" \
"<nd ref='#{node.id}'/>" \
"<tag k='foo' v='#{'x' * 256}'/>" \
"</way></osm>"
put :create, :body => xml
# expect failure
assert_response :bad_request,
"way upload to with too long tag did not return 'bad_request'"
end
# -------------------------------------
# Test deleting ways.
# -------------------------------------
def test_delete
private_user = create(:user, :data_public => false)
private_open_changeset = create(:changeset, :user => private_user)
private_closed_changeset = create(:changeset, :closed, :user => private_user)
private_way = create(:way, :changeset => private_open_changeset)
private_deleted_way = create(:way, :deleted, :changeset => private_open_changeset)
private_used_way = create(:way, :changeset => private_open_changeset)
create(:relation_member, :member => private_used_way)
user = create(:user)
open_changeset = create(:changeset, :user => user)
closed_changeset = create(:changeset, :closed, :user => user)
way = create(:way, :changeset => open_changeset)
deleted_way = create(:way, :deleted, :changeset => open_changeset)
used_way = create(:way, :changeset => open_changeset)
relation_member = create(:relation_member, :member => used_way)
relation = relation_member.relation
# first try to delete way without auth
delete :delete, :params => { :id => way.id }
assert_response :unauthorized
# now set auth using the private user
basic_authorization private_user.email, "test"
# this shouldn't work as with the 0.6 api we need pay load to delete
delete :delete, :params => { :id => private_way.id }
assert_response :forbidden
# Now try without having a changeset
xml = "<osm><way id='#{private_way.id}'/></osm>"
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
assert_response :forbidden
# try to delete with an invalid (closed) changeset
xml = update_changeset(private_way.to_xml, private_closed_changeset.id)
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
assert_response :forbidden
# try to delete with an invalid (non-existent) changeset
xml = update_changeset(private_way.to_xml, 0)
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
assert_response :forbidden
# Now try with a valid changeset
xml = private_way.to_xml
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
assert_response :forbidden
# check the returned value - should be the new version number
# valid delete should return the new version number, which should
# be greater than the old version number
# assert @response.body.to_i > current_ways(:visible_way).version,
# "delete request should return a new version number for way"
# this won't work since the way is already deleted
xml = private_deleted_way.to_xml
delete :delete, :params => { :id => private_deleted_way.id }, :body => xml.to_s
assert_response :forbidden
# this shouldn't work as the way is used in a relation
xml = private_used_way.to_xml
delete :delete, :params => { :id => private_used_way.id }, :body => xml.to_s
assert_response :forbidden,
"shouldn't be able to delete a way used in a relation (#{@response.body}), when done by a private user"
# this won't work since the way never existed
delete :delete, :params => { :id => 0 }
assert_response :forbidden
### Now check with a public user
# now set auth
basic_authorization user.email, "test"
# this shouldn't work as with the 0.6 api we need pay load to delete
delete :delete, :params => { :id => way.id }
assert_response :bad_request
# Now try without having a changeset
xml = "<osm><way id='#{way.id}'/></osm>"
delete :delete, :params => { :id => way.id }, :body => xml.to_s
assert_response :bad_request
# try to delete with an invalid (closed) changeset
xml = update_changeset(way.to_xml, closed_changeset.id)
delete :delete, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict
# try to delete with an invalid (non-existent) changeset
xml = update_changeset(way.to_xml, 0)
delete :delete, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict
# Now try with a valid changeset
xml = way.to_xml
delete :delete, :params => { :id => way.id }, :body => xml.to_s
assert_response :success
# check the returned value - should be the new version number
# valid delete should return the new version number, which should
# be greater than the old version number
assert @response.body.to_i > way.version,
"delete request should return a new version number for way"
# this won't work since the way is already deleted
xml = deleted_way.to_xml
delete :delete, :params => { :id => deleted_way.id }, :body => xml.to_s
assert_response :gone
# this shouldn't work as the way is used in a relation
xml = used_way.to_xml
delete :delete, :params => { :id => used_way.id }, :body => xml.to_s
assert_response :precondition_failed,
"shouldn't be able to delete a way used in a relation (#{@response.body})"
assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
# this won't work since the way never existed
delete :delete, :params => { :id => 0 }
assert_response :not_found
end
##
# tests whether the API works and prevents incorrect use while trying
# to update ways.
def test_update
private_user = create(:user, :data_public => false)
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
user = create(:user)
way = create(:way, :changeset => create(:changeset, :user => user))
node = create(:node)
create(:way_node, :way => private_way, :node => node)
create(:way_node, :way => way, :node => node)
## First test with no user credentials
# try and update a way without authorisation
xml = way.to_xml
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :unauthorized
## Second test with the private user
# setup auth
basic_authorization private_user.email, "test"
## trying to break changesets
# try and update in someone else's changeset
xml = update_changeset(private_way.to_xml,
create(:changeset).id)
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data "update with other user's changeset should be forbidden when date isn't public"
# try and update in a closed changeset
xml = update_changeset(private_way.to_xml,
create(:changeset, :closed, :user => private_user).id)
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
# try and update in a non-existant changeset
xml = update_changeset(private_way.to_xml, 0)
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data("update with changeset=0 should be forbidden, when data isn't public")
## try and submit invalid updates
xml = xml_replace_node(private_way.to_xml, node.id, 9999)
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data "way with non-existent node should be forbidden, when data isn't public"
xml = xml_replace_node(private_way.to_xml, node.id, create(:node, :deleted).id)
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data "way with deleted node should be forbidden, when data isn't public"
## finally, produce a good request which will still not work
xml = private_way.to_xml
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data "should have failed with a forbidden when data isn't public"
## Finally test with the public user
# setup auth
basic_authorization user.email, "test"
## trying to break changesets
# try and update in someone else's changeset
xml = update_changeset(way.to_xml,
create(:changeset).id)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict, "update with other user's changeset should be rejected"
# try and update in a closed changeset
xml = update_changeset(way.to_xml,
create(:changeset, :closed, :user => user).id)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict, "update with closed changeset should be rejected"
# try and update in a non-existant changeset
xml = update_changeset(way.to_xml, 0)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict, "update with changeset=0 should be rejected"
## try and submit invalid updates
xml = xml_replace_node(way.to_xml, node.id, 9999)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :precondition_failed, "way with non-existent node should be rejected"
xml = xml_replace_node(way.to_xml, node.id, create(:node, :deleted).id)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :precondition_failed, "way with deleted node should be rejected"
## next, attack the versioning
current_way_version = way.version
# try and submit a version behind
xml = xml_attr_rewrite(way.to_xml,
"version", current_way_version - 1)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict, "should have failed on old version number"
# try and submit a version ahead
xml = xml_attr_rewrite(way.to_xml,
"version", current_way_version + 1)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict, "should have failed on skipped version number"
# try and submit total crap in the version field
xml = xml_attr_rewrite(way.to_xml,
"version", "p1r4t3s!")
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict,
"should not be able to put 'p1r4at3s!' in the version field"
## try an update with the wrong ID
xml = create(:way).to_xml
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to update a way with a different ID from the XML"
## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
xml = "<update/>"
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to update a way with non-OSM XML doc."
## finally, produce a good request which should work
xml = way.to_xml
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :success, "a valid update request failed"
end
# ------------------------------------------------------------
# test tags handling
# ------------------------------------------------------------
##
# Try adding a new tag to a way
def test_add_tags
private_user = create(:user, :data_public => false)
private_way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => private_user))
user = create(:user)
way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => user))
## Try with the non-public user
# setup auth
basic_authorization private_user.email, "test"
# add an identical tag to the way
tag_xml = XML::Node.new("tag")
tag_xml["k"] = "new"
tag_xml["v"] = "yes"
# add the tag into the existing xml
way_xml = private_way.to_xml
way_xml.find("//osm/way").first << tag_xml
# try and upload it
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
assert_response :forbidden,
"adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
## Now try with the public user
# setup auth
basic_authorization user.email, "test"
# add an identical tag to the way
tag_xml = XML::Node.new("tag")
tag_xml["k"] = "new"
tag_xml["v"] = "yes"
# add the tag into the existing xml
way_xml = way.to_xml
way_xml.find("//osm/way").first << tag_xml
# try and upload it
put :update, :params => { :id => way.id }, :body => way_xml.to_s
assert_response :success,
"adding a new tag to a way should succeed"
assert_equal way.version + 1, @response.body.to_i
end
##
# Try adding a duplicate of an existing tag to a way
def test_add_duplicate_tags
private_user = create(:user, :data_public => false)
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
private_existing_tag = create(:way_tag, :way => private_way)
user = create(:user)
way = create(:way, :changeset => create(:changeset, :user => user))
existing_tag = create(:way_tag, :way => way)
## Try with the non-public user
# setup auth
basic_authorization private_user.email, "test"
# add an identical tag to the way
tag_xml = XML::Node.new("tag")
tag_xml["k"] = private_existing_tag.k
tag_xml["v"] = private_existing_tag.v
# add the tag into the existing xml
way_xml = private_way.to_xml
way_xml.find("//osm/way").first << tag_xml
# try and upload it
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
assert_response :forbidden,
"adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
## Now try with the public user
# setup auth
basic_authorization user.email, "test"
# add an identical tag to the way
tag_xml = XML::Node.new("tag")
tag_xml["k"] = existing_tag.k
tag_xml["v"] = existing_tag.v
# add the tag into the existing xml
way_xml = way.to_xml
way_xml.find("//osm/way").first << tag_xml
# try and upload it
put :update, :params => { :id => way.id }, :body => way_xml.to_s
assert_response :bad_request,
"adding a duplicate tag to a way should fail with 'bad request'"
assert_equal "Element way/#{way.id} has duplicate tags with key #{existing_tag.k}", @response.body
end
##
# Try adding a new duplicate tags to a way
def test_new_duplicate_tags
private_user = create(:user, :data_public => false)
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
user = create(:user)
way = create(:way, :changeset => create(:changeset, :user => user))
## First test with the non-public user so should be rejected
# setup auth
basic_authorization private_user.email, "test"
# create duplicate tag
tag_xml = XML::Node.new("tag")
tag_xml["k"] = "i_am_a_duplicate"
tag_xml["v"] = "foobar"
# add the tag into the existing xml
way_xml = private_way.to_xml
# add two copies of the tag
way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
# try and upload it
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
assert_response :forbidden,
"adding new duplicate tags to a way using a non-public user should fail with 'forbidden'"
## Now test with the public user
# setup auth
basic_authorization user.email, "test"
# create duplicate tag
tag_xml = XML::Node.new("tag")
tag_xml["k"] = "i_am_a_duplicate"
tag_xml["v"] = "foobar"
# add the tag into the existing xml
way_xml = way.to_xml
# add two copies of the tag
way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
# try and upload it
put :update, :params => { :id => way.id }, :body => way_xml.to_s
assert_response :bad_request,
"adding new duplicate tags to a way should fail with 'bad request'"
assert_equal "Element way/#{way.id} has duplicate tags with key i_am_a_duplicate", @response.body
end
##
# Try adding a new duplicate tags to a way.
# But be a bit subtle - use unicode decoding ambiguities to use different
# binary strings which have the same decoding.
def test_invalid_duplicate_tags
private_user = create(:user, :data_public => false)
private_changeset = create(:changeset, :user => private_user)
user = create(:user)
changeset = create(:changeset, :user => user)
## First make sure that you can't with a non-public user
# setup auth
basic_authorization private_user.email, "test"
# add the tag into the existing xml
way_str = "<osm><way changeset='#{private_changeset.id}'>"
way_str << "<tag k='addr:housenumber' v='1'/>"
way_str << "<tag k='addr:housenumber' v='2'/>"
way_str << "</way></osm>"
# try and upload it
put :create, :body => way_str
assert_response :forbidden,
"adding new duplicate tags to a way with a non-public user should fail with 'forbidden'"
## Now do it with a public user
# setup auth
basic_authorization user.email, "test"
# add the tag into the existing xml
way_str = "<osm><way changeset='#{changeset.id}'>"
way_str << "<tag k='addr:housenumber' v='1'/>"
way_str << "<tag k='addr:housenumber' v='2'/>"
way_str << "</way></osm>"
# try and upload it
put :create, :body => way_str
assert_response :bad_request,
"adding new duplicate tags to a way should fail with 'bad request'"
assert_equal "Element way/ has duplicate tags with key addr:housenumber", @response.body
end
##
# test that a call to ways_for_node returns all ways that contain the node
# and none that don't.
def test_ways_for_node
node = create(:node)
way1 = create(:way)
way2 = create(:way)
create(:way_node, :way => way1, :node => node)
create(:way_node, :way => way2, :node => node)
# create an unrelated way
create(:way_with_nodes, :nodes_count => 2)
# create a way which used to use the node
way3_v1 = create(:old_way, :version => 1)
_way3_v2 = create(:old_way, :current_way => way3_v1.current_way, :version => 2)
create(:old_way_node, :old_way => way3_v1, :node => node)
get :ways_for_node, :params => { :id => node.id }
assert_response :success
ways_xml = XML::Parser.string(@response.body).parse
assert_not_nil ways_xml, "failed to parse ways_for_node response"
# check that the set of IDs match expectations
expected_way_ids = [way1.id,
way2.id]
found_way_ids = ways_xml.find("//osm/way").collect { |w| w["id"].to_i }
assert_equal expected_way_ids.sort, found_way_ids.sort,
"expected ways for node #{node.id} did not match found"
# check the full ways to ensure we're not missing anything
expected_way_ids.each do |id|
way_xml = ways_xml.find("//osm/way[@id='#{id}']").first
assert_ways_are_equal(Way.find(id),
Way.from_xml_node(way_xml))
end
end
##
# update the changeset_id of a way element
def update_changeset(xml, changeset_id)
xml_attr_rewrite(xml, "changeset", changeset_id)
end
##
# update an attribute in the way element
def xml_attr_rewrite(xml, name, value)
xml.find("//osm/way").first[name] = value.to_s
xml
end
##
# replace a node in a way element
def xml_replace_node(xml, old_node, new_node)
xml.find("//osm/way/nd[@ref='#{old_node}']").first["ref"] = new_node.to_s
xml
end
end
end

View file

@ -4,18 +4,6 @@ class ChangesetCommentsControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/changeset/1/comment", :method => :post },
{ :controller => "changeset_comments", :action => "create", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/changeset/comment/1/hide", :method => :post },
{ :controller => "changeset_comments", :action => "destroy", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/changeset/comment/1/unhide", :method => :post },
{ :controller => "changeset_comments", :action => "restore", :id => "1" }
)
assert_routing(
{ :path => "/changeset/1/comments/feed", :method => :get },
{ :controller => "changeset_comments", :action => "index", :id => "1", :format => "rss" }
@ -26,185 +14,6 @@ class ChangesetCommentsControllerTest < ActionController::TestCase
)
end
##
# create comment success
def test_create_comment_success
user = create(:user)
user2 = create(:user)
private_user = create(:user, :data_public => false)
suspended_user = create(:user, :suspended)
deleted_user = create(:user, :deleted)
private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
basic_authorization user.email, "test"
assert_difference "ChangesetComment.count", 1 do
assert_no_difference "ActionMailer::Base.deliveries.size" do
perform_enqueued_jobs do
post :create, :params => { :id => private_user_closed_changeset.id, :text => "This is a comment" }
end
end
end
assert_response :success
changeset = create(:changeset, :closed, :user => private_user)
changeset.subscribers.push(private_user)
changeset.subscribers.push(user)
changeset.subscribers.push(suspended_user)
changeset.subscribers.push(deleted_user)
assert_difference "ChangesetComment.count", 1 do
assert_difference "ActionMailer::Base.deliveries.size", 1 do
perform_enqueued_jobs do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
end
end
assert_response :success
email = ActionMailer::Base.deliveries.first
assert_equal 1, email.to.length
assert_equal "[OpenStreetMap] #{user.display_name} has commented on one of your changesets", email.subject
assert_equal private_user.email, email.to.first
ActionMailer::Base.deliveries.clear
basic_authorization user2.email, "test"
assert_difference "ChangesetComment.count", 1 do
assert_difference "ActionMailer::Base.deliveries.size", 2 do
perform_enqueued_jobs do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
end
end
assert_response :success
email = ActionMailer::Base.deliveries.find { |e| e.to.first == private_user.email }
assert_not_nil email
assert_equal 1, email.to.length
assert_equal "[OpenStreetMap] #{user2.display_name} has commented on one of your changesets", email.subject
email = ActionMailer::Base.deliveries.find { |e| e.to.first == user.email }
assert_not_nil email
assert_equal 1, email.to.length
assert_equal "[OpenStreetMap] #{user2.display_name} has commented on a changeset you are interested in", email.subject
ActionMailer::Base.deliveries.clear
end
##
# create comment fail
def test_create_comment_fail
# unauthorized
post :create, :params => { :id => create(:changeset, :closed).id, :text => "This is a comment" }
assert_response :unauthorized
basic_authorization create(:user).email, "test"
# bad changeset id
assert_no_difference "ChangesetComment.count" do
post :create, :params => { :id => 999111, :text => "This is a comment" }
end
assert_response :not_found
# not closed changeset
assert_no_difference "ChangesetComment.count" do
post :create, :params => { :id => create(:changeset).id, :text => "This is a comment" }
end
assert_response :conflict
# no text
assert_no_difference "ChangesetComment.count" do
post :create, :params => { :id => create(:changeset, :closed).id }
end
assert_response :bad_request
# empty text
assert_no_difference "ChangesetComment.count" do
post :create, :params => { :id => create(:changeset, :closed).id, :text => "" }
end
assert_response :bad_request
end
##
# test hide comment fail
def test_destroy_comment_fail
# unauthorized
comment = create(:changeset_comment)
assert_equal true, comment.visible
post :destroy, :params => { :id => comment.id }
assert_response :unauthorized
assert_equal true, comment.reload.visible
basic_authorization create(:user).email, "test"
# not a moderator
post :destroy, :params => { :id => comment.id }
assert_response :forbidden
assert_equal true, comment.reload.visible
basic_authorization create(:moderator_user).email, "test"
# bad comment id
post :destroy, :params => { :id => 999111 }
assert_response :not_found
assert_equal true, comment.reload.visible
end
##
# test hide comment succes
def test_hide_comment_success
comment = create(:changeset_comment)
assert_equal true, comment.visible
basic_authorization create(:moderator_user).email, "test"
post :destroy, :params => { :id => comment.id }
assert_response :success
assert_equal false, comment.reload.visible
end
##
# test unhide comment fail
def test_restore_comment_fail
# unauthorized
comment = create(:changeset_comment, :visible => false)
assert_equal false, comment.visible
post :restore, :params => { :id => comment.id }
assert_response :unauthorized
assert_equal false, comment.reload.visible
basic_authorization create(:user).email, "test"
# not a moderator
post :restore, :params => { :id => comment.id }
assert_response :forbidden
assert_equal false, comment.reload.visible
basic_authorization create(:moderator_user).email, "test"
# bad comment id
post :restore, :params => { :id => 999111 }
assert_response :not_found
assert_equal false, comment.reload.visible
end
##
# test unhide comment succes
def test_unhide_comment_success
comment = create(:changeset_comment, :visible => false)
assert_equal false, comment.visible
basic_authorization create(:moderator_user).email, "test"
post :restore, :params => { :id => comment.id }
assert_response :success
assert_equal true, comment.reload.visible
end
##
# test comments feed
def test_feed
@ -248,57 +57,4 @@ class ChangesetCommentsControllerTest < ActionController::TestCase
get :index, :params => { :format => "rss", :limit => 100001 }
assert_response :bad_request
end
# This test ensures that token capabilities behave correctly for a method that
# requires the terms to have been agreed.
# (This would be better as an integration or system testcase, since the changeset_comment
# create method is simply a stand-in for any method that requires terms agreement.
# But writing oauth tests is hard, and so it's easier to put in a controller test.)
def test_api_write_and_terms_agreed_via_token
user = create(:user, :terms_agreed => nil)
token = create(:access_token, :user => user, :allow_write_api => true)
changeset = create(:changeset, :closed)
# Hack together an oauth request - an alternative would be to sign the request properly
@request.env["oauth.version"] = 1
@request.env["oauth.strategies"] = [:token]
@request.env["oauth.token"] = token
assert_difference "ChangesetComment.count", 0 do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
assert_response :forbidden
# Try again, after agreement with the terms
user.terms_agreed = Time.now
user.save!
assert_difference "ChangesetComment.count", 1 do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
assert_response :success
end
# This test does the same as above, but with basic auth, to similarly test that the
# abilities take into account terms agreement too.
def test_api_write_and_terms_agreed_via_basic_auth
user = create(:user, :terms_agreed => nil)
changeset = create(:changeset, :closed)
basic_authorization user.email, "test"
assert_difference "ChangesetComment.count", 0 do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
assert_response :forbidden
# Try again, after agreement with the terms
user.terms_agreed = Time.now
user.save!
assert_difference "ChangesetComment.count", 1 do
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
end
assert_response :success
end
end

File diff suppressed because it is too large Load diff

View file

@ -1,550 +0,0 @@
require "test_helper"
class NodesControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/node/create", :method => :put },
{ :controller => "nodes", :action => "create" }
)
assert_routing(
{ :path => "/api/0.6/node/1", :method => :get },
{ :controller => "nodes", :action => "show", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/node/1", :method => :put },
{ :controller => "nodes", :action => "update", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/node/1", :method => :delete },
{ :controller => "nodes", :action => "delete", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/nodes", :method => :get },
{ :controller => "nodes", :action => "index" }
)
end
def test_create
private_user = create(:user, :data_public => false)
private_changeset = create(:changeset, :user => private_user)
user = create(:user)
changeset = create(:changeset, :user => user)
# create a node with random lat/lon
lat = rand(-50..50) + rand
lon = rand(-50..50) + rand
## First try with no auth
# create a minimal xml file
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
assert_difference("OldNode.count", 0) do
put :create, :body => xml
end
# hope for unauthorized
assert_response :unauthorized, "node upload did not return unauthorized status"
## Now try with the user which doesn't have their data public
basic_authorization private_user.email, "test"
# create a minimal xml file
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{private_changeset.id}'/></osm>"
assert_difference("Node.count", 0) do
put :create, :body => xml
end
# hope for success
assert_require_public_data "node create did not return forbidden status"
## Now try with the user that has the public data
basic_authorization user.email, "test"
# create a minimal xml file
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
put :create, :body => xml
# hope for success
assert_response :success, "node upload did not return success status"
# read id of created node and search for it
nodeid = @response.body
checknode = Node.find(nodeid)
assert_not_nil checknode, "uploaded node not found in data base after upload"
# compare values
assert_in_delta lat * 10000000, checknode.latitude, 1, "saved node does not match requested latitude"
assert_in_delta lon * 10000000, checknode.longitude, 1, "saved node does not match requested longitude"
assert_equal changeset.id, checknode.changeset_id, "saved node does not belong to changeset that it was created in"
assert_equal true, checknode.visible, "saved node is not visible"
end
def test_create_invalid_xml
## Only test public user here, as test_create should cover what's the forbiddens
## that would occur here
user = create(:user)
changeset = create(:changeset, :user => user)
basic_authorization user.email, "test"
lat = 3.434
lon = 3.23
# test that the upload is rejected when xml is valid, but osm doc isn't
xml = "<create/>"
put :create, :body => xml
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal "Cannot parse valid node from xml string <create/>. XML doesn't contain an osm/node element.", @response.body
# test that the upload is rejected when no lat is supplied
# create a minimal xml file
xml = "<osm><node lon='#{lon}' changeset='#{changeset.id}'/></osm>"
put :create, :body => xml
# hope for success
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal "Cannot parse valid node from xml string <node lon=\"3.23\" changeset=\"#{changeset.id}\"/>. lat missing", @response.body
# test that the upload is rejected when no lon is supplied
# create a minimal xml file
xml = "<osm><node lat='#{lat}' changeset='#{changeset.id}'/></osm>"
put :create, :body => xml
# hope for success
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal "Cannot parse valid node from xml string <node lat=\"3.434\" changeset=\"#{changeset.id}\"/>. lon missing", @response.body
# test that the upload is rejected when lat is non-numeric
# create a minimal xml file
xml = "<osm><node lat='abc' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
put :create, :body => xml
# hope for success
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal "Cannot parse valid node from xml string <node lat=\"abc\" lon=\"#{lon}\" changeset=\"#{changeset.id}\"/>. lat not a number", @response.body
# test that the upload is rejected when lon is non-numeric
# create a minimal xml file
xml = "<osm><node lat='#{lat}' lon='abc' changeset='#{changeset.id}'/></osm>"
put :create, :body => xml
# hope for success
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal "Cannot parse valid node from xml string <node lat=\"#{lat}\" lon=\"abc\" changeset=\"#{changeset.id}\"/>. lon not a number", @response.body
# test that the upload is rejected when we have a tag which is too long
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'><tag k='foo' v='#{'x' * 256}'/></node></osm>"
put :create, :body => xml
assert_response :bad_request, "node upload did not return bad_request status"
assert_equal ["NodeTag ", " v: is too long (maximum is 255 characters) (\"#{'x' * 256}\")"], @response.body.split(/[0-9]+,foo:/)
end
def test_show
# check that a visible node is returned properly
get :show, :params => { :id => create(:node).id }
assert_response :success
# check that an deleted node is not returned
get :show, :params => { :id => create(:node, :deleted).id }
assert_response :gone
# check chat a non-existent node is not returned
get :show, :params => { :id => 0 }
assert_response :not_found
end
# this tests deletion restrictions - basic deletion is tested in the unit
# tests for node!
def test_delete
private_user = create(:user, :data_public => false)
private_user_changeset = create(:changeset, :user => private_user)
private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
private_node = create(:node, :changeset => private_user_changeset)
private_deleted_node = create(:node, :deleted, :changeset => private_user_changeset)
## first try to delete node without auth
delete :delete, :params => { :id => private_node.id }
assert_response :unauthorized
## now set auth for the non-data public user
basic_authorization private_user.email, "test"
# try to delete with an invalid (closed) changeset
xml = update_changeset(private_node.to_xml, private_user_closed_changeset.id)
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data("non-public user shouldn't be able to delete node")
# try to delete with an invalid (non-existent) changeset
xml = update_changeset(private_node.to_xml, 0)
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data("shouldn't be able to delete node, when user's data is private")
# valid delete now takes a payload
xml = private_node.to_xml
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data("shouldn't be able to delete node when user's data isn't public'")
# this won't work since the node is already deleted
xml = private_deleted_node.to_xml
delete :delete, :params => { :id => private_deleted_node.id }, :body => xml.to_s
assert_require_public_data
# this won't work since the node never existed
delete :delete, :params => { :id => 0 }
assert_require_public_data
## these test whether nodes which are in-use can be deleted:
# in a way...
private_used_node = create(:node, :changeset => private_user_changeset)
create(:way_node, :node => private_used_node)
xml = private_used_node.to_xml
delete :delete, :params => { :id => private_used_node.id }, :body => xml.to_s
assert_require_public_data "shouldn't be able to delete a node used in a way (#{@response.body})"
# in a relation...
private_used_node2 = create(:node, :changeset => private_user_changeset)
create(:relation_member, :member => private_used_node2)
xml = private_used_node2.to_xml
delete :delete, :params => { :id => private_used_node2.id }, :body => xml.to_s
assert_require_public_data "shouldn't be able to delete a node used in a relation (#{@response.body})"
## now setup for the public data user
user = create(:user, :data_public => true)
changeset = create(:changeset, :user => user)
closed_changeset = create(:changeset, :closed, :user => user)
node = create(:node, :changeset => changeset)
basic_authorization user.email, "test"
# try to delete with an invalid (closed) changeset
xml = update_changeset(node.to_xml, closed_changeset.id)
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict
# try to delete with an invalid (non-existent) changeset
xml = update_changeset(node.to_xml, 0)
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict
# try to delete a node with a different ID
other_node = create(:node)
xml = other_node.to_xml
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to delete a node with a different ID from the XML"
# try to delete a node rubbish in the payloads
xml = "<delete/>"
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to delete a node without a valid XML payload"
# valid delete now takes a payload
xml = node.to_xml
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :success
# valid delete should return the new version number, which should
# be greater than the old version number
assert @response.body.to_i > node.version,
"delete request should return a new version number for node"
# deleting the same node twice doesn't work
xml = node.to_xml
delete :delete, :params => { :id => node.id }, :body => xml.to_s
assert_response :gone
# this won't work since the node never existed
delete :delete, :params => { :id => 0 }
assert_response :not_found
## these test whether nodes which are in-use can be deleted:
# in a way...
used_node = create(:node, :changeset => create(:changeset, :user => user))
way_node = create(:way_node, :node => used_node)
way_node2 = create(:way_node, :node => used_node)
xml = used_node.to_xml
delete :delete, :params => { :id => used_node.id }, :body => xml.to_s
assert_response :precondition_failed,
"shouldn't be able to delete a node used in a way (#{@response.body})"
assert_equal "Precondition failed: Node #{used_node.id} is still used by ways #{way_node.way.id},#{way_node2.way.id}.", @response.body
# in a relation...
used_node2 = create(:node, :changeset => create(:changeset, :user => user))
relation_member = create(:relation_member, :member => used_node2)
relation_member2 = create(:relation_member, :member => used_node2)
xml = used_node2.to_xml
delete :delete, :params => { :id => used_node2.id }, :body => xml.to_s
assert_response :precondition_failed,
"shouldn't be able to delete a node used in a relation (#{@response.body})"
assert_equal "Precondition failed: Node #{used_node2.id} is still used by relations #{relation_member.relation.id},#{relation_member2.relation.id}.", @response.body
end
##
# tests whether the API works and prevents incorrect use while trying
# to update nodes.
def test_update
## First test with no user credentials
# try and update a node without authorisation
# first try to delete node without auth
private_user = create(:user, :data_public => false)
private_node = create(:node, :changeset => create(:changeset, :user => private_user))
user = create(:user)
node = create(:node, :changeset => create(:changeset, :user => user))
xml = node.to_xml
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :unauthorized
## Second test with the private user
# setup auth
basic_authorization private_user.email, "test"
## trying to break changesets
# try and update in someone else's changeset
xml = update_changeset(private_node.to_xml,
create(:changeset).id)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "update with other user's changeset should be forbidden when data isn't public"
# try and update in a closed changeset
xml = update_changeset(private_node.to_xml,
create(:changeset, :closed, :user => private_user).id)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
# try and update in a non-existant changeset
xml = update_changeset(private_node.to_xml, 0)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "update with changeset=0 should be forbidden, when data isn't public"
## try and submit invalid updates
xml = xml_attr_rewrite(private_node.to_xml, "lat", 91.0)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "node at lat=91 should be forbidden, when data isn't public"
xml = xml_attr_rewrite(private_node.to_xml, "lat", -91.0)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "node at lat=-91 should be forbidden, when data isn't public"
xml = xml_attr_rewrite(private_node.to_xml, "lon", 181.0)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "node at lon=181 should be forbidden, when data isn't public"
xml = xml_attr_rewrite(private_node.to_xml, "lon", -181.0)
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "node at lon=-181 should be forbidden, when data isn't public"
## finally, produce a good request which still won't work
xml = private_node.to_xml
put :update, :params => { :id => private_node.id }, :body => xml.to_s
assert_require_public_data "should have failed with a forbidden when data isn't public"
## Finally test with the public user
# try and update a node without authorisation
# first try to update node without auth
xml = node.to_xml
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :forbidden
# setup auth
basic_authorization user.email, "test"
## trying to break changesets
# try and update in someone else's changeset
xml = update_changeset(node.to_xml,
create(:changeset).id)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict, "update with other user's changeset should be rejected"
# try and update in a closed changeset
xml = update_changeset(node.to_xml,
create(:changeset, :closed, :user => user).id)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict, "update with closed changeset should be rejected"
# try and update in a non-existant changeset
xml = update_changeset(node.to_xml, 0)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict, "update with changeset=0 should be rejected"
## try and submit invalid updates
xml = xml_attr_rewrite(node.to_xml, "lat", 91.0)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request, "node at lat=91 should be rejected"
xml = xml_attr_rewrite(node.to_xml, "lat", -91.0)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request, "node at lat=-91 should be rejected"
xml = xml_attr_rewrite(node.to_xml, "lon", 181.0)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request, "node at lon=181 should be rejected"
xml = xml_attr_rewrite(node.to_xml, "lon", -181.0)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request, "node at lon=-181 should be rejected"
## next, attack the versioning
current_node_version = node.version
# try and submit a version behind
xml = xml_attr_rewrite(node.to_xml,
"version", current_node_version - 1)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict, "should have failed on old version number"
# try and submit a version ahead
xml = xml_attr_rewrite(node.to_xml,
"version", current_node_version + 1)
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict, "should have failed on skipped version number"
# try and submit total crap in the version field
xml = xml_attr_rewrite(node.to_xml,
"version", "p1r4t3s!")
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :conflict,
"should not be able to put 'p1r4at3s!' in the version field"
## try an update with the wrong ID
xml = create(:node).to_xml
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to update a node with a different ID from the XML"
## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
xml = "<update/>"
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to update a node with non-OSM XML doc."
## finally, produce a good request which should work
xml = node.to_xml
put :update, :params => { :id => node.id }, :body => xml.to_s
assert_response :success, "a valid update request failed"
end
##
# test fetching multiple nodes
def test_index
node1 = create(:node)
node2 = create(:node, :deleted)
node3 = create(:node)
node4 = create(:node, :with_history, :version => 2)
node5 = create(:node, :deleted, :with_history, :version => 2)
# check error when no parameter provided
get :index
assert_response :bad_request
# check error when no parameter value provided
get :index, :params => { :nodes => "" }
assert_response :bad_request
# test a working call
get :index, :params => { :nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id}" }
assert_response :success
assert_select "osm" do
assert_select "node", :count => 5
assert_select "node[id='#{node1.id}'][visible='true']", :count => 1
assert_select "node[id='#{node2.id}'][visible='false']", :count => 1
assert_select "node[id='#{node3.id}'][visible='true']", :count => 1
assert_select "node[id='#{node4.id}'][visible='true']", :count => 1
assert_select "node[id='#{node5.id}'][visible='false']", :count => 1
end
# check error when a non-existent node is included
get :index, :params => { :nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id},0" }
assert_response :not_found
end
##
# test adding tags to a node
def test_duplicate_tags
existing_tag = create(:node_tag)
assert_equal true, existing_tag.node.changeset.user.data_public
# setup auth
basic_authorization existing_tag.node.changeset.user.email, "test"
# add an identical tag to the node
tag_xml = XML::Node.new("tag")
tag_xml["k"] = existing_tag.k
tag_xml["v"] = existing_tag.v
# add the tag into the existing xml
node_xml = existing_tag.node.to_xml
node_xml.find("//osm/node").first << tag_xml
# try and upload it
put :update, :params => { :id => existing_tag.node.id }, :body => node_xml.to_s
assert_response :bad_request,
"adding duplicate tags to a node should fail with 'bad request'"
assert_equal "Element node/#{existing_tag.node.id} has duplicate tags with key #{existing_tag.k}", @response.body
end
# test whether string injection is possible
def test_string_injection
private_user = create(:user, :data_public => false)
private_changeset = create(:changeset, :user => private_user)
user = create(:user)
changeset = create(:changeset, :user => user)
## First try with the non-data public user
basic_authorization private_user.email, "test"
# try and put something into a string that the API might
# use unquoted and therefore allow code injection...
xml = "<osm><node lat='0' lon='0' changeset='#{private_changeset.id}'>" \
'<tag k="#{@user.inspect}" v="0"/>' \
"</node></osm>"
put :create, :body => xml
assert_require_public_data "Shouldn't be able to create with non-public user"
## Then try with the public data user
basic_authorization user.email, "test"
# try and put something into a string that the API might
# use unquoted and therefore allow code injection...
xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'>" \
'<tag k="#{@user.inspect}" v="0"/>' \
"</node></osm>"
put :create, :body => xml
assert_response :success
nodeid = @response.body
# find the node in the database
checknode = Node.find(nodeid)
assert_not_nil checknode, "node not found in data base after upload"
# and grab it using the api
get :show, :params => { :id => nodeid }
assert_response :success
apinode = Node.from_xml(@response.body)
assert_not_nil apinode, "downloaded node is nil, but shouldn't be"
# check the tags are not corrupted
assert_equal checknode.tags, apinode.tags
assert apinode.tags.include?("\#{@user.inspect}")
end
##
# update the changeset_id of a node element
def update_changeset(xml, changeset_id)
xml_attr_rewrite(xml, "changeset", changeset_id)
end
##
# update an attribute in the node element
def xml_attr_rewrite(xml, name, value)
xml.find("//osm/node").first[name] = value.to_s
xml
end
##
# parse some xml
def xml_parse(xml)
parser = XML::Parser.string(xml)
parser.parse
end
end

File diff suppressed because it is too large Load diff

View file

@ -1,428 +0,0 @@
require "test_helper"
class OldNodesControllerTest < ActionController::TestCase
#
# TODO: test history
#
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/node/1/history", :method => :get },
{ :controller => "old_nodes", :action => "history", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/node/1/2", :method => :get },
{ :controller => "old_nodes", :action => "version", :id => "1", :version => "2" }
)
assert_routing(
{ :path => "/api/0.6/node/1/2/redact", :method => :post },
{ :controller => "old_nodes", :action => "redact", :id => "1", :version => "2" }
)
end
##
# test the version call by submitting several revisions of a new node
# to the API and ensuring that later calls to version return the
# matching versions of the object.
#
##
# FIXME: Move this test to being an integration test since it spans multiple controllers
def test_version
private_user = create(:user, :data_public => false)
private_node = create(:node, :with_history, :version => 4, :changeset => create(:changeset, :user => private_user))
user = create(:user)
node = create(:node, :with_history, :version => 4, :changeset => create(:changeset, :user => user))
create_list(:node_tag, 2, :node => node)
# Ensure that the current tags are propagated to the history too
propagate_tags(node, node.old_nodes.last)
## First try this with a non-public user
basic_authorization private_user.email, "test"
# setup a simple XML node
xml_doc = private_node.to_xml
xml_node = xml_doc.find("//osm/node").first
nodeid = private_node.id
# keep a hash of the versions => string, as we'll need something
# to test against later
versions = {}
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
# randomly move the node about
3.times do
# move the node somewhere else
xml_node["lat"] = precision(rand * 180 - 90).to_s
xml_node["lon"] = precision(rand * 360 - 180).to_s
with_controller(NodesController.new) do
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
assert_response :forbidden, "Should have rejected node update"
xml_node["version"] = @response.body.to_s
end
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
end
# add a bunch of random tags
3.times do
xml_tag = XML::Node.new("tag")
xml_tag["k"] = random_string
xml_tag["v"] = random_string
xml_node << xml_tag
with_controller(NodesController.new) do
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
assert_response :forbidden,
"should have rejected node #{nodeid} (#{@response.body}) with forbidden"
xml_node["version"] = @response.body.to_s
end
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
end
# probably should check that they didn't get written to the database
## Now do it with the public user
basic_authorization user.email, "test"
# setup a simple XML node
xml_doc = node.to_xml
xml_node = xml_doc.find("//osm/node").first
nodeid = node.id
# keep a hash of the versions => string, as we'll need something
# to test against later
versions = {}
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
# randomly move the node about
3.times do
# move the node somewhere else
xml_node["lat"] = precision(rand * 180 - 90).to_s
xml_node["lon"] = precision(rand * 360 - 180).to_s
with_controller(NodesController.new) do
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
assert_response :success
xml_node["version"] = @response.body.to_s
end
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
end
# add a bunch of random tags
3.times do
xml_tag = XML::Node.new("tag")
xml_tag["k"] = random_string
xml_tag["v"] = random_string
xml_node << xml_tag
with_controller(NodesController.new) do
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
assert_response :success,
"couldn't update node #{nodeid} (#{@response.body})"
xml_node["version"] = @response.body.to_s
end
# save a version for later checking
versions[xml_node["version"]] = xml_doc.to_s
end
# check all the versions
versions.each_key do |key|
get :version, :params => { :id => nodeid, :version => key.to_i }
assert_response :success,
"couldn't get version #{key.to_i} of node #{nodeid}"
check_node = Node.from_xml(versions[key])
api_node = Node.from_xml(@response.body.to_s)
assert_nodes_are_equal check_node, api_node
end
end
def test_not_found_version
check_not_found_id_version(70000, 312344)
check_not_found_id_version(-1, -13)
check_not_found_id_version(create(:node).id, 24354)
check_not_found_id_version(24356, create(:node).version)
end
def check_not_found_id_version(id, version)
get :version, :params => { :id => id, :version => version }
assert_response :not_found
rescue ActionController::UrlGenerationError => ex
assert_match(/No route matches/, ex.to_s)
end
##
# Test that getting the current version is identical to picking
# that version with the version URI call.
def test_current_version
node = create(:node, :with_history)
used_node = create(:node, :with_history)
create(:way_node, :node => used_node)
node_used_by_relationship = create(:node, :with_history)
create(:relation_member, :member => node_used_by_relationship)
node_with_versions = create(:node, :with_history, :version => 4)
create(:node_tag, :node => node)
create(:node_tag, :node => used_node)
create(:node_tag, :node => node_used_by_relationship)
create(:node_tag, :node => node_with_versions)
propagate_tags(node, node.old_nodes.last)
propagate_tags(used_node, used_node.old_nodes.last)
propagate_tags(node_used_by_relationship, node_used_by_relationship.old_nodes.last)
propagate_tags(node_with_versions, node_with_versions.old_nodes.last)
check_current_version(node)
check_current_version(used_node)
check_current_version(node_used_by_relationship)
check_current_version(node_with_versions)
end
##
# test the redaction of an old version of a node, while not being
# authorised.
def test_redact_node_unauthorised
node = create(:node, :with_history, :version => 4)
node_v3 = node.old_nodes.find_by(:version => 3)
do_redact_node(node_v3,
create(:redaction))
assert_response :unauthorized, "should need to be authenticated to redact."
end
##
# test the redaction of an old version of a node, while being
# authorised as a normal user.
def test_redact_node_normal_user
basic_authorization create(:user).email, "test"
node = create(:node, :with_history, :version => 4)
node_v3 = node.old_nodes.find_by(:version => 3)
do_redact_node(node_v3,
create(:redaction))
assert_response :forbidden, "should need to be moderator to redact."
end
##
# test that, even as moderator, the current version of a node
# can't be redacted.
def test_redact_node_current_version
basic_authorization create(:moderator_user).email, "test"
node = create(:node, :with_history, :version => 4)
node_v4 = node.old_nodes.find_by(:version => 4)
do_redact_node(node_v4,
create(:redaction))
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
end
##
# test that redacted nodes aren't visible, regardless of
# authorisation except as moderator...
def test_version_redacted
node = create(:node, :with_history, :version => 2)
node_v1 = node.old_nodes.find_by(:version => 1)
node_v1.redact!(create(:redaction))
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :forbidden, "Redacted node shouldn't be visible via the version API, even when logged in."
end
##
# test that redacted nodes aren't visible in the history
def test_history_redacted
node = create(:node, :with_history, :version => 2)
node_v1 = node.old_nodes.find_by(:version => 1)
node_v1.redact!(create(:redaction))
get :history, :params => { :id => node_v1.node_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 0, "redacted node #{node_v1.node_id} version #{node_v1.version} shouldn't be present in the history."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :history, :params => { :id => node_v1.node_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 0, "redacted node #{node_v1.node_id} version #{node_v1.version} shouldn't be present in the history, even when logged in."
end
##
# test the redaction of an old version of a node, while being
# authorised as a moderator.
def test_redact_node_moderator
node = create(:node, :with_history, :version => 4)
node_v3 = node.old_nodes.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_node(node_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# check moderator can still see the redacted data, when passing
# the appropriate flag
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version }
assert_response :forbidden, "After redaction, node should be gone for moderator, when flag not passed."
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version, :show_redactions => "true" }
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
# and when accessed via history
get :history, :params => { :id => node_v3.node_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 0, "node #{node_v3.node_id} version #{node_v3.version} should not be present in the history for moderators when not passing flag."
get :history, :params => { :id => node_v3.node_id, :show_redactions => "true" }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 1, "node #{node_v3.node_id} version #{node_v3.version} should still be present in the history for moderators when passing flag."
end
# testing that if the moderator drops auth, he can't see the
# redacted stuff any more.
def test_redact_node_is_redacted
node = create(:node, :with_history, :version => 4)
node_v3 = node.old_nodes.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_node(node_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# re-auth as non-moderator
basic_authorization create(:user).email, "test"
# check can't see the redacted data
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version }
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
# and when accessed via history
get :history, :params => { :id => node_v3.node_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 0, "redacted node #{node_v3.node_id} version #{node_v3.version} shouldn't be present in the history."
end
##
# test the unredaction of an old version of a node, while not being
# authorised.
def test_unredact_node_unauthorised
node = create(:node, :with_history, :version => 2)
node_v1 = node.old_nodes.find_by(:version => 1)
node_v1.redact!(create(:redaction))
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :unauthorized, "should need to be authenticated to unredact."
end
##
# test the unredaction of an old version of a node, while being
# authorised as a normal user.
def test_unredact_node_normal_user
user = create(:user)
node = create(:node, :with_history, :version => 2)
node_v1 = node.old_nodes.find_by(:version => 1)
node_v1.redact!(create(:redaction))
basic_authorization user.email, "test"
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :forbidden, "should need to be moderator to unredact."
end
##
# test the unredaction of an old version of a node, while being
# authorised as a moderator.
def test_unredact_node_moderator
moderator_user = create(:moderator_user)
node = create(:node, :with_history, :version => 2)
node_v1 = node.old_nodes.find_by(:version => 1)
node_v1.redact!(create(:redaction))
basic_authorization moderator_user.email, "test"
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :success, "should be OK to unredact old version as moderator."
# check moderator can now see the redacted data, when not
# passing the aspecial flag
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :success, "After unredaction, node should not be gone for moderator."
# and when accessed via history
get :history, :params => { :id => node_v1.node_id }
assert_response :success, "Unredaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 1, "node #{node_v1.node_id} version #{node_v1.version} should now be present in the history for moderators without passing flag."
basic_authorization create(:user).email, "test"
# check normal user can now see the redacted data
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
assert_response :success, "After unredaction, node should be visible to normal users."
# and when accessed via history
get :history, :params => { :id => node_v1.node_id }
assert_response :success, "Unredaction shouldn't have stopped history working."
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 1, "node #{node_v1.node_id} version #{node_v1.version} should now be present in the history for normal users without passing flag."
end
private
def do_redact_node(node, redaction)
get :version, :params => { :id => node.node_id, :version => node.version }
assert_response :success, "should be able to get version #{node.version} of node #{node.node_id}."
# now redact it
post :redact, :params => { :id => node.node_id, :version => node.version, :redaction => redaction.id }
end
def check_current_version(node_id)
# get the current version of the node
current_node = with_controller(NodesController.new) do
get :show, :params => { :id => node_id }
assert_response :success, "cant get current node #{node_id}"
Node.from_xml(@response.body)
end
assert_not_nil current_node, "getting node #{node_id} returned nil"
# get the "old" version of the node from the old_node interface
get :version, :params => { :id => node_id, :version => current_node.version }
assert_response :success, "cant get old node #{node_id}, v#{current_node.version}"
old_node = Node.from_xml(@response.body)
# check the nodes are the same
assert_nodes_are_equal current_node, old_node
end
##
# returns a 16 character long string with some nasty characters in it.
# this ought to stress-test the tag handling as well as the versioning.
def random_string
letters = [["!", '"', "$", "&", ";", "@"],
("a".."z").to_a,
("A".."Z").to_a,
("0".."9").to_a].flatten
(1..16).map { |_i| letters[rand(letters.length)] }.join
end
##
# truncate a floating point number to the scale that it is stored in
# the database. otherwise rounding errors can produce failing unit
# tests when they shouldn't.
def precision(f)
(f * GeoRecord::SCALE).round.to_f / GeoRecord::SCALE
end
def propagate_tags(node, old_node)
node.tags.each do |k, v|
create(:old_node_tag, :old_node => old_node, :k => k, :v => v)
end
end
end

View file

@ -1,272 +0,0 @@
require "test_helper"
class OldRelationsControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/relation/1/history", :method => :get },
{ :controller => "old_relations", :action => "history", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/relation/1/2", :method => :get },
{ :controller => "old_relations", :action => "version", :id => "1", :version => "2" }
)
assert_routing(
{ :path => "/api/0.6/relation/1/2/redact", :method => :post },
{ :controller => "old_relations", :action => "redact", :id => "1", :version => "2" }
)
end
# -------------------------------------
# Test reading old relations.
# -------------------------------------
def test_history
# check that a visible relations is returned properly
get :history, :params => { :id => create(:relation, :with_history).id }
assert_response :success
# check chat a non-existent relations is not returned
get :history, :params => { :id => 0 }
assert_response :not_found
end
##
# test the redaction of an old version of a relation, while not being
# authorised.
def test_redact_relation_unauthorised
relation = create(:relation, :with_history, :version => 4)
relation_v3 = relation.old_relations.find_by(:version => 3)
do_redact_relation(relation_v3, create(:redaction))
assert_response :unauthorized, "should need to be authenticated to redact."
end
##
# test the redaction of an old version of a relation, while being
# authorised as a normal user.
def test_redact_relation_normal_user
relation = create(:relation, :with_history, :version => 4)
relation_v3 = relation.old_relations.find_by(:version => 3)
basic_authorization create(:user).email, "test"
do_redact_relation(relation_v3, create(:redaction))
assert_response :forbidden, "should need to be moderator to redact."
end
##
# test that, even as moderator, the current version of a relation
# can't be redacted.
def test_redact_relation_current_version
relation = create(:relation, :with_history, :version => 4)
relation_latest = relation.old_relations.last
basic_authorization create(:moderator_user).email, "test"
do_redact_relation(relation_latest, create(:redaction))
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
end
##
# test that redacted relations aren't visible, regardless of
# authorisation except as moderator...
def test_version_redacted
relation = create(:relation, :with_history, :version => 2)
relation_v1 = relation.old_relations.find_by(:version => 1)
relation_v1.redact!(create(:redaction))
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API, even when logged in."
end
##
# test that redacted relations aren't visible in the history
def test_history_redacted
relation = create(:relation, :with_history, :version => 2)
relation_v1 = relation.old_relations.find_by(:version => 1)
relation_v1.redact!(create(:redaction))
get :history, :params => { :id => relation_v1.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 0, "redacted relation #{relation_v1.relation_id} version #{relation_v1.version} shouldn't be present in the history."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
get :history, :params => { :id => relation_v1.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 0, "redacted relation #{relation_v1.relation_id} version #{relation_v1.version} shouldn't be present in the history, even when logged in."
end
##
# test the redaction of an old version of a relation, while being
# authorised as a moderator.
def test_redact_relation_moderator
relation = create(:relation, :with_history, :version => 4)
relation_v3 = relation.old_relations.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_relation(relation_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# check moderator can still see the redacted data, when passing
# the appropriate flag
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version }
assert_response :forbidden, "After redaction, relation should be gone for moderator, when flag not passed."
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version, :show_redactions => "true" }
assert_response :success, "After redaction, relation should not be gone for moderator, when flag passed."
# and when accessed via history
get :history, :params => { :id => relation_v3.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 0, "relation #{relation_v3.relation_id} version #{relation_v3.version} should not be present in the history for moderators when not passing flag."
get :history, :params => { :id => relation_v3.relation_id, :show_redactions => "true" }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 1, "relation #{relation_v3.relation_id} version #{relation_v3.version} should still be present in the history for moderators when passing flag."
end
# testing that if the moderator drops auth, he can't see the
# redacted stuff any more.
def test_redact_relation_is_redacted
relation = create(:relation, :with_history, :version => 4)
relation_v3 = relation.old_relations.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_relation(relation_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# re-auth as non-moderator
basic_authorization create(:user).email, "test"
# check can't see the redacted data
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version }
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API."
# and when accessed via history
get :history, :params => { :id => relation_v3.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 0, "redacted relation #{relation_v3.relation_id} version #{relation_v3.version} shouldn't be present in the history."
end
##
# test the unredaction of an old version of a relation, while not being
# authorised.
def test_unredact_relation_unauthorised
relation = create(:relation, :with_history, :version => 2)
relation_v1 = relation.old_relations.find_by(:version => 1)
relation_v1.redact!(create(:redaction))
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :unauthorized, "should need to be authenticated to unredact."
end
##
# test the unredaction of an old version of a relation, while being
# authorised as a normal user.
def test_unredact_relation_normal_user
relation = create(:relation, :with_history, :version => 2)
relation_v1 = relation.old_relations.find_by(:version => 1)
relation_v1.redact!(create(:redaction))
basic_authorization create(:user).email, "test"
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :forbidden, "should need to be moderator to unredact."
end
##
# test the unredaction of an old version of a relation, while being
# authorised as a moderator.
def test_unredact_relation_moderator
relation = create(:relation, :with_history, :version => 2)
relation_v1 = relation.old_relations.find_by(:version => 1)
relation_v1.redact!(create(:redaction))
basic_authorization create(:moderator_user).email, "test"
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :success, "should be OK to unredact old version as moderator."
# check moderator can still see the redacted data, without passing
# the appropriate flag
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :success, "After unredaction, relation should not be gone for moderator."
# and when accessed via history
get :history, :params => { :id => relation_v1.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 1, "relation #{relation_v1.relation_id} version #{relation_v1.version} should still be present in the history for moderators."
basic_authorization create(:user).email, "test"
# check normal user can now see the redacted data
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
assert_response :success, "After redaction, node should not be gone for normal user."
# and when accessed via history
get :history, :params => { :id => relation_v1.relation_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 1, "relation #{relation_v1.relation_id} version #{relation_v1.version} should still be present in the history for normal users."
end
private
##
# check that the current version of a relation is equivalent to the
# version which we're getting from the versions call.
def check_current_version(relation_id)
# get the current version
current_relation = with_controller(RelationsController.new) do
get :show, :params => { :id => relation_id }
assert_response :success, "can't get current relation #{relation_id}"
Relation.from_xml(@response.body)
end
assert_not_nil current_relation, "getting relation #{relation_id} returned nil"
# get the "old" version of the relation from the version method
get :version, :params => { :id => relation_id, :version => current_relation.version }
assert_response :success, "can't get old relation #{relation_id}, v#{current_relation.version}"
old_relation = Relation.from_xml(@response.body)
# check that the relations are identical
assert_relations_are_equal current_relation, old_relation
end
##
# look at all the versions of the relation in the history and get each version from
# the versions call. check that they're the same.
def check_history_equals_versions(relation_id)
get :history, :params => { :id => relation_id }
assert_response :success, "can't get relation #{relation_id} from API"
history_doc = XML::Parser.string(@response.body).parse
assert_not_nil history_doc, "parsing relation #{relation_id} history failed"
history_doc.find("//osm/relation").each do |relation_doc|
history_relation = Relation.from_xml_node(relation_doc)
assert_not_nil history_relation, "parsing relation #{relation_id} version failed"
get :version, :params => { :id => relation_id, :version => history_relation.version }
assert_response :success, "couldn't get relation #{relation_id}, v#{history_relation.version}"
version_relation = Relation.from_xml(@response.body)
assert_not_nil version_relation, "failed to parse #{relation_id}, v#{history_relation.version}"
assert_relations_are_equal history_relation, version_relation
end
end
def do_redact_relation(relation, redaction)
get :version, :params => { :id => relation.relation_id, :version => relation.version }
assert_response :success, "should be able to get version #{relation.version} of relation #{relation.relation_id}."
# now redact it
post :redact, :params => { :id => relation.relation_id, :version => relation.version, :redaction => redaction.id }
end
end

View file

@ -1,318 +0,0 @@
require "test_helper"
class OldWaysControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/way/1/history", :method => :get },
{ :controller => "old_ways", :action => "history", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/way/1/2", :method => :get },
{ :controller => "old_ways", :action => "version", :id => "1", :version => "2" }
)
assert_routing(
{ :path => "/api/0.6/way/1/2/redact", :method => :post },
{ :controller => "old_ways", :action => "redact", :id => "1", :version => "2" }
)
end
# -------------------------------------
# Test reading old ways.
# -------------------------------------
def test_history_visible
# check that a visible way is returned properly
get :history, :params => { :id => create(:way, :with_history).id }
assert_response :success
end
def test_history_invisible
# check that an invisible way's history is returned properly
get :history, :params => { :id => create(:way, :with_history, :deleted).id }
assert_response :success
end
def test_history_invalid
# check chat a non-existent way is not returned
get :history, :params => { :id => 0 }
assert_response :not_found
end
##
# check that we can retrieve versions of a way
def test_version
way = create(:way, :with_history)
used_way = create(:way, :with_history)
create(:relation_member, :member => used_way)
way_with_versions = create(:way, :with_history, :version => 4)
create(:way_tag, :way => way)
create(:way_tag, :way => used_way)
create(:way_tag, :way => way_with_versions)
propagate_tags(way, way.old_ways.last)
propagate_tags(used_way, used_way.old_ways.last)
propagate_tags(way_with_versions, way_with_versions.old_ways.last)
check_current_version(way.id)
check_current_version(used_way.id)
check_current_version(way_with_versions.id)
end
##
# check that returned history is the same as getting all
# versions of a way from the api.
def test_history_equals_versions
way = create(:way, :with_history)
used_way = create(:way, :with_history)
create(:relation_member, :member => used_way)
way_with_versions = create(:way, :with_history, :version => 4)
check_history_equals_versions(way.id)
check_history_equals_versions(used_way.id)
check_history_equals_versions(way_with_versions.id)
end
##
# test the redaction of an old version of a way, while not being
# authorised.
def test_redact_way_unauthorised
way = create(:way, :with_history, :version => 4)
way_v3 = way.old_ways.find_by(:version => 3)
do_redact_way(way_v3, create(:redaction))
assert_response :unauthorized, "should need to be authenticated to redact."
end
##
# test the redaction of an old version of a way, while being
# authorised as a normal user.
def test_redact_way_normal_user
basic_authorization create(:user).email, "test"
way = create(:way, :with_history, :version => 4)
way_v3 = way.old_ways.find_by(:version => 3)
do_redact_way(way_v3, create(:redaction))
assert_response :forbidden, "should need to be moderator to redact."
end
##
# test that, even as moderator, the current version of a way
# can't be redacted.
def test_redact_way_current_version
basic_authorization create(:moderator_user).email, "test"
way = create(:way, :with_history, :version => 4)
way_latest = way.old_ways.last
do_redact_way(way_latest, create(:redaction))
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
end
##
# test that redacted ways aren't visible, regardless of
# authorisation except as moderator...
def test_version_redacted
way = create(:way, :with_history, :version => 2)
way_v1 = way.old_ways.find_by(:version => 1)
way_v1.redact!(create(:redaction))
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :forbidden, "Redacted way shouldn't be visible via the version API."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :forbidden, "Redacted way shouldn't be visible via the version API, even when logged in."
end
##
# test that redacted ways aren't visible in the history
def test_history_redacted
way = create(:way, :with_history, :version => 2)
way_v1 = way.old_ways.find_by(:version => 1)
way_v1.redact!(create(:redaction))
get :history, :params => { :id => way_v1.way_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 0, "redacted way #{way_v1.way_id} version #{way_v1.version} shouldn't be present in the history."
# not even to a logged-in user
basic_authorization create(:user).email, "test"
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
get :history, :params => { :id => way_v1.way_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 0, "redacted node #{way_v1.way_id} version #{way_v1.version} shouldn't be present in the history, even when logged in."
end
##
# test the redaction of an old version of a way, while being
# authorised as a moderator.
def test_redact_way_moderator
way = create(:way, :with_history, :version => 4)
way_v3 = way.old_ways.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_way(way_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# check moderator can still see the redacted data, when passing
# the appropriate flag
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version }
assert_response :forbidden, "After redaction, node should be gone for moderator, when flag not passed."
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version, :show_redactions => "true" }
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
# and when accessed via history
get :history, :params => { :id => way_v3.way_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 0, "way #{way_v3.way_id} version #{way_v3.version} should not be present in the history for moderators when not passing flag."
get :history, :params => { :id => way_v3.way_id, :show_redactions => "true" }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 1, "way #{way_v3.way_id} version #{way_v3.version} should still be present in the history for moderators when passing flag."
end
# testing that if the moderator drops auth, he can't see the
# redacted stuff any more.
def test_redact_way_is_redacted
way = create(:way, :with_history, :version => 4)
way_v3 = way.old_ways.find_by(:version => 3)
basic_authorization create(:moderator_user).email, "test"
do_redact_way(way_v3, create(:redaction))
assert_response :success, "should be OK to redact old version as moderator."
# re-auth as non-moderator
basic_authorization create(:user).email, "test"
# check can't see the redacted data
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version }
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
# and when accessed via history
get :history, :params => { :id => way_v3.way_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 0, "redacted way #{way_v3.way_id} version #{way_v3.version} shouldn't be present in the history."
end
##
# test the unredaction of an old version of a way, while not being
# authorised.
def test_unredact_way_unauthorised
way = create(:way, :with_history, :version => 2)
way_v1 = way.old_ways.find_by(:version => 1)
way_v1.redact!(create(:redaction))
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :unauthorized, "should need to be authenticated to unredact."
end
##
# test the unredaction of an old version of a way, while being
# authorised as a normal user.
def test_unredact_way_normal_user
way = create(:way, :with_history, :version => 2)
way_v1 = way.old_ways.find_by(:version => 1)
way_v1.redact!(create(:redaction))
basic_authorization create(:user).email, "test"
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :forbidden, "should need to be moderator to unredact."
end
##
# test the unredaction of an old version of a way, while being
# authorised as a moderator.
def test_unredact_way_moderator
moderator_user = create(:moderator_user)
way = create(:way, :with_history, :version => 2)
way_v1 = way.old_ways.find_by(:version => 1)
way_v1.redact!(create(:redaction))
basic_authorization moderator_user.email, "test"
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :success, "should be OK to unredact old version as moderator."
# check moderator can still see the unredacted data, without passing
# the appropriate flag
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :success, "After unredaction, node should not be gone for moderator."
# and when accessed via history
get :history, :params => { :id => way_v1.way_id }
assert_response :success, "Unredaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 1, "way #{way_v1.way_id} version #{way_v1.version} should still be present in the history for moderators."
basic_authorization create(:user).email, "test"
# check normal user can now see the unredacted data
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
# and when accessed via history
get :history, :params => { :id => way_v1.way_id }
assert_response :success, "Redaction shouldn't have stopped history working."
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 1, "way #{way_v1.way_id} version #{way_v1.version} should still be present in the history for normal users."
end
private
##
# check that the current version of a way is equivalent to the
# version which we're getting from the versions call.
def check_current_version(way_id)
# get the current version
current_way = with_controller(WaysController.new) do
get :show, :params => { :id => way_id }
assert_response :success, "can't get current way #{way_id}"
Way.from_xml(@response.body)
end
assert_not_nil current_way, "getting way #{way_id} returned nil"
# get the "old" version of the way from the version method
get :version, :params => { :id => way_id, :version => current_way.version }
assert_response :success, "can't get old way #{way_id}, v#{current_way.version}"
old_way = Way.from_xml(@response.body)
# check that the ways are identical
assert_ways_are_equal current_way, old_way
end
##
# look at all the versions of the way in the history and get each version from
# the versions call. check that they're the same.
def check_history_equals_versions(way_id)
get :history, :params => { :id => way_id }
assert_response :success, "can't get way #{way_id} from API"
history_doc = XML::Parser.string(@response.body).parse
assert_not_nil history_doc, "parsing way #{way_id} history failed"
history_doc.find("//osm/way").each do |way_doc|
history_way = Way.from_xml_node(way_doc)
assert_not_nil history_way, "parsing way #{way_id} version failed"
get :version, :params => { :id => way_id, :version => history_way.version }
assert_response :success, "couldn't get way #{way_id}, v#{history_way.version}"
version_way = Way.from_xml(@response.body)
assert_not_nil version_way, "failed to parse #{way_id}, v#{history_way.version}"
assert_ways_are_equal history_way, version_way
end
end
def do_redact_way(way, redaction)
get :version, :params => { :id => way.way_id, :version => way.version }
assert_response :success, "should be able to get version #{way.version} of way #{way.way_id}."
# now redact it
post :redact, :params => { :id => way.way_id, :version => way.version, :redaction => redaction.id }
end
def propagate_tags(way, old_way)
way.tags.each do |k, v|
create(:old_way_tag, :old_way => old_way, :k => k, :v => v)
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -1,106 +0,0 @@
require "test_helper"
class SearchControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/search", :method => :get },
{ :controller => "search", :action => "search_all" }
)
assert_routing(
{ :path => "/api/0.6/nodes/search", :method => :get },
{ :controller => "search", :action => "search_nodes" }
)
assert_routing(
{ :path => "/api/0.6/ways/search", :method => :get },
{ :controller => "search", :action => "search_ways" }
)
assert_routing(
{ :path => "/api/0.6/relations/search", :method => :get },
{ :controller => "search", :action => "search_relations" }
)
end
##
# test searching nodes
def test_search_nodes
get :search_nodes, :params => { :type => "test" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
get :search_nodes, :params => { :type => "test", :value => "yes" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
get :search_nodes, :params => { :name => "Test Node" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
end
##
# test searching ways
def test_search_ways
first_way = create(:way_with_nodes, :nodes_count => 2)
deleted_way = create(:way_with_nodes, :deleted, :nodes_count => 2)
third_way = create(:way_with_nodes, :nodes_count => 2)
[first_way, deleted_way, third_way].each do |way|
create(:way_tag, :way => way, :k => "test", :v => "yes")
end
create(:way_tag, :way => third_way, :k => "name", :v => "Test Way")
get :search_ways, :params => { :type => "test" }
assert_response :service_unavailable
assert_equal "Searching for a key without value is currently unavailable", response.headers["Error"]
get :search_ways, :params => { :type => "test", :value => "yes" }
assert_response :success
assert_select "way", 3
get :search_ways, :params => { :name => "Test Way" }
assert_response :success
assert_select "way", 1
end
##
# test searching relations
def test_search_relations
first_relation = create(:relation)
deleted_relation = create(:relation)
third_relation = create(:relation)
[first_relation, deleted_relation, third_relation].each do |relation|
create(:relation_tag, :relation => relation, :k => "test", :v => "yes")
end
create(:relation_tag, :relation => third_relation, :k => "name", :v => "Test Relation")
get :search_relations, :params => { :type => "test" }
assert_response :service_unavailable
assert_equal "Searching for a key without value is currently unavailable", response.headers["Error"]
get :search_relations, :params => { :type => "test", :value => "yes" }
assert_response :success
assert_select "relation", 3
get :search_relations, :params => { :name => "Test Relation" }
assert_response :success
assert_select "relation", 1
end
##
# test searching nodes, ways and relations
def test_search_all
get :search_all, :params => { :type => "test" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
get :search_all, :params => { :type => "test", :value => "yes" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
get :search_all, :params => { :name => "Test" }
assert_response :service_unavailable
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
end
end

View file

@ -1,44 +0,0 @@
require "test_helper"
class SwfControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/swf/trackpoints", :method => :get },
{ :controller => "swf", :action => "trackpoints" }
)
end
##
# basic test that trackpoints at least returns some sort of flash movie
def test_trackpoints
user = create(:user)
other_user = create(:user)
create(:trace, :visibility => "trackable", :latitude => 51.51, :longitude => -0.14, :user => user) do |trace|
create(:tracepoint, :trace => trace, :trackid => 1, :latitude => (51.510 * GeoRecord::SCALE).to_i, :longitude => (-0.140 * GeoRecord::SCALE).to_i)
create(:tracepoint, :trace => trace, :trackid => 2, :latitude => (51.511 * GeoRecord::SCALE).to_i, :longitude => (-0.141 * GeoRecord::SCALE).to_i)
end
create(:trace, :visibility => "identifiable", :latitude => 51.512, :longitude => 0.142) do |trace|
create(:tracepoint, :trace => trace, :latitude => (51.512 * GeoRecord::SCALE).to_i, :longitude => (0.142 * GeoRecord::SCALE).to_i)
end
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1 }
assert_response :success
assert_equal "application/x-shockwave-flash", response.content_type
assert_match(/^FWS/, response.body)
assert_equal 80, response.body.length
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1, :token => other_user.tokens.create.token }
assert_response :success
assert_equal "application/x-shockwave-flash", response.content_type
assert_match(/^FWS/, response.body)
assert_equal 67, response.body.length
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1, :token => user.tokens.create.token }
assert_response :success
assert_equal "application/x-shockwave-flash", response.content_type
assert_match(/^FWS/, response.body)
assert_equal 74, response.body.length
end
end

View file

@ -24,35 +24,6 @@ class TracesControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/gpx/create", :method => :post },
{ :controller => "traces", :action => "api_create" }
)
assert_routing(
{ :path => "/api/0.6/gpx/1", :method => :get },
{ :controller => "traces", :action => "api_read", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/gpx/1", :method => :put },
{ :controller => "traces", :action => "api_update", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/gpx/1", :method => :delete },
{ :controller => "traces", :action => "api_delete", :id => "1" }
)
assert_recognizes(
{ :controller => "traces", :action => "api_read", :id => "1" },
{ :path => "/api/0.6/gpx/1/details", :method => :get }
)
assert_routing(
{ :path => "/api/0.6/gpx/1/data", :method => :get },
{ :controller => "traces", :action => "api_data", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/gpx/1/data.xml", :method => :get },
{ :controller => "traces", :action => "api_data", :id => "1", :format => "xml" }
)
assert_routing(
{ :path => "/traces", :method => :get },
{ :controller => "traces", :action => "index" }
@ -719,296 +690,6 @@ class TracesControllerTest < ActionController::TestCase
assert_equal false, trace.visible
end
# Check getting a specific trace through the api
def test_api_read
public_trace_file = create(:trace, :visibility => "public")
# First with no auth
get :api_read, :params => { :id => public_trace_file.id }
assert_response :unauthorized
# Now with some other user, which should work since the trace is public
basic_authorization create(:user).display_name, "test"
get :api_read, :params => { :id => public_trace_file.id }
assert_response :success
# And finally we should be able to do it with the owner of the trace
basic_authorization public_trace_file.user.display_name, "test"
get :api_read, :params => { :id => public_trace_file.id }
assert_response :success
end
# Check an anoymous trace can't be specifically fetched by another user
def test_api_read_anon
anon_trace_file = create(:trace, :visibility => "private")
# First with no auth
get :api_read, :params => { :id => anon_trace_file.id }
assert_response :unauthorized
# Now try with another user, which shouldn't work since the trace is anon
basic_authorization create(:user).display_name, "test"
get :api_read, :params => { :id => anon_trace_file.id }
assert_response :forbidden
# And finally we should be able to get the trace details with the trace owner
basic_authorization anon_trace_file.user.display_name, "test"
get :api_read, :params => { :id => anon_trace_file.id }
assert_response :success
end
# Check the api details for a trace that doesn't exist
def test_api_read_not_found
deleted_trace_file = create(:trace, :deleted)
# Try first with no auth, as it should require it
get :api_read, :params => { :id => 0 }
assert_response :unauthorized
# Login, and try again
basic_authorization deleted_trace_file.user.display_name, "test"
get :api_read, :params => { :id => 0 }
assert_response :not_found
# Now try a trace which did exist but has been deleted
basic_authorization deleted_trace_file.user.display_name, "test"
get :api_read, :params => { :id => deleted_trace_file.id }
assert_response :not_found
end
# Test downloading a trace through the api
def test_api_data
public_trace_file = create(:trace, :visibility => "public", :fixture => "a")
# First with no auth
get :api_data, :params => { :id => public_trace_file.id }
assert_response :unauthorized
# Now with some other user, which should work since the trace is public
basic_authorization create(:user).display_name, "test"
get :api_data, :params => { :id => public_trace_file.id }
check_trace_data public_trace_file, "848caa72f2f456d1bd6a0fdf228aa1b9"
# And finally we should be able to do it with the owner of the trace
basic_authorization public_trace_file.user.display_name, "test"
get :api_data, :params => { :id => public_trace_file.id }
check_trace_data public_trace_file, "848caa72f2f456d1bd6a0fdf228aa1b9"
end
# Test downloading a compressed trace through the api
def test_api_data_compressed
identifiable_trace_file = create(:trace, :visibility => "identifiable", :fixture => "d")
# Authenticate as the owner of the trace we will be using
basic_authorization identifiable_trace_file.user.display_name, "test"
# First get the data as is
get :api_data, :params => { :id => identifiable_trace_file.id }
check_trace_data identifiable_trace_file, "c6422a3d8750faae49ed70e7e8a51b93", "application/x-gzip", "gpx.gz"
# Now ask explicitly for XML format
get :api_data, :params => { :id => identifiable_trace_file.id, :format => "xml" }
check_trace_data identifiable_trace_file, "abd6675fdf3024a84fc0a1deac147c0d", "application/xml", "xml"
# Now ask explicitly for GPX format
get :api_data, :params => { :id => identifiable_trace_file.id, :format => "gpx" }
check_trace_data identifiable_trace_file, "abd6675fdf3024a84fc0a1deac147c0d"
end
# Check an anonymous trace can't be downloaded by another user through the api
def test_api_data_anon
anon_trace_file = create(:trace, :visibility => "private", :fixture => "b")
# First with no auth
get :api_data, :params => { :id => anon_trace_file.id }
assert_response :unauthorized
# Now with some other user, which shouldn't work since the trace is anon
basic_authorization create(:user).display_name, "test"
get :api_data, :params => { :id => anon_trace_file.id }
assert_response :forbidden
# And finally we should be able to do it with the owner of the trace
basic_authorization anon_trace_file.user.display_name, "test"
get :api_data, :params => { :id => anon_trace_file.id }
check_trace_data anon_trace_file, "66179ca44f1e93d8df62e2b88cbea732"
end
# Test downloading a trace that doesn't exist through the api
def test_api_data_not_found
deleted_trace_file = create(:trace, :deleted)
# Try first with no auth, as it should require it
get :api_data, :params => { :id => 0 }
assert_response :unauthorized
# Login, and try again
basic_authorization create(:user).display_name, "test"
get :api_data, :params => { :id => 0 }
assert_response :not_found
# Now try a trace which did exist but has been deleted
basic_authorization deleted_trace_file.user.display_name, "test"
get :api_data, :params => { :id => deleted_trace_file.id }
assert_response :not_found
end
# Test creating a trace through the api
def test_api_create
# Get file to use
fixture = Rails.root.join("test", "gpx", "fixtures", "a.gpx")
file = Rack::Test::UploadedFile.new(fixture, "application/gpx+xml")
user = create(:user)
# First with no auth
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :visibility => "trackable" }
assert_response :unauthorized
# Rewind the file
file.rewind
# Now authenticated
create(:user_preference, :user => user, :k => "gps.trace.visibility", :v => "identifiable")
assert_not_equal "trackable", user.preferences.where(:k => "gps.trace.visibility").first.v
basic_authorization user.display_name, "test"
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :visibility => "trackable" }
assert_response :success
trace = Trace.find(response.body.to_i)
assert_equal "a.gpx", trace.name
assert_equal "New Trace", trace.description
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
assert_equal "trackable", trace.visibility
assert_equal false, trace.inserted
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
trace.destroy
assert_equal "trackable", user.preferences.where(:k => "gps.trace.visibility").first.v
# Rewind the file
file.rewind
# Now authenticated, with the legacy public flag
assert_not_equal "public", user.preferences.where(:k => "gps.trace.visibility").first.v
basic_authorization user.display_name, "test"
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :public => 1 }
assert_response :success
trace = Trace.find(response.body.to_i)
assert_equal "a.gpx", trace.name
assert_equal "New Trace", trace.description
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
assert_equal "public", trace.visibility
assert_equal false, trace.inserted
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
trace.destroy
assert_equal "public", user.preferences.where(:k => "gps.trace.visibility").first.v
# Rewind the file
file.rewind
# Now authenticated, with the legacy private flag
second_user = create(:user)
assert_nil second_user.preferences.where(:k => "gps.trace.visibility").first
basic_authorization second_user.display_name, "test"
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :public => 0 }
assert_response :success
trace = Trace.find(response.body.to_i)
assert_equal "a.gpx", trace.name
assert_equal "New Trace", trace.description
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
assert_equal "private", trace.visibility
assert_equal false, trace.inserted
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
trace.destroy
assert_equal "private", second_user.preferences.where(:k => "gps.trace.visibility").first.v
end
# Check updating a trace through the api
def test_api_update
public_trace_file = create(:trace, :visibility => "public", :fixture => "a")
deleted_trace_file = create(:trace, :deleted)
anon_trace_file = create(:trace, :visibility => "private")
# First with no auth
put :api_update, :params => { :id => public_trace_file.id }, :body => public_trace_file.to_xml.to_s
assert_response :unauthorized
# Now with some other user, which should fail
basic_authorization create(:user).display_name, "test"
put :api_update, :params => { :id => public_trace_file.id }, :body => public_trace_file.to_xml.to_s
assert_response :forbidden
# Now with a trace which doesn't exist
basic_authorization create(:user).display_name, "test"
put :api_update, :params => { :id => 0 }, :body => public_trace_file.to_xml.to_s
assert_response :not_found
# Now with a trace which did exist but has been deleted
basic_authorization deleted_trace_file.user.display_name, "test"
put :api_update, :params => { :id => deleted_trace_file.id }, :body => deleted_trace_file.to_xml.to_s
assert_response :not_found
# Now try an update with the wrong ID
basic_authorization public_trace_file.user.display_name, "test"
put :api_update, :params => { :id => public_trace_file.id }, :body => anon_trace_file.to_xml.to_s
assert_response :bad_request,
"should not be able to update a trace with a different ID from the XML"
# And finally try an update that should work
basic_authorization public_trace_file.user.display_name, "test"
t = public_trace_file
t.description = "Changed description"
t.visibility = "private"
put :api_update, :params => { :id => t.id }, :body => t.to_xml.to_s
assert_response :success
nt = Trace.find(t.id)
assert_equal nt.description, t.description
assert_equal nt.visibility, t.visibility
end
# Test that updating a trace doesn't duplicate the tags
def test_api_update_tags
tracetag = create(:tracetag)
trace = tracetag.trace
basic_authorization trace.user.display_name, "test"
put :api_update, :params => { :id => trace.id }, :body => trace.to_xml.to_s
assert_response :success
updated = Trace.find(trace.id)
# Ensure there's only one tag in the database after updating
assert_equal Tracetag.count, 1
# The new tag object might have a different id, so check the string representation
assert_equal trace.tagstring, updated.tagstring
end
# Check deleting a trace through the api
def test_api_delete
public_trace_file = create(:trace, :visibility => "public")
# First with no auth
delete :api_delete, :params => { :id => public_trace_file.id }
assert_response :unauthorized
# Now with some other user, which should fail
basic_authorization create(:user).display_name, "test"
delete :api_delete, :params => { :id => public_trace_file.id }
assert_response :forbidden
# Now with a trace which doesn't exist
basic_authorization create(:user).display_name, "test"
delete :api_delete, :params => { :id => 0 }
assert_response :not_found
# And finally we should be able to do it with the owner of the trace
basic_authorization public_trace_file.user.display_name, "test"
delete :api_delete, :params => { :id => public_trace_file.id }
assert_response :success
# Try it a second time, which should fail
basic_authorization public_trace_file.user.display_name, "test"
delete :api_delete, :params => { :id => public_trace_file.id }
assert_response :not_found
end
private
def check_trace_feed(traces)

View file

@ -1,244 +0,0 @@
require "test_helper"
class UserPreferencesControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/user/preferences", :method => :get },
{ :controller => "user_preferences", :action => "read" }
)
assert_routing(
{ :path => "/api/0.6/user/preferences", :method => :put },
{ :controller => "user_preferences", :action => "update" }
)
assert_routing(
{ :path => "/api/0.6/user/preferences/key", :method => :get },
{ :controller => "user_preferences", :action => "read_one", :preference_key => "key" }
)
assert_routing(
{ :path => "/api/0.6/user/preferences/key", :method => :put },
{ :controller => "user_preferences", :action => "update_one", :preference_key => "key" }
)
assert_routing(
{ :path => "/api/0.6/user/preferences/key", :method => :delete },
{ :controller => "user_preferences", :action => "delete_one", :preference_key => "key" }
)
end
##
# test read action
def test_read
# first try without auth
get :read
assert_response :unauthorized, "should be authenticated"
# authenticate as a user with no preferences
basic_authorization create(:user).email, "test"
# try the read again
get :read
assert_select "osm" do
assert_select "preferences", :count => 1 do
assert_select "preference", :count => 0
end
end
# authenticate as a user with preferences
user = create(:user)
user_preference = create(:user_preference, :user => user)
user_preference2 = create(:user_preference, :user => user)
basic_authorization user.email, "test"
# try the read again
get :read
assert_response :success
assert_equal "application/xml", @response.content_type
assert_select "osm" do
assert_select "preferences", :count => 1 do
assert_select "preference", :count => 2
assert_select "preference[k=\"#{user_preference.k}\"][v=\"#{user_preference.v}\"]", :count => 1
assert_select "preference[k=\"#{user_preference2.k}\"][v=\"#{user_preference2.v}\"]", :count => 1
end
end
end
##
# test read_one action
def test_read_one
user = create(:user)
create(:user_preference, :user => user, :k => "key", :v => "value")
# try a read without auth
get :read_one, :params => { :preference_key => "key" }
assert_response :unauthorized, "should be authenticated"
# authenticate as a user with preferences
basic_authorization user.email, "test"
# try the read again
get :read_one, :params => { :preference_key => "key" }
assert_response :success
assert_equal "text/plain", @response.content_type
assert_equal "value", @response.body
# try the read again for a non-existent key
get :read_one, :params => { :preference_key => "unknown_key" }
assert_response :not_found
end
##
# test update action
def test_update
user = create(:user)
create(:user_preference, :user => user, :k => "key", :v => "value")
create(:user_preference, :user => user, :k => "some_key", :v => "some_value")
# try a put without auth
assert_no_difference "UserPreference.count" do
put :update, :body => "<osm><preferences><preference k='key' v='new_value'/><preference k='new_key' v='value'/></preferences></osm>"
end
assert_response :unauthorized, "should be authenticated"
assert_equal "value", UserPreference.find([user.id, "key"]).v
assert_equal "some_value", UserPreference.find([user.id, "some_key"]).v
assert_raises ActiveRecord::RecordNotFound do
UserPreference.find([user.id, "new_key"])
end
# authenticate as a user with preferences
basic_authorization user.email, "test"
# try the put again
assert_no_difference "UserPreference.count" do
put :update, :body => "<osm><preferences><preference k='key' v='new_value'/><preference k='new_key' v='value'/></preferences></osm>"
end
assert_response :success
assert_equal "text/plain", @response.content_type
assert_equal "", @response.body
assert_equal "new_value", UserPreference.find([user.id, "key"]).v
assert_equal "value", UserPreference.find([user.id, "new_key"]).v
assert_raises ActiveRecord::RecordNotFound do
UserPreference.find([user.id, "some_key"])
end
# try a put with duplicate keys
assert_no_difference "UserPreference.count" do
put :update, :body => "<osm><preferences><preference k='key' v='value'/><preference k='key' v='newer_value'/></preferences></osm>"
end
assert_response :bad_request
assert_equal "text/plain", @response.content_type
assert_equal "Duplicate preferences with key key", @response.body
assert_equal "new_value", UserPreference.find([user.id, "key"]).v
# try a put with invalid content
assert_no_difference "UserPreference.count" do
put :update, :body => "nonsense"
end
assert_response :bad_request
end
##
# test update_one action
def test_update_one
user = create(:user)
create(:user_preference, :user => user)
# try a put without auth
assert_no_difference "UserPreference.count" do
put :update_one, :params => { :preference_key => "new_key" }, :body => "new_value"
end
assert_response :unauthorized, "should be authenticated"
assert_raises ActiveRecord::RecordNotFound do
UserPreference.find([user.id, "new_key"])
end
# authenticate as a user with preferences
basic_authorization user.email, "test"
# try adding a new preference
assert_difference "UserPreference.count", 1 do
put :update_one, :params => { :preference_key => "new_key" }, :body => "new_value"
end
assert_response :success
assert_equal "text/plain", @response.content_type
assert_equal "", @response.body
assert_equal "new_value", UserPreference.find([user.id, "new_key"]).v
# try changing the value of a preference
assert_no_difference "UserPreference.count" do
put :update_one, :params => { :preference_key => "new_key" }, :body => "newer_value"
end
assert_response :success
assert_equal "text/plain", @response.content_type
assert_equal "", @response.body
assert_equal "newer_value", UserPreference.find([user.id, "new_key"]).v
end
##
# test delete_one action
def test_delete_one
user = create(:user)
create(:user_preference, :user => user, :k => "key", :v => "value")
# try a delete without auth
assert_no_difference "UserPreference.count" do
delete :delete_one, :params => { :preference_key => "key" }
end
assert_response :unauthorized, "should be authenticated"
assert_equal "value", UserPreference.find([user.id, "key"]).v
# authenticate as a user with preferences
basic_authorization user.email, "test"
# try the delete again
assert_difference "UserPreference.count", -1 do
get :delete_one, :params => { :preference_key => "key" }
end
assert_response :success
assert_equal "text/plain", @response.content_type
assert_equal "", @response.body
assert_raises ActiveRecord::RecordNotFound do
UserPreference.find([user.id, "key"])
end
# try the delete again for the same key
assert_no_difference "UserPreference.count" do
get :delete_one, :params => { :preference_key => "key" }
end
assert_response :not_found
assert_raises ActiveRecord::RecordNotFound do
UserPreference.find([user.id, "key"])
end
end
# Ensure that a valid access token with correct capabilities can be used to
# read preferences
def test_read_one_using_token
user = create(:user)
token = create(:access_token, :user => user, :allow_read_prefs => true)
create(:user_preference, :user => user, :k => "key", :v => "value")
# Hack together an oauth request - an alternative would be to sign the request properly
@request.env["oauth.version"] = 1
@request.env["oauth.strategies"] = [:token]
@request.env["oauth.token"] = token
get :read_one, :params => { :preference_key => "key" }
assert_response :success
end
# Ensure that a valid access token with incorrect capabilities can't be used
# to read preferences even, though the owner of that token could read them
# by other methods.
def test_read_one_using_token_fail
user = create(:user)
token = create(:access_token, :user => user, :allow_read_prefs => false)
create(:user_preference, :user => user, :k => "key", :v => "value")
@request.env["oauth.version"] = 1
@request.env["oauth.strategies"] = [:token]
@request.env["oauth.token"] = token
get :read_one, :params => { :preference_key => "key" }
assert_response :forbidden
end
end

View file

@ -8,23 +8,6 @@ class UsersControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/user/1", :method => :get },
{ :controller => "users", :action => "api_read", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/user/details", :method => :get },
{ :controller => "users", :action => "api_details" }
)
assert_routing(
{ :path => "/api/0.6/user/gpx_files", :method => :get },
{ :controller => "users", :action => "api_gpx_files" }
)
assert_routing(
{ :path => "/api/0.6/users", :method => :get },
{ :controller => "users", :action => "api_users" }
)
assert_routing(
{ :path => "/login", :method => :get },
{ :controller => "users", :action => "login" }
@ -1123,175 +1106,6 @@ class UsersControllerTest < ActionController::TestCase
end
end
def test_api_read
user = create(:user, :description => "test", :terms_agreed => Date.yesterday)
# check that a visible user is returned properly
get :api_read, :params => { :id => user.id }
assert_response :success
assert_equal "text/xml", response.content_type
# check the data that is returned
assert_select "description", :count => 1, :text => "test"
assert_select "contributor-terms", :count => 1 do
assert_select "[agreed='true']"
end
assert_select "img", :count => 0
assert_select "roles", :count => 1 do
assert_select "role", :count => 0
end
assert_select "changesets", :count => 1 do
assert_select "[count='0']"
end
assert_select "traces", :count => 1 do
assert_select "[count='0']"
end
assert_select "blocks", :count => 1 do
assert_select "received", :count => 1 do
assert_select "[count='0'][active='0']"
end
assert_select "issued", :count => 0
end
# check that we aren't revealing private information
assert_select "contributor-terms[pd]", false
assert_select "home", false
assert_select "languages", false
assert_select "messages", false
# check that a suspended user is not returned
get :api_read, :params => { :id => create(:user, :suspended).id }
assert_response :gone
# check that a deleted user is not returned
get :api_read, :params => { :id => create(:user, :deleted).id }
assert_response :gone
# check that a non-existent user is not returned
get :api_read, :params => { :id => 0 }
assert_response :not_found
end
def test_api_details
user = create(:user, :description => "test", :terms_agreed => Date.yesterday, :home_lat => 12.1, :home_lon => 12.1, :languages => ["en"])
create(:message, :read, :recipient => user)
create(:message, :sender => user)
# check that nothing is returned when not logged in
get :api_details
assert_response :unauthorized
# check that we get a response when logged in
basic_authorization user.email, "test"
get :api_details
assert_response :success
assert_equal "text/xml", response.content_type
# check the data that is returned
assert_select "description", :count => 1, :text => "test"
assert_select "contributor-terms", :count => 1 do
assert_select "[agreed='true'][pd='false']"
end
assert_select "img", :count => 0
assert_select "roles", :count => 1 do
assert_select "role", :count => 0
end
assert_select "changesets", :count => 1 do
assert_select "[count='0']", :count => 1
end
assert_select "traces", :count => 1 do
assert_select "[count='0']", :count => 1
end
assert_select "blocks", :count => 1 do
assert_select "received", :count => 1 do
assert_select "[count='0'][active='0']"
end
assert_select "issued", :count => 0
end
assert_select "home", :count => 1 do
assert_select "[lat='12.1'][lon='12.1'][zoom='3']"
end
assert_select "languages", :count => 1 do
assert_select "lang", :count => 1, :text => "en"
end
assert_select "messages", :count => 1 do
assert_select "received", :count => 1 do
assert_select "[count='1'][unread='0']"
end
assert_select "sent", :count => 1 do
assert_select "[count='1']"
end
end
end
def test_api_users
user1 = create(:user, :description => "test1", :terms_agreed => Date.yesterday)
user2 = create(:user, :description => "test2", :terms_agreed => Date.yesterday)
user3 = create(:user, :description => "test3", :terms_agreed => Date.yesterday)
get :api_users, :params => { :users => user1.id }
assert_response :success
assert_equal "text/xml", response.content_type
assert_select "user", :count => 1 do
assert_select "user[id='#{user1.id}']", :count => 1
assert_select "user[id='#{user2.id}']", :count => 0
assert_select "user[id='#{user3.id}']", :count => 0
end
get :api_users, :params => { :users => user2.id }
assert_response :success
assert_equal "text/xml", response.content_type
assert_select "user", :count => 1 do
assert_select "user[id='#{user1.id}']", :count => 0
assert_select "user[id='#{user2.id}']", :count => 1
assert_select "user[id='#{user3.id}']", :count => 0
end
get :api_users, :params => { :users => "#{user1.id},#{user3.id}" }
assert_response :success
assert_equal "text/xml", response.content_type
assert_select "user", :count => 2 do
assert_select "user[id='#{user1.id}']", :count => 1
assert_select "user[id='#{user2.id}']", :count => 0
assert_select "user[id='#{user3.id}']", :count => 1
end
get :api_users, :params => { :users => create(:user, :suspended).id }
assert_response :not_found
get :api_users, :params => { :users => create(:user, :deleted).id }
assert_response :not_found
get :api_users, :params => { :users => 0 }
assert_response :not_found
end
def test_api_gpx_files
user = create(:user)
trace1 = create(:trace, :user => user) do |trace|
create(:tracetag, :trace => trace, :tag => "London")
end
trace2 = create(:trace, :user => user) do |trace|
create(:tracetag, :trace => trace, :tag => "Birmingham")
end
# check that nothing is returned when not logged in
get :api_gpx_files
assert_response :unauthorized
# check that we get a response when logged in
basic_authorization user.email, "test"
get :api_gpx_files
assert_response :success
assert_equal "application/xml", response.content_type
# check the data that is returned
assert_select "gpx_file[id='#{trace1.id}']", 1 do
assert_select "tag", "London"
end
assert_select "gpx_file[id='#{trace2.id}']", 1 do
assert_select "tag", "Birmingham"
end
end
def test_make_friend
# Get users to work with
user = create(:user)

View file

@ -1,752 +0,0 @@
require "test_helper"
class WaysControllerTest < ActionController::TestCase
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/api/0.6/way/create", :method => :put },
{ :controller => "ways", :action => "create" }
)
assert_routing(
{ :path => "/api/0.6/way/1/full", :method => :get },
{ :controller => "ways", :action => "full", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/way/1", :method => :get },
{ :controller => "ways", :action => "show", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/way/1", :method => :put },
{ :controller => "ways", :action => "update", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/way/1", :method => :delete },
{ :controller => "ways", :action => "delete", :id => "1" }
)
assert_routing(
{ :path => "/api/0.6/ways", :method => :get },
{ :controller => "ways", :action => "index" }
)
end
# -------------------------------------
# Test showing ways.
# -------------------------------------
def test_show
# check that a visible way is returned properly
get :show, :params => { :id => create(:way).id }
assert_response :success
# check that an invisible way is not returned
get :show, :params => { :id => create(:way, :deleted).id }
assert_response :gone
# check chat a non-existent way is not returned
get :show, :params => { :id => 0 }
assert_response :not_found
end
##
# check the "full" mode
def test_full
Way.all.each do |way|
get :full, :params => { :id => way.id }
# full call should say "gone" for non-visible ways...
unless way.visible
assert_response :gone
next
end
# otherwise it should say success
assert_response :success
# Check the way is correctly returned
assert_select "osm way[id='#{way.id}'][version='#{way.version}'][visible='#{way.visible}']", 1
# check that each node in the way appears once in the output as a
# reference and as the node element.
way.nodes.each do |n|
count = (way.nodes - (way.nodes - [n])).length
assert_select "osm way nd[ref='#{n.id}']", count
assert_select "osm node[id='#{n.id}'][version='#{n.version}'][lat='#{format('%.7f', n.lat)}'][lon='#{format('%.7f', n.lon)}']", 1
end
end
end
##
# test fetching multiple ways
def test_index
way1 = create(:way)
way2 = create(:way, :deleted)
way3 = create(:way)
way4 = create(:way)
# check error when no parameter provided
get :index
assert_response :bad_request
# check error when no parameter value provided
get :index, :params => { :ways => "" }
assert_response :bad_request
# test a working call
get :index, :params => { :ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}" }
assert_response :success
assert_select "osm" do
assert_select "way", :count => 4
assert_select "way[id='#{way1.id}'][visible='true']", :count => 1
assert_select "way[id='#{way2.id}'][visible='false']", :count => 1
assert_select "way[id='#{way3.id}'][visible='true']", :count => 1
assert_select "way[id='#{way4.id}'][visible='true']", :count => 1
end
# check error when a non-existent way is included
get :index, :params => { :ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id},0" }
assert_response :not_found
end
# -------------------------------------
# Test simple way creation.
# -------------------------------------
def test_create
node1 = create(:node)
node2 = create(:node)
private_user = create(:user, :data_public => false)
private_changeset = create(:changeset, :user => private_user)
user = create(:user)
changeset = create(:changeset, :user => user)
## First check that it fails when creating a way using a non-public user
basic_authorization private_user.email, "test"
# use the first user's open changeset
changeset_id = private_changeset.id
# create a way with pre-existing nodes
xml = "<osm><way changeset='#{changeset_id}'>" \
"<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
"<tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# hope for failure
assert_response :forbidden,
"way upload did not return forbidden status"
## Now use a public user
basic_authorization user.email, "test"
# use the first user's open changeset
changeset_id = changeset.id
# create a way with pre-existing nodes
xml = "<osm><way changeset='#{changeset_id}'>" \
"<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
"<tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# hope for success
assert_response :success,
"way upload did not return success status"
# read id of created way and search for it
wayid = @response.body
checkway = Way.find(wayid)
assert_not_nil checkway,
"uploaded way not found in data base after upload"
# compare values
assert_equal checkway.nds.length, 2,
"saved way does not contain exactly one node"
assert_equal checkway.nds[0], node1.id,
"saved way does not contain the right node on pos 0"
assert_equal checkway.nds[1], node2.id,
"saved way does not contain the right node on pos 1"
assert_equal checkway.changeset_id, changeset_id,
"saved way does not belong to the correct changeset"
assert_equal user.id, checkway.changeset.user_id,
"saved way does not belong to user that created it"
assert_equal true, checkway.visible,
"saved way is not visible"
end
# -------------------------------------
# Test creating some invalid ways.
# -------------------------------------
def test_create_invalid
node = create(:node)
private_user = create(:user, :data_public => false)
private_open_changeset = create(:changeset, :user => private_user)
private_closed_changeset = create(:changeset, :closed, :user => private_user)
user = create(:user)
open_changeset = create(:changeset, :user => user)
closed_changeset = create(:changeset, :closed, :user => user)
## First test with a private user to make sure that they are not authorized
basic_authorization private_user.email, "test"
# use the first user's open changeset
# create a way with non-existing node
xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
"<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# expect failure
assert_response :forbidden,
"way upload with invalid node using a private user did not return 'forbidden'"
# create a way with no nodes
xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
"<tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# expect failure
assert_response :forbidden,
"way upload with no node using a private userdid not return 'forbidden'"
# create a way inside a closed changeset
xml = "<osm><way changeset='#{private_closed_changeset.id}'>" \
"<nd ref='#{node.id}'/></way></osm>"
put :create, :body => xml
# expect failure
assert_response :forbidden,
"way upload to closed changeset with a private user did not return 'forbidden'"
## Now test with a public user
basic_authorization user.email, "test"
# use the first user's open changeset
# create a way with non-existing node
xml = "<osm><way changeset='#{open_changeset.id}'>" \
"<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# expect failure
assert_response :precondition_failed,
"way upload with invalid node did not return 'precondition failed'"
assert_equal "Precondition failed: Way requires the nodes with id in (0), which either do not exist, or are not visible.", @response.body
# create a way with no nodes
xml = "<osm><way changeset='#{open_changeset.id}'>" \
"<tag k='test' v='yes' /></way></osm>"
put :create, :body => xml
# expect failure
assert_response :precondition_failed,
"way upload with no node did not return 'precondition failed'"
assert_equal "Precondition failed: Cannot create way: data is invalid.", @response.body
# create a way inside a closed changeset
xml = "<osm><way changeset='#{closed_changeset.id}'>" \
"<nd ref='#{node.id}'/></way></osm>"
put :create, :body => xml
# expect failure
assert_response :conflict,
"way upload to closed changeset did not return 'conflict'"
# create a way with a tag which is too long
xml = "<osm><way changeset='#{open_changeset.id}'>" \
"<nd ref='#{node.id}'/>" \
"<tag k='foo' v='#{'x' * 256}'/>" \
"</way></osm>"
put :create, :body => xml
# expect failure
assert_response :bad_request,
"way upload to with too long tag did not return 'bad_request'"
end
# -------------------------------------
# Test deleting ways.
# -------------------------------------
def test_delete
private_user = create(:user, :data_public => false)
private_open_changeset = create(:changeset, :user => private_user)
private_closed_changeset = create(:changeset, :closed, :user => private_user)
private_way = create(:way, :changeset => private_open_changeset)
private_deleted_way = create(:way, :deleted, :changeset => private_open_changeset)
private_used_way = create(:way, :changeset => private_open_changeset)
create(:relation_member, :member => private_used_way)
user = create(:user)
open_changeset = create(:changeset, :user => user)
closed_changeset = create(:changeset, :closed, :user => user)
way = create(:way, :changeset => open_changeset)
deleted_way = create(:way, :deleted, :changeset => open_changeset)
used_way = create(:way, :changeset => open_changeset)
relation_member = create(:relation_member, :member => used_way)
relation = relation_member.relation
# first try to delete way without auth
delete :delete, :params => { :id => way.id }
assert_response :unauthorized
# now set auth using the private user
basic_authorization private_user.email, "test"
# this shouldn't work as with the 0.6 api we need pay load to delete
delete :delete, :params => { :id => private_way.id }
assert_response :forbidden
# Now try without having a changeset
xml = "<osm><way id='#{private_way.id}'/></osm>"
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
assert_response :forbidden
# try to delete with an invalid (closed) changeset
xml = update_changeset(private_way.to_xml, private_closed_changeset.id)
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
assert_response :forbidden
# try to delete with an invalid (non-existent) changeset
xml = update_changeset(private_way.to_xml, 0)
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
assert_response :forbidden
# Now try with a valid changeset
xml = private_way.to_xml
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
assert_response :forbidden
# check the returned value - should be the new version number
# valid delete should return the new version number, which should
# be greater than the old version number
# assert @response.body.to_i > current_ways(:visible_way).version,
# "delete request should return a new version number for way"
# this won't work since the way is already deleted
xml = private_deleted_way.to_xml
delete :delete, :params => { :id => private_deleted_way.id }, :body => xml.to_s
assert_response :forbidden
# this shouldn't work as the way is used in a relation
xml = private_used_way.to_xml
delete :delete, :params => { :id => private_used_way.id }, :body => xml.to_s
assert_response :forbidden,
"shouldn't be able to delete a way used in a relation (#{@response.body}), when done by a private user"
# this won't work since the way never existed
delete :delete, :params => { :id => 0 }
assert_response :forbidden
### Now check with a public user
# now set auth
basic_authorization user.email, "test"
# this shouldn't work as with the 0.6 api we need pay load to delete
delete :delete, :params => { :id => way.id }
assert_response :bad_request
# Now try without having a changeset
xml = "<osm><way id='#{way.id}'/></osm>"
delete :delete, :params => { :id => way.id }, :body => xml.to_s
assert_response :bad_request
# try to delete with an invalid (closed) changeset
xml = update_changeset(way.to_xml, closed_changeset.id)
delete :delete, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict
# try to delete with an invalid (non-existent) changeset
xml = update_changeset(way.to_xml, 0)
delete :delete, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict
# Now try with a valid changeset
xml = way.to_xml
delete :delete, :params => { :id => way.id }, :body => xml.to_s
assert_response :success
# check the returned value - should be the new version number
# valid delete should return the new version number, which should
# be greater than the old version number
assert @response.body.to_i > way.version,
"delete request should return a new version number for way"
# this won't work since the way is already deleted
xml = deleted_way.to_xml
delete :delete, :params => { :id => deleted_way.id }, :body => xml.to_s
assert_response :gone
# this shouldn't work as the way is used in a relation
xml = used_way.to_xml
delete :delete, :params => { :id => used_way.id }, :body => xml.to_s
assert_response :precondition_failed,
"shouldn't be able to delete a way used in a relation (#{@response.body})"
assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
# this won't work since the way never existed
delete :delete, :params => { :id => 0 }
assert_response :not_found
end
##
# tests whether the API works and prevents incorrect use while trying
# to update ways.
def test_update
private_user = create(:user, :data_public => false)
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
user = create(:user)
way = create(:way, :changeset => create(:changeset, :user => user))
node = create(:node)
create(:way_node, :way => private_way, :node => node)
create(:way_node, :way => way, :node => node)
## First test with no user credentials
# try and update a way without authorisation
xml = way.to_xml
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :unauthorized
## Second test with the private user
# setup auth
basic_authorization private_user.email, "test"
## trying to break changesets
# try and update in someone else's changeset
xml = update_changeset(private_way.to_xml,
create(:changeset).id)
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data "update with other user's changeset should be forbidden when date isn't public"
# try and update in a closed changeset
xml = update_changeset(private_way.to_xml,
create(:changeset, :closed, :user => private_user).id)
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
# try and update in a non-existant changeset
xml = update_changeset(private_way.to_xml, 0)
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data("update with changeset=0 should be forbidden, when data isn't public")
## try and submit invalid updates
xml = xml_replace_node(private_way.to_xml, node.id, 9999)
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data "way with non-existent node should be forbidden, when data isn't public"
xml = xml_replace_node(private_way.to_xml, node.id, create(:node, :deleted).id)
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data "way with deleted node should be forbidden, when data isn't public"
## finally, produce a good request which will still not work
xml = private_way.to_xml
put :update, :params => { :id => private_way.id }, :body => xml.to_s
assert_require_public_data "should have failed with a forbidden when data isn't public"
## Finally test with the public user
# setup auth
basic_authorization user.email, "test"
## trying to break changesets
# try and update in someone else's changeset
xml = update_changeset(way.to_xml,
create(:changeset).id)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict, "update with other user's changeset should be rejected"
# try and update in a closed changeset
xml = update_changeset(way.to_xml,
create(:changeset, :closed, :user => user).id)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict, "update with closed changeset should be rejected"
# try and update in a non-existant changeset
xml = update_changeset(way.to_xml, 0)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict, "update with changeset=0 should be rejected"
## try and submit invalid updates
xml = xml_replace_node(way.to_xml, node.id, 9999)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :precondition_failed, "way with non-existent node should be rejected"
xml = xml_replace_node(way.to_xml, node.id, create(:node, :deleted).id)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :precondition_failed, "way with deleted node should be rejected"
## next, attack the versioning
current_way_version = way.version
# try and submit a version behind
xml = xml_attr_rewrite(way.to_xml,
"version", current_way_version - 1)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict, "should have failed on old version number"
# try and submit a version ahead
xml = xml_attr_rewrite(way.to_xml,
"version", current_way_version + 1)
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict, "should have failed on skipped version number"
# try and submit total crap in the version field
xml = xml_attr_rewrite(way.to_xml,
"version", "p1r4t3s!")
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :conflict,
"should not be able to put 'p1r4at3s!' in the version field"
## try an update with the wrong ID
xml = create(:way).to_xml
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to update a way with a different ID from the XML"
## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
xml = "<update/>"
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :bad_request,
"should not be able to update a way with non-OSM XML doc."
## finally, produce a good request which should work
xml = way.to_xml
put :update, :params => { :id => way.id }, :body => xml.to_s
assert_response :success, "a valid update request failed"
end
# ------------------------------------------------------------
# test tags handling
# ------------------------------------------------------------
##
# Try adding a new tag to a way
def test_add_tags
private_user = create(:user, :data_public => false)
private_way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => private_user))
user = create(:user)
way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => user))
## Try with the non-public user
# setup auth
basic_authorization private_user.email, "test"
# add an identical tag to the way
tag_xml = XML::Node.new("tag")
tag_xml["k"] = "new"
tag_xml["v"] = "yes"
# add the tag into the existing xml
way_xml = private_way.to_xml
way_xml.find("//osm/way").first << tag_xml
# try and upload it
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
assert_response :forbidden,
"adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
## Now try with the public user
# setup auth
basic_authorization user.email, "test"
# add an identical tag to the way
tag_xml = XML::Node.new("tag")
tag_xml["k"] = "new"
tag_xml["v"] = "yes"
# add the tag into the existing xml
way_xml = way.to_xml
way_xml.find("//osm/way").first << tag_xml
# try and upload it
put :update, :params => { :id => way.id }, :body => way_xml.to_s
assert_response :success,
"adding a new tag to a way should succeed"
assert_equal way.version + 1, @response.body.to_i
end
##
# Try adding a duplicate of an existing tag to a way
def test_add_duplicate_tags
private_user = create(:user, :data_public => false)
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
private_existing_tag = create(:way_tag, :way => private_way)
user = create(:user)
way = create(:way, :changeset => create(:changeset, :user => user))
existing_tag = create(:way_tag, :way => way)
## Try with the non-public user
# setup auth
basic_authorization private_user.email, "test"
# add an identical tag to the way
tag_xml = XML::Node.new("tag")
tag_xml["k"] = private_existing_tag.k
tag_xml["v"] = private_existing_tag.v
# add the tag into the existing xml
way_xml = private_way.to_xml
way_xml.find("//osm/way").first << tag_xml
# try and upload it
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
assert_response :forbidden,
"adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
## Now try with the public user
# setup auth
basic_authorization user.email, "test"
# add an identical tag to the way
tag_xml = XML::Node.new("tag")
tag_xml["k"] = existing_tag.k
tag_xml["v"] = existing_tag.v
# add the tag into the existing xml
way_xml = way.to_xml
way_xml.find("//osm/way").first << tag_xml
# try and upload it
put :update, :params => { :id => way.id }, :body => way_xml.to_s
assert_response :bad_request,
"adding a duplicate tag to a way should fail with 'bad request'"
assert_equal "Element way/#{way.id} has duplicate tags with key #{existing_tag.k}", @response.body
end
##
# Try adding a new duplicate tags to a way
def test_new_duplicate_tags
private_user = create(:user, :data_public => false)
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
user = create(:user)
way = create(:way, :changeset => create(:changeset, :user => user))
## First test with the non-public user so should be rejected
# setup auth
basic_authorization private_user.email, "test"
# create duplicate tag
tag_xml = XML::Node.new("tag")
tag_xml["k"] = "i_am_a_duplicate"
tag_xml["v"] = "foobar"
# add the tag into the existing xml
way_xml = private_way.to_xml
# add two copies of the tag
way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
# try and upload it
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
assert_response :forbidden,
"adding new duplicate tags to a way using a non-public user should fail with 'forbidden'"
## Now test with the public user
# setup auth
basic_authorization user.email, "test"
# create duplicate tag
tag_xml = XML::Node.new("tag")
tag_xml["k"] = "i_am_a_duplicate"
tag_xml["v"] = "foobar"
# add the tag into the existing xml
way_xml = way.to_xml
# add two copies of the tag
way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
# try and upload it
put :update, :params => { :id => way.id }, :body => way_xml.to_s
assert_response :bad_request,
"adding new duplicate tags to a way should fail with 'bad request'"
assert_equal "Element way/#{way.id} has duplicate tags with key i_am_a_duplicate", @response.body
end
##
# Try adding a new duplicate tags to a way.
# But be a bit subtle - use unicode decoding ambiguities to use different
# binary strings which have the same decoding.
def test_invalid_duplicate_tags
private_user = create(:user, :data_public => false)
private_changeset = create(:changeset, :user => private_user)
user = create(:user)
changeset = create(:changeset, :user => user)
## First make sure that you can't with a non-public user
# setup auth
basic_authorization private_user.email, "test"
# add the tag into the existing xml
way_str = "<osm><way changeset='#{private_changeset.id}'>"
way_str << "<tag k='addr:housenumber' v='1'/>"
way_str << "<tag k='addr:housenumber' v='2'/>"
way_str << "</way></osm>"
# try and upload it
put :create, :body => way_str
assert_response :forbidden,
"adding new duplicate tags to a way with a non-public user should fail with 'forbidden'"
## Now do it with a public user
# setup auth
basic_authorization user.email, "test"
# add the tag into the existing xml
way_str = "<osm><way changeset='#{changeset.id}'>"
way_str << "<tag k='addr:housenumber' v='1'/>"
way_str << "<tag k='addr:housenumber' v='2'/>"
way_str << "</way></osm>"
# try and upload it
put :create, :body => way_str
assert_response :bad_request,
"adding new duplicate tags to a way should fail with 'bad request'"
assert_equal "Element way/ has duplicate tags with key addr:housenumber", @response.body
end
##
# test that a call to ways_for_node returns all ways that contain the node
# and none that don't.
def test_ways_for_node
node = create(:node)
way1 = create(:way)
way2 = create(:way)
create(:way_node, :way => way1, :node => node)
create(:way_node, :way => way2, :node => node)
# create an unrelated way
create(:way_with_nodes, :nodes_count => 2)
# create a way which used to use the node
way3_v1 = create(:old_way, :version => 1)
_way3_v2 = create(:old_way, :current_way => way3_v1.current_way, :version => 2)
create(:old_way_node, :old_way => way3_v1, :node => node)
get :ways_for_node, :params => { :id => node.id }
assert_response :success
ways_xml = XML::Parser.string(@response.body).parse
assert_not_nil ways_xml, "failed to parse ways_for_node response"
# check that the set of IDs match expectations
expected_way_ids = [way1.id,
way2.id]
found_way_ids = ways_xml.find("//osm/way").collect { |w| w["id"].to_i }
assert_equal expected_way_ids.sort, found_way_ids.sort,
"expected ways for node #{node.id} did not match found"
# check the full ways to ensure we're not missing anything
expected_way_ids.each do |id|
way_xml = ways_xml.find("//osm/way[@id='#{id}']").first
assert_ways_are_equal(Way.find(id),
Way.from_xml_node(way_xml))
end
end
##
# update the changeset_id of a way element
def update_changeset(xml, changeset_id)
xml_attr_rewrite(xml, "changeset", changeset_id)
end
##
# update an attribute in the way element
def xml_attr_rewrite(xml, name, value)
xml.find("//osm/way").first[name] = value.to_s
xml
end
##
# replace a node in a way element
def xml_replace_node(xml, old_node, new_node)
xml.find("//osm/way/nd[@ref='#{old_node}']").first["ref"] = new_node.to_s
xml
end
end