WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 01c1635

Browse files
committed
soft-block versions on delete (#23768)
* soft-block versions on delete * refactor block_activity_log_save as submission_obj is not optional
1 parent 0002d67 commit 01c1635

File tree

17 files changed

+413
-64
lines changed

17 files changed

+413
-64
lines changed

docs/topics/api/addons.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ Delete
416416
This endpoint allows an add-on to be deleted.
417417
Because add-on deletion is an irreversible and destructive action an additional token must be retrieved beforehand, and passed as a parameter to the delete endpoint.
418418
Deleting the add-on will permanently delete all versions and files submitted for this add-on, listed or not.
419+
All versions will be soft-blocked (restricted), which will disable and prevent any further installation in Firefox. Existing users can choose to re-enable the add-on.
419420
The add-on ID (``guid``) cannot be restored and will forever be unusable for submission.
420421

421422
.. note::
@@ -736,7 +737,7 @@ Version Delete
736737

737738
.. _version-delete:
738739

739-
This endpoint allows a version to be deleted.
740+
This endpoint allows a version to be deleted. The version will be soft-blocked (restricted), which will disable and prevent any further installation in Firefox. Existing users can choose to re-enable the add-on.
740741

741742
.. note::
742743
This API requires :doc:`authentication <auth>`, and for the user to be an author of the add-on.

src/olympia/addons/models.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ def _prepare_deletion_email(self, msg, reason):
844844
return subject, email_msg
845845

846846
@transaction.atomic
847-
def delete(self, msg='', reason='', send_delete_email=True):
847+
def delete(self, *, msg='', reason='', send_delete_email=True):
848848
# To avoid a circular import
849849
from olympia.versions import tasks as version_tasks
850850

@@ -919,6 +919,16 @@ def delete(self, msg='', reason='', send_delete_email=True):
919919

920920
if send_delete_email:
921921
send_mail(subject, email_msg, recipient_list=email_to)
922+
923+
all_versions = list(
924+
self.versions(manager='unfiltered_for_relations').values_list(
925+
'id', flat=True
926+
)
927+
)
928+
if all_versions:
929+
version_tasks.soft_block_versions.delay(
930+
version_ids=all_versions, reason='Addon deleted'
931+
)
922932
else:
923933
# Real deletion path.
924934
super().delete()

src/olympia/addons/tests/test_models.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
from olympia.amo.tests.test_models import BasePreviewMixin
4242
from olympia.applications.models import AppVersion
4343
from olympia.bandwagon.models import Collection
44-
from olympia.blocklist.models import Block, BlocklistSubmission
44+
from olympia.blocklist.models import Block, BlocklistSubmission, BlockType, BlockVersion
4545
from olympia.constants.categories import CATEGORIES
4646
from olympia.constants.promoted import PROMOTED_GROUP_CHOICES
4747
from olympia.devhub.models import RssKey
@@ -598,7 +598,7 @@ def _delete(self, addon_id):
598598
addon_count = Addon.unfiltered.count()
599599
addon = Addon.objects.get(pk=addon_id)
600600
guid = addon.guid
601-
addon.delete('bye')
601+
addon.delete(msg='bye')
602602
assert addon_count == Addon.unfiltered.count() # Soft deletion.
603603
assert addon.status == amo.STATUS_DELETED
604604
assert addon.slug is None
@@ -686,7 +686,7 @@ def test_delete_incomplete_no_versions(self):
686686
# The addon status will have been changed when we deleted the version,
687687
# and the instance should be the same, so we shouldn't need to reload.
688688
assert addon.status == amo.STATUS_NULL
689-
addon.delete(None)
689+
addon.delete(msg=None)
690690
assert len(mail.outbox) == 0
691691
assert Addon.unfiltered.count() == (count - 1)
692692

@@ -696,7 +696,7 @@ def test_delete_incomplete_with_versions(self):
696696
a = Addon.objects.get(pk=3615)
697697
a.status = 0
698698
a.save()
699-
a.delete('oh looky here')
699+
a.delete(msg='oh looky here')
700700
assert len(mail.outbox) == 1
701701
assert count == Addon.unfiltered.count()
702702

@@ -1331,7 +1331,7 @@ def test_slug_isdenied(self):
13311331
def delete(self):
13321332
addon = Addon.objects.get(id=3615)
13331333
assert len(mail.outbox) == 0
1334-
addon.delete('so long and thanks for all the fish')
1334+
addon.delete(msg='so long and thanks for all the fish')
13351335
assert len(mail.outbox) == 1
13361336

13371337
def test_delete_to(self):
@@ -2620,6 +2620,9 @@ def test_update_all_due_dates(self):
26202620

26212621

26222622
class TestAddonDelete(TestCase):
2623+
def setUp(self):
2624+
user_factory(pk=settings.TASK_USER_ID)
2625+
26232626
def test_cascades(self):
26242627
addon = Addon.objects.create(type=amo.ADDON_EXTENSION)
26252628

@@ -2667,6 +2670,34 @@ def test_delete_with_deleted_versions(self):
26672670
addon.delete()
26682671
assert Addon.unfiltered.filter(pk=addon.pk).exists()
26692672

2673+
def test_delete_soft_blocks_all_versions(self):
2674+
developer = user_factory()
2675+
addon = addon_factory(users=[developer])
2676+
version = addon.current_version
2677+
deleted_version = version_factory(addon=addon, deleted=True)
2678+
hard_blocked_version = version_factory(
2679+
addon=addon, file_kw={'status': amo.STATUS_DISABLED}
2680+
)
2681+
block = Block.objects.create(guid=addon.guid, updated_by=developer)
2682+
BlockVersion.objects.create(block=block, version=hard_blocked_version)
2683+
2684+
addon.delete(send_delete_email=False)
2685+
2686+
assert Block.objects.count() == 1
2687+
assert set(block.blockversion_set.values_list('version', flat=True)) == {
2688+
version.id,
2689+
deleted_version.id,
2690+
hard_blocked_version.id,
2691+
}
2692+
assert (
2693+
block.blockversion_set.filter(block_type=BlockType.SOFT_BLOCKED).count()
2694+
== 2
2695+
)
2696+
assert (
2697+
hard_blocked_version.blockversion.reload().block_type == BlockType.BLOCKED
2698+
)
2699+
assert len(mail.outbox) == 0
2700+
26702701

26712702
class TestUpdateStatus(TestCase):
26722703
def test_no_file_ends_with_NULL(self):

src/olympia/blocklist/tests/test_admin.py

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,13 @@ def test_initial_values_from_add_from_addon_pk_view(self):
379379

380380
addon = addon_factory(guid='guid@')
381381
ver = addon.current_version
382-
ver_deleted = version_factory(addon=addon, channel=amo.CHANNEL_UNLISTED)
383-
ver_deleted.delete() # shouldn't affect it's inclusion in the choices
382+
# being deleted shouldn't affect it's inclusion in the choices
383+
ver_deleted = version_factory(
384+
addon=addon,
385+
channel=amo.CHANNEL_UNLISTED,
386+
deleted=True,
387+
file_kw={'status': amo.STATUS_DISABLED},
388+
)
384389
ver_unlisted = version_factory(addon=addon, channel=amo.CHANNEL_UNLISTED)
385390
# these next two versions shouldn't be possible choices
386391
ver_add_subm = version_factory(addon=addon)
@@ -444,8 +449,13 @@ def test_version_checkboxes(self):
444449

445450
addon = addon_factory(guid='guid@', average_daily_users=100)
446451
ver = addon.current_version
447-
ver_deleted = version_factory(addon=addon, channel=amo.CHANNEL_UNLISTED)
448-
ver_deleted.delete() # shouldn't affect it's status
452+
# being deleted shouldn't affect it's status
453+
ver_deleted = version_factory(
454+
addon=addon,
455+
channel=amo.CHANNEL_UNLISTED,
456+
deleted=True,
457+
file_kw={'status': amo.STATUS_DISABLED},
458+
)
449459
# these next three versions shouldn't be possible choices
450460
ver_add_subm = version_factory(addon=addon)
451461
add_submission = BlocklistSubmission.objects.create(
@@ -515,8 +525,12 @@ def test_version_checkboxes_hardening_action(self):
515525
self.client.force_login(user)
516526

517527
addon = addon_factory(guid='guid@', average_daily_users=100)
518-
ver_deleted = version_factory(addon=addon, channel=amo.CHANNEL_UNLISTED)
519-
ver_deleted.delete() # shouldn't affect it's status
528+
version_factory(
529+
addon=addon,
530+
channel=amo.CHANNEL_UNLISTED,
531+
deleted=True,
532+
file_kw={'status': amo.STATUS_DISABLED},
533+
)
520534
# these next three versions shouldn't be possible choices
521535
ver_add_subm = version_factory(addon=addon)
522536
add_submission = BlocklistSubmission.objects.create(
@@ -567,8 +581,12 @@ def test_version_checkboxes_softening_action(self):
567581
self.client.force_login(user)
568582

569583
addon = addon_factory(guid='guid@', average_daily_users=100)
570-
ver_deleted = version_factory(addon=addon, channel=amo.CHANNEL_UNLISTED)
571-
ver_deleted.delete() # shouldn't affect it's status
584+
version_factory(
585+
addon=addon,
586+
channel=amo.CHANNEL_UNLISTED,
587+
deleted=True,
588+
file_kw={'status': amo.STATUS_DISABLED},
589+
)
572590
# these next three versions shouldn't be possible choices
573591
ver_add_subm = version_factory(addon=addon)
574592
add_submission = BlocklistSubmission.objects.create(
@@ -621,7 +639,8 @@ def test_add_single(self):
621639
deleted_addon = addon_factory(version_kw={'version': '1.2.5'})
622640
deleted_addon_version = deleted_addon.current_version
623641
NeedsHumanReview.objects.create(version=deleted_addon_version)
624-
deleted_addon.delete()
642+
deleted_addon.update(status=amo.STATUS_DELETED)
643+
deleted_addon_version.update(deleted=True)
625644
deleted_addon.addonguid.update(guid='guid@')
626645
addon = addon_factory(
627646
guid='guid@', name='Danger Danger', version_kw={'version': '1.2a'}
@@ -2098,8 +2117,9 @@ def test_blocked_deleted_keeps_addon_status(self):
20982117
deleted_addon = addon_factory(guid='guid@', version_kw={'version': '1.2.5'})
20992118
version = deleted_addon.current_version
21002119
NeedsHumanReview.objects.create(version=version)
2101-
deleted_addon.delete()
2102-
assert deleted_addon.status == amo.STATUS_DELETED
2120+
deleted_addon.update(status=amo.STATUS_DELETED)
2121+
version.update(deleted=True)
2122+
version.file.update(status=amo.STATUS_DISABLED)
21032123
assert not DeniedGuid.objects.filter(guid=deleted_addon.guid).exists()
21042124

21052125
response = self.client.get(self.submission_url + '?guids=guid@', follow=True)
@@ -2145,8 +2165,8 @@ def test_blocking_addon_guid_already_denied(self):
21452165

21462166
deleted_addon = addon_factory(guid='guid@', version_kw={'version': '1.2.5'})
21472167
version = deleted_addon.current_version
2148-
deleted_addon.delete()
2149-
assert deleted_addon.status == amo.STATUS_DELETED
2168+
deleted_addon.update(status=amo.STATUS_DELETED)
2169+
version.update(deleted=True)
21502170
deleted_addon.deny_resubmission()
21512171
assert DeniedGuid.objects.filter(guid=deleted_addon.guid).exists()
21522172

@@ -2737,8 +2757,7 @@ def test_version_checkboxes(self):
27372757
changed_version_ids=[ver_del_subm.id],
27382758
action=BlocklistSubmission.ACTIONS.DELETE,
27392759
)
2740-
ver_deleted = version_factory(addon=other_addon)
2741-
ver_deleted.delete() # shouldn't affect it's status
2760+
ver_deleted = version_factory(addon=other_addon, deleted=True)
27422761
ver_block = version_factory(addon=other_addon)
27432762
ver_soft_block = version_factory(addon=other_addon)
27442763
block_factory(

src/olympia/blocklist/utils.py

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@
1010
log = olympia.core.logger.getLogger('z.amo.blocklist')
1111

1212

13-
def block_activity_log_save(
14-
obj,
15-
change,
16-
submission_obj=None,
17-
):
13+
def block_activity_log_save(obj, *, change, submission_obj):
1814
from .models import BlockType
1915

2016
action = amo.LOG.BLOCKLIST_BLOCK_EDITED if change else amo.LOG.BLOCKLIST_BLOCK_ADDED
@@ -23,11 +19,9 @@ def block_activity_log_save(
2319
blocked_versions = sorted(
2420
ver.version for ver in obj.addon_versions if ver.is_blocked
2521
)
26-
changed_version_ids = (
27-
[v_id for v_id in submission_obj.changed_version_ids if v_id in addon_versions]
28-
if submission_obj
29-
else sorted(ver.id for ver in obj.addon_versions if ver.is_blocked)
30-
)
22+
changed_version_ids = [
23+
v_id for v_id in submission_obj.changed_version_ids if v_id in addon_versions
24+
]
3125
changed_versions = sorted(addon_versions[ver_id] for ver_id in changed_version_ids)
3226

3327
details = {
@@ -39,15 +33,15 @@ def block_activity_log_save(
3933
'comments': f'{len(changed_versions)} versions added to block; '
4034
f'{len(blocked_versions)} total versions now blocked.',
4135
}
42-
if submission_obj:
43-
details['signoff_state'] = submission_obj.SIGNOFF_STATES.for_value(
44-
submission_obj.signoff_state
45-
).display
46-
if submission_obj.signoff_by:
47-
details['signoff_by'] = submission_obj.signoff_by.id
48-
details['block_type'] = submission_obj.block_type
49-
if submission_obj.block_type == BlockType.SOFT_BLOCKED:
50-
action_version = amo.LOG.BLOCKLIST_VERSION_SOFT_BLOCKED
36+
37+
details['signoff_state'] = submission_obj.SIGNOFF_STATES.for_value(
38+
submission_obj.signoff_state
39+
).display
40+
if submission_obj.signoff_by:
41+
details['signoff_by'] = submission_obj.signoff_by.id
42+
details['block_type'] = submission_obj.block_type
43+
if submission_obj.block_type == BlockType.SOFT_BLOCKED:
44+
action_version = amo.LOG.BLOCKLIST_VERSION_SOFT_BLOCKED
5145

5246
log_create(action, obj.addon, obj.guid, obj, details=details, user=obj.updated_by)
5347
log_create(
@@ -58,7 +52,7 @@ def block_activity_log_save(
5852
user=obj.updated_by,
5953
)
6054

61-
if submission_obj and submission_obj.signoff_by:
55+
if submission_obj.signoff_by:
6256
log_create(
6357
amo.LOG.BLOCKLIST_SIGNOFF,
6458
obj.addon,
@@ -161,6 +155,7 @@ def disable_versions_for_block(block, submission):
161155
if ver.addon == block.addon
162156
and ver.id in submission.changed_version_ids
163157
and ver.file.status != amo.STATUS_DISABLED
158+
and ver.deleted is False
164159
]
165160
if versions_to_reject:
166161
review.set_data(
@@ -173,14 +168,15 @@ def disable_versions_for_block(block, submission):
173168
)
174169
review.auto_reject_multiple_versions()
175170

176-
for version in block.addon_versions:
177-
# Clear active NeedsHumanReview on all blocked versions, we consider
178-
# that the admin looked at them before blocking (don't limit to
179-
# versions we are rejecting, which is only a subset).
180-
review.clear_specific_needs_human_review_flags(version)
171+
if human_review:
172+
for version in block.addon_versions:
173+
# Clear active NeedsHumanReview on all blocked versions, if a human has
174+
# reviewed we consider that the admin looked at them before blocking (don't
175+
# limit to versions we are rejecting, which is only a subset).
176+
review.clear_specific_needs_human_review_flags(version)
181177

182178

183-
def save_versions_to_blocks(guids, submission):
179+
def save_versions_to_blocks(guids, submission, *, overwrite_block_metadata=True):
184180
from olympia.addons.models import GuidAlreadyDeniedError
185181

186182
from .models import Block, BlockVersion
@@ -192,10 +188,13 @@ def save_versions_to_blocks(guids, submission):
192188
change = bool(block.id)
193189
if change:
194190
block.modified = modified_datetime
191+
should_override_block_metadata = overwrite_block_metadata
192+
else:
193+
should_override_block_metadata = True
195194
block.updated_by = submission.updated_by
196-
if submission.reason is not None:
195+
if submission.reason is not None and should_override_block_metadata:
197196
block.reason = submission.reason
198-
if submission.url is not None:
197+
if submission.url is not None and should_override_block_metadata:
199198
block.url = submission.url
200199
block.average_daily_users_snapshot = block.current_adu
201200
# And now update the BlockVersion instances - instances to add first
@@ -230,7 +229,7 @@ def save_versions_to_blocks(guids, submission):
230229
block_activity_log_save(
231230
block,
232231
change=change,
233-
submission_obj=submission if submission.id else None,
232+
submission_obj=submission,
234233
)
235234
disable_versions_for_block(block, submission)
236235
if submission.disable_addon:

src/olympia/devhub/templates/devhub/addons/listing/_delete_warning.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
have submitted for this add-on, listed or not.
44
{% endtrans %}
55

6+
{% trans %}
7+
All versions will be Restricted, which will disable and prevent any further installation in Firefox.
8+
Existing users can choose to re-enable the add-on.
9+
{% endtrans %}
10+
611
{% trans %}
712
The add-on ID cannot be restored and will forever be unusable for submission.
813
{% endtrans %}

src/olympia/devhub/templates/devhub/versions/list.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,10 +285,12 @@ <h3 data-tmpl="{{ _('Delete Version {version}') }}"></h3>
285285
<li id="del-files"></li>
286286
</ul>
287287
<p class="highlight warning">
288+
<strong>{{ _('Important:') }}</strong>
288289
{% trans %}
289-
<strong>Important:</strong>
290290
Once a version has been deleted, you may not upload a new
291291
version with the same version number.
292+
The version will also be Restricted, which will disable and prevent any further installation in Firefox.
293+
Existing users can choose to re-enable the add-on.
292294
{% endtrans %}
293295
</p>
294296
<p>{{ _('Are you sure you wish to delete this version?') }}</p>

src/olympia/devhub/tests/test_views_versions.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ def test_delete_message(self):
137137
assert doc('#modal-delete p').eq(0).text() == (
138138
'Deleting your add-on will permanently delete all versions and '
139139
'files you have submitted for this add-on, listed or not. '
140+
'All versions will be Restricted, which will disable and prevent any '
141+
'further installation in Firefox. '
142+
'Existing users can choose to re-enable the add-on. '
140143
'The add-on ID cannot be restored and will forever be unusable '
141144
'for submission.'
142145
)

src/olympia/lib/settings_base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,7 @@ def get_language_url_map():
833833
'olympia.scanners.tasks.call_mad_api': {'queue': 'devhub'},
834834
'olympia.scanners.tasks.run_customs': {'queue': 'devhub'},
835835
'olympia.scanners.tasks.run_yara': {'queue': 'devhub'},
836+
'olympia.versions.tasks.soft_block_versions': {'queue': 'devhub'},
836837
# Crons.
837838
'olympia.addons.tasks.update_addon_average_daily_users': {'queue': 'cron'},
838839
'olympia.addons.tasks.update_addon_hotness': {'queue': 'cron'},

0 commit comments

Comments
 (0)