diff --git a/app/controllers/api/v1/profiles_controller.rb b/app/controllers/api/v1/profiles_controller.rb new file mode 100644 index 000000000..f781f0089 --- /dev/null +++ b/app/controllers/api/v1/profiles_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class Api::V1::ProfilesController < Api::BaseController + before_action -> { doorkeeper_authorize! :write, :'write:accounts' } + before_action :require_user! + before_action :set_image + before_action :validate_image_param + + def destroy + @account = current_account + UpdateAccountService.new.call(@account, { @image => nil }, raise_error: true) + ActivityPub::UpdateDistributionWorker.perform_async(@account.id) + render json: @account, serializer: REST::CredentialAccountSerializer + end + + private + + def set_image + @image = params[:image] + end + + def validate_image_param + raise(Mastodon::InvalidParameterError, 'Image must be either "avatar" or "header"') unless valid_image? + end + + def valid_image? + %w(avatar header).include?(@image) + end +end diff --git a/config/routes/api.rb b/config/routes/api.rb index 48c4c6522..dc6aea7f7 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -95,6 +95,7 @@ namespace :api, format: false do resources :filters, only: [:index, :create, :show, :update, :destroy] resources :endorsements, only: [:index] resources :markers, only: [:index, :create] + resources :profile, only: :destroy, param: :image, controller: 'profiles' namespace :apps do get :verify_credentials, to: 'credentials#show' diff --git a/spec/requests/api/v1/profiles_spec.rb b/spec/requests/api/v1/profiles_spec.rb new file mode 100644 index 000000000..9fa5fd329 --- /dev/null +++ b/spec/requests/api/v1/profiles_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Deleting profile images' do + let(:account) do + Fabricate( + :account, + avatar: fixture_file_upload('avatar.gif', 'image/gif'), + header: fixture_file_upload('attachment.jpg', 'image/jpeg') + ) + end + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: account.user.id, scopes: scopes) } + let(:scopes) { 'write:accounts' } + let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } + + describe 'DELETE /api/v1/profile' do + before do + allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async) + end + + context 'when deleting an avatar' do + context 'with wrong scope' do + before do + delete '/api/v1/profile/avatar', headers: headers + end + + it_behaves_like 'forbidden for wrong scope', 'read' + end + + it 'returns http success' do + delete '/api/v1/profile/avatar', headers: headers + + expect(response).to have_http_status(200) + end + + it 'deletes the avatar' do + delete '/api/v1/profile/avatar', headers: headers + + account.reload + + expect(account.avatar).to_not exist + end + + it 'does not delete the header' do + delete '/api/v1/profile/avatar', headers: headers + + account.reload + + expect(account.header).to exist + end + + it 'queues up an account update distribution' do + delete '/api/v1/profile/avatar', headers: headers + + expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id) + end + end + + context 'when deleting a header' do + context 'with wrong scope' do + before do + delete '/api/v1/profile/header', headers: headers + end + + it_behaves_like 'forbidden for wrong scope', 'read' + end + + it 'returns http success' do + delete '/api/v1/profile/header', headers: headers + + expect(response).to have_http_status(200) + end + + it 'does not delete the avatar' do + delete '/api/v1/profile/header', headers: headers + + account.reload + + expect(account.avatar).to exist + end + + it 'deletes the header' do + delete '/api/v1/profile/header', headers: headers + + account.reload + + expect(account.header).to_not exist + end + + it 'queues up an account update distribution' do + delete '/api/v1/profile/header', headers: headers + + expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id) + end + end + + context 'when provided picture value is invalid' do + it 'returns http bad request' do + delete '/api/v1/profile/invalid', headers: headers + + expect(response).to have_http_status(400) + end + + it 'does not queue up an account update distribution' do + delete '/api/v1/profile/invalid', headers: headers + + expect(ActivityPub::UpdateDistributionWorker).to_not have_received(:perform_async).with(account.id) + end + end + end +end