Skip to content

Commit

Permalink
Add user block api call
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonKhorev committed Jun 12, 2024
1 parent b5ed80a commit e0c17b7
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 4 deletions.
3 changes: 2 additions & 1 deletion app/abilities/api_capability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def initialize(token)
if user.moderator?
can [:destroy, :restore], ChangesetComment if scope?(token, :write_api)
can :destroy, Note if scope?(token, :write_notes)
can :redact, [OldNode, OldWay, OldRelation] if user&.terms_agreed? && (scope?(token, :write_api) || scope?(token, :write_redactions))
can :redact, [OldNode, OldWay, OldRelation] if user.terms_agreed? && (scope?(token, :write_api) || scope?(token, :write_redactions))
can :create, UserBlock if scope?(token, :write_blocks)
end
end
end
Expand Down
28 changes: 28 additions & 0 deletions app/controllers/api/user_blocks_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
module Api
class UserBlocksController < ApiController
before_action :check_api_writable, :only => [:create]
before_action :authorize, :only => [:create]

authorize_resource

around_action :api_call_handle_error, :api_call_timeout
Expand All @@ -12,5 +15,30 @@ def show
rescue ActiveRecord::RecordNotFound
raise OSM::APINotFoundError
end

def create
raise OSM::APIBadUserInput, "No user was given" unless params[:user]

user = User.visible.find_by(:id => params[:user])
raise OSM::APINotFoundError unless user
raise OSM::APIBadUserInput, "No reason was given" unless params[:reason]
raise OSM::APIBadUserInput, "No period was given" unless params[:period]

period = Integer(params[:period], :exception => false)
raise OSM::APIBadUserInput, "Period should be a number of hours" unless period

max_period = UserBlock::PERIODS.max
raise OSM::APIBadUserInput, "Period must be between 0 and #{max_period}" if period.negative? || period > max_period
raise OSM::APIBadUserInput, "Needs_view must be true if provided" unless params[:needs_view].nil? || params[:needs_view] == "true"

@user_block = UserBlock.create(
:user => user,
:creator => current_user,
:reason => params[:reason],
:ends_at => Time.now.utc + period.hours,
:needs_view => params[:needs_view] == "true"
)
render :show
end
end
end
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2646,6 +2646,7 @@ en:
write_gpx: Upload GPS traces
write_notes: Modify notes
write_redactions: Redact map data
write_blocks: Create and revoke user blocks
read_email: Read user email address
skip_authorization: Auto approve application
for_roles:
Expand Down
2 changes: 1 addition & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
get "notes/getGPX" => "notes#index", :format => "gpx"
get "notes/getRSSfeed" => "notes#feed", :format => "rss"

resources :user_blocks, :only => [:show], :constraints => { :id => /\d+/ }, :controller => "user_blocks", :as => :api_user_blocks
resources :user_blocks, :only => [:show, :create], :constraints => { :id => /\d+/ }, :controller => "user_blocks", :as => :api_user_blocks
end

# Data browsing
Expand Down
4 changes: 2 additions & 2 deletions lib/oauth.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module Oauth
SCOPES = %w[read_prefs write_prefs write_diary write_api read_gpx write_gpx write_notes].freeze
PRIVILEGED_SCOPES = %w[read_email skip_authorization].freeze
MODERATOR_SCOPES = %w[write_redactions].freeze
OAUTH2_SCOPES = %w[write_redactions openid].freeze
MODERATOR_SCOPES = %w[write_redactions write_blocks].freeze
OAUTH2_SCOPES = %w[write_redactions write_blocks openid].freeze

class Scope
attr_reader :name
Expand Down
193 changes: 193 additions & 0 deletions test/controllers/api/user_blocks_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
module Api
class UserBlocksControllerTest < ActionDispatch::IntegrationTest
def test_routes
assert_routing(
{ :path => "/api/0.6/user_blocks", :method => :post },
{ :controller => "api/user_blocks", :action => "create" }
)
assert_routing(
{ :path => "/api/0.6/user_blocks/1", :method => :get },
{ :controller => "api/user_blocks", :action => "show", :id => "1" }
Expand Down Expand Up @@ -43,5 +47,194 @@ def test_show_not_found
assert_response :not_found
assert_equal "text/plain", @response.media_type
end

def test_create_no_permission
blocked_user = create(:user)
assert_empty blocked_user.blocks

post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1)
assert_response :unauthorized
assert_empty blocked_user.blocks

regular_creator_user = create(:user)
token = create(:oauth_access_token,
:resource_owner_id => regular_creator_user.id,
:scopes => %w[read_prefs])
auth_header = bearer_authorization_header(token.token)
post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1), :headers => auth_header
assert_response :forbidden
assert_empty blocked_user.blocks

token = create(:oauth_access_token,
:resource_owner_id => regular_creator_user.id,
:scopes => %w[read_prefs write_blocks])
auth_header = bearer_authorization_header(token.token)
post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1), :headers => auth_header
assert_response :forbidden
assert_empty blocked_user.blocks

moderator_creator_user = create(:moderator_user)
token = create(:oauth_access_token,
:resource_owner_id => moderator_creator_user.id,
:scopes => %w[read_prefs])
auth_header = bearer_authorization_header(token.token)
post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1), :headers => auth_header
assert_response :forbidden
assert_empty blocked_user.blocks
end

def test_create_invalid_because_no_user
blocked_user = create(:user, :deleted)
assert_empty blocked_user.blocks

creator_user = create(:moderator_user)
token = create(:oauth_access_token,
:resource_owner_id => creator_user.id,
:scopes => %w[read_prefs write_blocks])
auth_header = bearer_authorization_header(token.token)
post api_user_blocks_path(:reason => "because", :period => 1), :headers => auth_header
assert_response :bad_request
assert_equal "text/plain", @response.media_type
assert_equal "No user was given", @response.body

assert_empty blocked_user.blocks
end

def test_create_invalid_because_user_is_unknown
creator_user = create(:moderator_user)
token = create(:oauth_access_token,
:resource_owner_id => creator_user.id,
:scopes => %w[read_prefs write_blocks])
auth_header = bearer_authorization_header(token.token)
post api_user_blocks_path(:user => 0, :reason => "because", :period => 1), :headers => auth_header
assert_response :not_found
assert_equal "text/plain", @response.media_type
end

def test_create_invalid_because_user_is_deleted
blocked_user = create(:user, :deleted)
assert_empty blocked_user.blocks

creator_user = create(:moderator_user)
token = create(:oauth_access_token,
:resource_owner_id => creator_user.id,
:scopes => %w[read_prefs write_blocks])
auth_header = bearer_authorization_header(token.token)
post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1), :headers => auth_header
assert_response :not_found
assert_equal "text/plain", @response.media_type

assert_empty blocked_user.blocks
end

def test_create_invalid_because_missing_reason
create_with_params_and_assert_bad_request("No reason was given", :period => "10")
end

def test_create_invalid_because_missing_period
create_with_params_and_assert_bad_request("No period was given", :reason => "because")
end

def test_create_invalid_because_non_numeric_period
create_with_params_and_assert_bad_request("Period should be a number of hours", :reason => "because", :period => "one hour")
end

def test_create_invalid_because_negative_period
create_with_params_and_assert_bad_request("Period must be between 0 and #{UserBlock::PERIODS.max}", :reason => "go away", :period => "-1")
end

def test_create_invalid_because_excessive_period
create_with_params_and_assert_bad_request("Period must be between 0 and #{UserBlock::PERIODS.max}", :reason => "go away", :period => "10000000")
end

def test_create_invalid_because_unknown_needs_view
create_with_params_and_assert_bad_request("Needs_view must be true if provided", :reason => "because", :period => "1", :needs_view => "maybe")
end

private

def create_with_params_and_assert_bad_request(message, **params)
blocked_user = create(:user)
assert_empty blocked_user.blocks

moderator_creator_user = create(:moderator_user)
token = create(:oauth_access_token,
:resource_owner_id => moderator_creator_user.id,
:scopes => %w[read_prefs write_blocks])
auth_header = bearer_authorization_header(token.token)

post api_user_blocks_path({ :user => blocked_user.id }.merge(params)), :headers => auth_header
assert_response :bad_request
assert_equal "text/plain", @response.media_type
assert_equal message, @response.body

assert_empty blocked_user.blocks
end

public

def test_create_success
blocked_user = create(:user)
creator_user = create(:moderator_user)

assert_empty blocked_user.blocks
token = create(:oauth_access_token,
:resource_owner_id => creator_user.id,
:scopes => %w[read_prefs write_blocks])
auth_header = bearer_authorization_header(token.token)
post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => 1), :headers => auth_header
assert_response :success
assert_equal 1, blocked_user.blocks.length

block = blocked_user.blocks.take
assert_predicate block, :active?
assert_equal "because", block.reason
assert_equal creator_user, block.creator

assert_equal "application/xml", @response.media_type
assert_select "osm>user_block", 1 do
assert_select ">@id", block.id.to_s
assert_select ">@needs_view", "false"
assert_select ">user", 1
assert_select ">user>@uid", blocked_user.id.to_s
assert_select ">creator", 1
assert_select ">creator>@uid", creator_user.id.to_s
assert_select ">revoker", 0
assert_select ">reason", 1
assert_select ">reason", "because"
end
end

def test_create_success_with_needs_view
blocked_user = create(:user)
creator_user = create(:moderator_user)

assert_empty blocked_user.blocks
token = create(:oauth_access_token,
:resource_owner_id => creator_user.id,
:scopes => %w[read_prefs write_blocks])
auth_header = bearer_authorization_header(token.token)
post api_user_blocks_path(:user => blocked_user.id, :reason => "because", :period => "1", :needs_view => "true"), :headers => auth_header
assert_response :success
assert_equal 1, blocked_user.blocks.length

block = blocked_user.blocks.take
assert_predicate block, :active?
assert_equal "because", block.reason
assert_equal creator_user, block.creator

assert_equal "application/xml", @response.media_type
assert_select "osm>user_block", 1 do
assert_select ">@id", block.id.to_s
assert_select ">@needs_view", "true"
assert_select ">user", 1
assert_select ">user>@uid", blocked_user.id.to_s
assert_select ">creator", 1
assert_select ">creator>@uid", creator_user.id.to_s
assert_select ">revoker", 0
assert_select ">reason", 1
assert_select ">reason", "because"
end
end
end
end

0 comments on commit e0c17b7

Please sign in to comment.