This introduces different deny_access handlers for web and api requests, since we want to avoid sending redirects as API responses. See #2064 for discussion.
include CanCan::Ability
def initialize(user)
+ can :index, ChangesetComment
can [:index, :permalink, :edit, :help, :fixthemap, :offline, :export, :about, :preview, :copyright, :key, :id], :site
can [:index, :rss, :show, :comments], DiaryEntry
can [:search, :search_latlon, :search_ca_postcode, :search_osm_nominatim,
if user
can :welcome, :site
+ can :create, ChangesetComment
can [:create, :edit, :comment, :subscribe, :unsubscribe], DiaryEntry
can [:new, :create], Report
can [:read, :read_one, :update, :update_one, :delete_one], UserPreference
if user.moderator?
+ can [:destroy, :restore], ChangesetComment
can [:index, :show, :resolve, :ignore, :reopen], Issue
can :create, IssueComment
can [:new, :create, :edit, :update, :destroy], Redaction
include CanCan::Ability
def initialize(token)
+ can :create, ChangesetComment if capability?(token, :allow_write_api)
can [:read, :read_one], UserPreference if capability?(token, :allow_read_prefs)
can [:update, :update_one, :delete_one], UserPreference if capability?(token, :allow_write_prefs)
+
+ if token&.user&.moderator?
+ can [:destroy, :restore], ChangesetComment if capability?(token, :allow_write_api)
+ end
end
private
end
end
- def deny_access(_exception)
+ def deny_access(exception)
+ if @api_deny_access_handling
+ api_deny_access(exception)
+ else
+ web_deny_access(exception)
+ end
+ end
+
+ def web_deny_access(_exception)
if current_token
set_locale
report_error t("oauth.permissions.missing"), :forbidden
end
end
+ def api_deny_access(_exception)
+ if current_token
+ set_locale
+ report_error t("oauth.permissions.missing"), :forbidden
+ elsif current_user
+ head :forbidden
+ else
+ realm = "Web Password"
+ errormessage = "Couldn't authenticate you"
+ response.headers["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
+ render :plain => errormessage, :status => :unauthorized
+ end
+ end
+
+ attr_accessor :api_access_handling
+
+ def api_deny_access_handler
+ @api_deny_access_handling = true
+ end
+
private
# extract authorisation credentials from headers, returns user = nil if none
before_action :authorize_web, :only => [:index]
before_action :set_locale, :only => [:index]
before_action :authorize, :only => [:create, :destroy, :restore]
- before_action :require_moderator, :only => [:destroy, :restore]
- before_action :require_allow_write_api, :only => [:create, :destroy, :restore]
+ before_action :api_deny_access_handler, :only => [:create, :destroy, :restore]
+
+ 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]
end
end
+class ChangesetCommentCapabilityTest < CapabilityTest
+ test "as a normal user with permissionless token" do
+ token = create(:access_token)
+ capability = Capability.new token
+
+ [:create, :destroy, :restore].each do |action|
+ assert capability.cannot? action, ChangesetComment
+ end
+ end
+
+ test "as a normal user with allow_write_api token" do
+ token = create(:access_token, :allow_write_api => true)
+ capability = Capability.new token
+
+ [:destroy, :restore].each do |action|
+ assert capability.cannot? action, ChangesetComment
+ end
+
+ [:create].each do |action|
+ assert capability.can? action, ChangesetComment
+ end
+ end
+
+ test "as a moderator with permissionless token" do
+ token = create(:access_token, :user => create(:moderator_user))
+ capability = Capability.new token
+
+ [:create, :destroy, :restore].each do |action|
+ assert capability.cannot? action, ChangesetComment
+ end
+ end
+
+ test "as a moderator with allow_write_api token" do
+ token = create(:access_token, :user => create(:moderator_user), :allow_write_api => true)
+ capability = Capability.new token
+
+ [:create, :destroy, :restore].each do |action|
+ assert capability.can? action, ChangesetComment
+ end
+ end
+end
+
class UserCapabilityTest < CapabilityTest
test "user preferences" do
# a user with no tokens
--- /dev/null
+FactoryBot.define do
+ factory :access_token do
+ user
+ client_application
+ end
+end