Fix #4990: Allow admins to delete uploads.

Allow admins to delete media asset files.

This only deletes the image file itself, not the upload or media asset record. The upload will still
be in the user's upload list, but the image will be gone. The media asset page will still exist, but
it will only show the file's metadata, not the image itself. We don't delete the metadata so we have
a record of what the file's MD5 was and who uploaded it, to prevent the file from being uploaded
again and to take action against the user if necessary.
This commit is contained in:
evazion
2022-11-29 18:09:25 -06:00
parent 695568e08b
commit 756362f89e
11 changed files with 83 additions and 13 deletions

View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class MediaAssetsController < ApplicationController class MediaAssetsController < ApplicationController
respond_to :html, :json, :xml respond_to :html, :json, :xml, :js
rate_limit :image, rate: 5.0/1.seconds, burst: 50 rate_limit :image, rate: 5.0/1.seconds, burst: 50
@@ -36,6 +36,13 @@ class MediaAssetsController < ApplicationController
end end
end end
def destroy
@media_asset = authorize MediaAsset.find(params[:id])
@media_asset.trash!(CurrentUser.user)
flash[:notice] = "File deleted"
respond_with(@media_asset)
end
def image def image
media_asset = authorize MediaAsset.find(params[:media_asset_id]) media_asset = authorize MediaAsset.find(params[:media_asset_id])
variant = media_asset.variant(params[:variant]) variant = media_asset.variant(params[:variant])

View File

@@ -119,6 +119,9 @@ html, body[data-current-user-theme="light"] {
--success-color: var(--green-5); --success-color: var(--green-5);
--error-color: var(--red-5); --error-color: var(--red-5);
--success-hover-color: var(--green-4);
--error-hover-color: var(--red-4);
--error-background-color: var(--red-1); --error-background-color: var(--red-1);
--success-background-color: var(--green-0); --success-background-color: var(--green-0);
--target-background: var(--yellow-0); --target-background: var(--yellow-0);
@@ -342,8 +345,11 @@ html, body[data-current-user-theme="light"] {
--link-color: var(--azure-4); --link-color: var(--azure-4);
--link-hover-color: var(--azure-3); --link-hover-color: var(--azure-3);
--success-color: var(--green-3); --success-color: var(--green-4);
--error-color: var(--red-3); --error-color: var(--red-4);
--success-hover-color: var(--green-3);
--error-hover-color: var(--red-3);
--default-border-color: var(--grey-7); --default-border-color: var(--grey-7);

View File

@@ -252,8 +252,13 @@ $spacer: 0.25rem; /* 4px */
.link-color { color: var(--link-color); } .link-color { color: var(--link-color); }
.text-success { color: var(--success-color); } .text-success { color: var(--success-color) !important; }
.text-error { color: var(--error-color); } .text-error { color: var(--error-color) !important; }
.text-danger { color: var(--error-color) !important; }
a.text-success:hover { color: var(--success-hover-color) !important; }
a.text-error:hover { color: var(--error-hover-color) !important; }
a.text-danger:hover { color: var(--error-hover-color) !important; }
.transition-opacity { .transition-opacity {
transition: opacity 0.15s ease; transition: opacity 0.15s ease;

View File

@@ -392,17 +392,23 @@ class MediaAsset < ApplicationRecord
end end
end end
def expunge! def expunge!(current_user, log: true)
delete_files! with_lock do
update!(status: :expunged) delete_files!
update!(status: :expunged)
ModAction.log("expunged media asset ##{id} (md5=#{md5})", :media_asset_expunge, subject: self, user: current_user) if log
end
rescue rescue
update!(status: :failed) update!(status: :failed)
raise raise
end end
def trash! def trash!(current_user, log: true)
variants.each(&:trash_file!) with_lock do
update!(status: :deleted) variants.each(&:trash_file!)
update!(status: :deleted)
ModAction.log("deleted media asset ##{id} (md5=#{md5})", :media_asset_delete, subject: self, user: current_user) if log
end
rescue rescue
update!(status: :failed) update!(status: :failed)
raise raise

View File

@@ -42,6 +42,8 @@ class ModAction < ApplicationRecord
post_vote_undelete: 233, post_vote_undelete: 233,
pool_delete: 62, pool_delete: 62,
pool_undelete: 63, pool_undelete: 63,
media_asset_delete: 72,
media_asset_expunge: 76,
artist_ban: 184, artist_ban: 184,
artist_unban: 185, artist_unban: 185,
comment_update: 81, comment_update: 81,

View File

@@ -780,7 +780,7 @@ class Post < ApplicationRecord
decrement_tag_post_counts decrement_tag_post_counts
remove_from_all_pools remove_from_all_pools
remove_from_fav_groups remove_from_fav_groups
media_asset.trash! media_asset.trash!(current_user, log: false)
destroy destroy
update_parent_on_destroy update_parent_on_destroy
end end

View File

@@ -5,6 +5,10 @@ class MediaAssetPolicy < ApplicationPolicy
true true
end end
def destroy?
user.is_admin?
end
def image? def image?
can_see_image? can_see_image?
end end

View File

@@ -0,0 +1 @@
location.reload();

View File

@@ -73,6 +73,18 @@
<%= search_icon %> <%= Danbooru.config.app_name %> <%= search_icon %> <%= Danbooru.config.app_name %>
<% end %> <% end %>
<% end %> <% end %>
<% if policy(@media_asset).destroy? && @media_asset.post.nil? && !@media_asset.deleted? && !@media_asset.expunged? %>
<% menu.item do %>
<hr class="border">
<% end %>
<% menu.item do %>
<%= link_to media_asset_path(@media_asset), class: "text-danger", remote: true, xhr: true, method: :delete, "data-confirm": "This will permanently delete this file. Are you sure?" do %>
<%= delete_icon %> Delete file
<% end %>
<% end %>
<% end %>
<% end %> <% end %>
</span> </span>
</div> </div>

View File

@@ -141,7 +141,7 @@ Rails.application.routes.draw do
get :check, to: redirect {|path_params, req| "/iqdb_queries?#{req.query_string}"} get :check, to: redirect {|path_params, req| "/iqdb_queries?#{req.query_string}"}
end end
end end
resources :media_assets, only: [:index, :show] do resources :media_assets, only: [:index, :show, :destroy] do
get "/:variant", to: "media_assets#image", as: :image get "/:variant", to: "media_assets#image", as: :image
end end
resources :media_metadata, only: [:index] resources :media_metadata, only: [:index]

View File

@@ -44,5 +44,32 @@ class MediaAssetsControllerTest < ActionDispatch::IntegrationTest
assert_response :success assert_response :success
end end
end end
context "destroy action" do
should "delete the asset's files" do
@admin = create(:admin_user)
@media_asset = MediaAsset.upload!("test/files/test.jpg")
delete_auth media_asset_path(@media_asset), @admin
assert_redirected_to @media_asset
assert_equal("deleted", @media_asset.reload.status)
@media_asset.variants.each do |variant|
assert_nil(variant.open_file)
end
assert_equal(1, ModAction.count)
assert_equal("media_asset_delete", ModAction.last.category)
assert_equal(@media_asset, ModAction.last.subject)
assert_equal(@admin, ModAction.last.creator)
end
should "fail for non-admins" do
@media_asset = create(:media_asset)
delete_auth media_asset_path(@media_asset), create(:user)
assert_response 403
end
end
end end
end end