Use CanCanCan for changeset comments
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.
This commit is contained in:
parent
b29c173ac7
commit
8f70fb2114
6 changed files with 89 additions and 3 deletions
|
@ -4,6 +4,7 @@ class Ability
|
||||||
include CanCan::Ability
|
include CanCan::Ability
|
||||||
|
|
||||||
def initialize(user)
|
def initialize(user)
|
||||||
|
can :index, ChangesetComment
|
||||||
can [:index, :permalink, :edit, :help, :fixthemap, :offline, :export, :about, :preview, :copyright, :key, :id], :site
|
can [:index, :permalink, :edit, :help, :fixthemap, :offline, :export, :about, :preview, :copyright, :key, :id], :site
|
||||||
can [:index, :rss, :show, :comments], DiaryEntry
|
can [:index, :rss, :show, :comments], DiaryEntry
|
||||||
can [:search, :search_latlon, :search_ca_postcode, :search_osm_nominatim,
|
can [:search, :search_latlon, :search_ca_postcode, :search_osm_nominatim,
|
||||||
|
@ -13,11 +14,13 @@ class Ability
|
||||||
|
|
||||||
if user
|
if user
|
||||||
can :welcome, :site
|
can :welcome, :site
|
||||||
|
can :create, ChangesetComment
|
||||||
can [:create, :edit, :comment, :subscribe, :unsubscribe], DiaryEntry
|
can [:create, :edit, :comment, :subscribe, :unsubscribe], DiaryEntry
|
||||||
can [:new, :create], Report
|
can [:new, :create], Report
|
||||||
can [:read, :read_one, :update, :update_one, :delete_one], UserPreference
|
can [:read, :read_one, :update, :update_one, :delete_one], UserPreference
|
||||||
|
|
||||||
if user.moderator?
|
if user.moderator?
|
||||||
|
can [:destroy, :restore], ChangesetComment
|
||||||
can [:index, :show, :resolve, :ignore, :reopen], Issue
|
can [:index, :show, :resolve, :ignore, :reopen], Issue
|
||||||
can :create, IssueComment
|
can :create, IssueComment
|
||||||
can [:new, :create, :edit, :update, :destroy], Redaction
|
can [:new, :create, :edit, :update, :destroy], Redaction
|
||||||
|
|
|
@ -4,8 +4,13 @@ class Capability
|
||||||
include CanCan::Ability
|
include CanCan::Ability
|
||||||
|
|
||||||
def initialize(token)
|
def initialize(token)
|
||||||
|
can :create, ChangesetComment if capability?(token, :allow_write_api)
|
||||||
can [:read, :read_one], UserPreference if capability?(token, :allow_read_prefs)
|
can [:read, :read_one], UserPreference if capability?(token, :allow_read_prefs)
|
||||||
can [:update, :update_one, :delete_one], UserPreference if capability?(token, :allow_write_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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -477,7 +477,15 @@ class ApplicationController < ActionController::Base
|
||||||
end
|
end
|
||||||
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
|
if current_token
|
||||||
set_locale
|
set_locale
|
||||||
report_error t("oauth.permissions.missing"), :forbidden
|
report_error t("oauth.permissions.missing"), :forbidden
|
||||||
|
@ -497,6 +505,26 @@ class ApplicationController < ActionController::Base
|
||||||
end
|
end
|
||||||
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
|
private
|
||||||
|
|
||||||
# extract authorisation credentials from headers, returns user = nil if none
|
# extract authorisation credentials from headers, returns user = nil if none
|
||||||
|
|
|
@ -3,8 +3,10 @@ class ChangesetCommentsController < ApplicationController
|
||||||
before_action :authorize_web, :only => [:index]
|
before_action :authorize_web, :only => [:index]
|
||||||
before_action :set_locale, :only => [:index]
|
before_action :set_locale, :only => [:index]
|
||||||
before_action :authorize, :only => [:create, :destroy, :restore]
|
before_action :authorize, :only => [:create, :destroy, :restore]
|
||||||
before_action :require_moderator, :only => [:destroy, :restore]
|
before_action :api_deny_access_handler, :only => [:create, :destroy, :restore]
|
||||||
before_action :require_allow_write_api, :only => [:create, :destroy, :restore]
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
before_action :require_public_data, :only => [:create]
|
before_action :require_public_data, :only => [:create]
|
||||||
before_action :check_api_writable, :only => [:create, :destroy, :restore]
|
before_action :check_api_writable, :only => [:create, :destroy, :restore]
|
||||||
before_action :check_api_readable, :except => [:create, :index]
|
before_action :check_api_readable, :except => [:create, :index]
|
||||||
|
|
|
@ -12,6 +12,48 @@ class CapabilityTest < ActiveSupport::TestCase
|
||||||
end
|
end
|
||||||
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
|
class UserCapabilityTest < CapabilityTest
|
||||||
test "user preferences" do
|
test "user preferences" do
|
||||||
# a user with no tokens
|
# a user with no tokens
|
||||||
|
|
6
test/factories/access_tokens.rb
Normal file
6
test/factories/access_tokens.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :access_token do
|
||||||
|
user
|
||||||
|
client_application
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Add a link
Reference in a new issue