1 # frozen_string_literal: true
3 class ApiController < ApplicationController
4 skip_before_action :verify_authenticity_token
6 before_action :check_api_readable
8 around_action :api_call_handle_error, :api_call_timeout
13 # Set allowed request formats if no explicit format has been
14 # requested via a URL suffix. Allowed formats are taken from
15 # any HTTP Accept header with XML as the default.
16 def set_request_formats
17 unless params[:format]
18 accept_header = request.headers["HTTP_ACCEPT"]
21 # Some clients (such asJOSM) send Accept headers which cannot be
22 # parse by Rails, for example:
24 # Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
26 # where both "*" and ".2" as a quality do not adhere to the syntax
27 # described in RFC 7231, section 5.3.1, etc.
29 # As a workaround, and for back compatibility, default to XML format.
31 Mime::Type.parse(accept_header)
32 rescue Mime::Type::InvalidMimeType
36 # Allow XML and JSON formats, and treat an all formats wildcard
37 # as XML for backwards compatibility - all other formats are discarded
38 # which will result in a 406 Not Acceptable response being sent
39 formats = mimetypes.map do |mime|
40 if mime.symbol == :xml || mime == "*/*" then :xml
41 elsif mime.symbol == :json then :json
45 # Default to XML if no accept header was sent - this includes
46 # the unit tests which don't set one by default
50 request.formats = formats.compact
54 def authorize(errormessage: "Couldn't authenticate you", skip_blocks: false, skip_terms: false)
55 # make the current_user object from any auth sources we have
56 setup_user_auth(:skip_blocks => skip_blocks, :skip_terms => skip_terms)
58 # error if we could not authenticate the user
59 render :plain => errormessage, :status => :unauthorized unless current_user
63 # Use capabilities from the oauth token if it exists and is a valid access token
64 if doorkeeper_token&.accessible?
65 user = User.find(doorkeeper_token.resource_owner_id)
66 scopes = Set.new doorkeeper_token.scopes
67 if scopes.include?("write_api")
68 scopes.add("write_map")
69 scopes.add("write_changeset_comments")
70 scopes.delete("write_api")
72 ApiAbility.new(user, scopes)
74 ApiAbility.new(nil, Set.new)
78 def deny_access(_exception)
81 report_error t("oauth.permissions.missing"), :forbidden
88 status = database_status
89 status = "offline" if status == "online" && Settings.status == "gpx_offline"
94 # sets up the current_user for use by other methods. this is mostly called
95 # from the authorize method, but can be called elsewhere if authorisation
97 def setup_user_auth(skip_blocks: false, skip_terms: false)
98 logger.info " setup_user_auth"
99 # try and setup using OAuth
100 self.current_user = User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token&.accessible?
102 # have we identified the user?
104 # check if the user has been banned
106 user_block = current_user.blocks.active.take
107 unless user_block.nil?
109 if user_block.zero_hour?
110 report_error t("application.setup_user_auth.blocked_zero_hour"), :forbidden
112 report_error t("application.setup_user_auth.blocked"), :forbidden
117 # if the user hasn't seen the contributor terms then don't
118 # allow editing - they have to go to the web site and see
119 # (but can decline) the CTs to continue.
120 if !current_user.terms_seen && !skip_terms
122 report_error t("application.setup_user_auth.need_to_see_terms"), :forbidden
127 def api_call_handle_error
129 rescue ActionController::UnknownFormat
131 rescue ActiveRecord::RecordNotFound => e
133 rescue LibXML::XML::Error, ArgumentError => e
134 report_error e.message, :bad_request
135 rescue ActiveRecord::RecordInvalid => e
136 message = "#{e.record.class} #{e.record.id}: "
137 e.record.errors.each { |error| message << "#{error.attribute}: #{error.message} (#{e.record[error.attribute].inspect})" }
138 report_error message, :bad_request
139 rescue OSM::APIError => e
140 report_error e.message, e.status
141 rescue AbstractController::ActionNotFound, CanCan::AccessDenied => e
143 rescue StandardError => e
144 logger.info("API threw unexpected #{e.class} exception: #{e.message}")
145 e.backtrace.each { |l| logger.info(l) }
146 report_error "#{e.class}: #{e.message}", :internal_server_error
150 # wrap an api call in a timeout
151 def api_call_timeout(&)
152 Timeout.timeout(Settings.api_timeout, &)
153 rescue ActionView::Template::Error => e
156 if e.is_a?(Timeout::Error) ||
157 (e.is_a?(ActiveRecord::StatementInvalid) && e.message.include?("execution expired"))
158 ActiveRecord::Base.connection.raw_connection.cancel
159 raise OSM::APITimeoutError
163 rescue Timeout::Error
164 ActiveRecord::Base.connection.raw_connection.cancel
165 raise OSM::APITimeoutError
169 # check the api change rate limit
170 def check_rate_limit(new_changes = 1)
171 max_changes = ActiveRecord::Base.connection.select_value(
172 "SELECT api_rate_limit($1)", "api_rate_limit", [current_user.id]
175 raise OSM::APIRateLimitExceeded if new_changes > max_changes
178 def scope_enabled?(scope)
179 doorkeeper_token&.includes_scope?(scope)
182 helper_method :scope_enabled?