Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 5.2.16 on 2026-07-15 15:20

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('abuse', '0069_auto_20260625_1600'),
]

operations = [
migrations.AlterField(
model_name='cinderpolicy',
name='expose_in_reviewer_tools',
field=models.SmallIntegerField(choices=[(0, 'Not exposed'), (1, 'All add-ons'), (2, 'Extensions'), (3, 'Themes')], default=0),
),
migrations.AlterField(
model_name='contentdecision',
name='action',
field=models.PositiveSmallIntegerField(choices=[(1, 'User ban'), (2, 'Add-on disable'), (3, '(Obsolete) Forward add-on to reviewers'), (5, 'Rating delete'), (6, 'Collection delete'), (7, 'Approved'), (8, 'Add-on version reject'), (9, 'Add-on version delayed reject warning'), (10, 'Approved (new version approval)'), (11, 'Invalid report, so ignored'), (12, 'Content already moderated (no action)'), (13, 'Forward add-on to legal'), (14, 'Pending rejection date changed'), (15, 'No action - internal requeue'), (16, 'Add-on disable and block'), (17, 'Add-on listing content rejection'), (18, '[Follow-up] Short-delayed soft block'), (19, '[Follow-up] Mid-delayed soft block'), (20, '[Follow-up] Long-delayed soft block'), (21, '[Follow-up] Short-delayed hard block'), (22, '[Follow-up] Mid-delayed hard block'), (23, '[Follow-up] Long-delayed hard block'), (24, 'Add-on disable, due to legal takedown')]),
),
migrations.AlterField(
model_name='contentdecisionfollowupaction',
name='action',
field=models.PositiveSmallIntegerField(choices=[(1, 'User ban'), (2, 'Add-on disable'), (3, '(Obsolete) Forward add-on to reviewers'), (5, 'Rating delete'), (6, 'Collection delete'), (7, 'Approved'), (8, 'Add-on version reject'), (9, 'Add-on version delayed reject warning'), (10, 'Approved (new version approval)'), (11, 'Invalid report, so ignored'), (12, 'Content already moderated (no action)'), (13, 'Forward add-on to legal'), (14, 'Pending rejection date changed'), (15, 'No action - internal requeue'), (16, 'Add-on disable and block'), (17, 'Add-on listing content rejection'), (18, '[Follow-up] Short-delayed soft block'), (19, '[Follow-up] Mid-delayed soft block'), (20, '[Follow-up] Long-delayed soft block'), (21, '[Follow-up] Short-delayed hard block'), (22, '[Follow-up] Mid-delayed hard block'), (23, '[Follow-up] Long-delayed hard block'), (24, 'Add-on disable, due to legal takedown')]),
),
]
5 changes: 4 additions & 1 deletion src/olympia/abuse/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
DECISION_SOURCES,
ILLEGAL_CATEGORIES,
ILLEGAL_SUBCATEGORIES,
POLICY_EXPOSURE,
)
from olympia.ratings.models import Rating
from olympia.users.models import UserProfile
Expand Down Expand Up @@ -935,7 +936,9 @@ class POLICY_STATUSES(EnumChoices):
on_delete=models.SET_NULL,
related_name='children',
)
expose_in_reviewer_tools = models.BooleanField(default=False)
expose_in_reviewer_tools = models.SmallIntegerField(
default=POLICY_EXPOSURE.NONE, choices=POLICY_EXPOSURE.choices
)
enforcement_actions = models.JSONField(default=list)
status_in_cinder = models.SmallIntegerField(null=True, choices=POLICY_STATUSES)

Expand Down
12 changes: 7 additions & 5 deletions src/olympia/abuse/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from olympia.amo.celery import task
from olympia.amo.decorators import use_primary_db
from olympia.amo.utils import to_language
from olympia.constants.abuse import DECISION_ACTIONS
from olympia.constants.abuse import DECISION_ACTIONS, POLICY_EXPOSURE
from olympia.reviewers.models import NeedsHumanReview, UsageTier
from olympia.users.models import UserProfile

Expand Down Expand Up @@ -216,7 +216,9 @@ def remove_unused_deleted_policies(orphaned_policies):
qs.delete()

def mark_inactive_and_orphaned_policies(orphaned_policies):
reviewer_qs = orphaned_policies.filter(expose_in_reviewer_tools=True)
reviewer_qs = orphaned_policies.exclude(
expose_in_reviewer_tools=POLICY_EXPOSURE.NONE
)
if reviewer_qs.exists():
log.info(
'Marking orphaned Cinder Policy still in use in reviewer tools as '
Expand All @@ -225,7 +227,7 @@ def mark_inactive_and_orphaned_policies(orphaned_policies):
)
reviewer_qs.update(
status_in_cinder=CinderPolicy.POLICY_STATUSES.DELETED_WAS_REVIEWER,
expose_in_reviewer_tools=False,
expose_in_reviewer_tools=POLICY_EXPOSURE.NONE,
)

deleted_qs = (
Expand All @@ -251,14 +253,14 @@ def mark_inactive_and_orphaned_policies(orphaned_policies):

inactive_qs = CinderPolicy.objects.exclude(
status_in_cinder=CinderPolicy.POLICY_STATUSES.PUBLISHED
).filter(expose_in_reviewer_tools=True)
).exclude(expose_in_reviewer_tools=POLICY_EXPOSURE.NONE)
if inactive_qs.exists():
log.info(
'Marking Cinder Policy not published as not exposed in reviewer tools: '
'%s',
list(inactive_qs.values_list('uuid', flat=True)),
)
inactive_qs.update(expose_in_reviewer_tools=False)
inactive_qs.update(expose_in_reviewer_tools=POLICY_EXPOSURE.NONE)

url = f'{settings.CINDER_SERVER_URL}v1/policies'
headers = {
Expand Down
19 changes: 9 additions & 10 deletions src/olympia/abuse/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
grant_permission,
user_factory,
)
from olympia.constants.abuse import POLICY_EXPOSURE
from olympia.ratings.models import Rating
from olympia.reviewers.models import AutoApprovalSummary
from olympia.versions.models import VersionPreview
Expand Down Expand Up @@ -626,11 +627,9 @@ def _make_list_request(self, *, read_only=False):
'id': 'abuse_sync_cinder_policies',
'value': 'Sync from Cinder',
}
assert len(
doc(
'input[id^="id_form-"][id$="-expose_in_reviewer_tools"][type="checkbox"]'
)
) == (CinderPolicy.objects.count() if not read_only else 0)
assert len(doc('select[id^="id_form-"][id$="-expose_in_reviewer_tools"]')) == (
CinderPolicy.objects.count() if not read_only else 0
)

def test_list_no_permission(self):
self.login(permission=None)
Expand All @@ -647,29 +646,29 @@ def test_list_with_view_permission(self):

def _make_edit_request(self, expected_status_code):
policy = CinderPolicy.objects.create(
name='Bar', uuid=uuid.uuid4(), expose_in_reviewer_tools=False
name='Bar', uuid=uuid.uuid4(), expose_in_reviewer_tools=POLICY_EXPOSURE.NONE
)
detail_url = reverse('admin:abuse_cinderpolicy_change', args=(policy.id,))
response = self.client.post(
detail_url, {'expose_in_reviewer_tools': True}, follow=True
detail_url, {'expose_in_reviewer_tools': POLICY_EXPOSURE.BOTH}, follow=True
)
assert response.status_code == expected_status_code
return policy

def test_edit_policies(self):
self.login()
policy = self._make_edit_request(200)
assert policy.reload().expose_in_reviewer_tools is True
assert policy.reload().expose_in_reviewer_tools == POLICY_EXPOSURE.BOTH

def test_edit_policies_cannot_with_no_permission(self):
self.login(permission=None)
policy = self._make_edit_request(403)
assert policy.reload().expose_in_reviewer_tools is False
assert policy.reload().expose_in_reviewer_tools == POLICY_EXPOSURE.NONE

def test_edit_policies_cannot_with_view_permission(self):
self.login(permission='CinderPolicies:View')
policy = self._make_edit_request(403)
assert policy.reload().expose_in_reviewer_tools is False
assert policy.reload().expose_in_reviewer_tools == POLICY_EXPOSURE.NONE

def test_sync_policies_no_permission(self):
self.login(permission=None)
Expand Down
23 changes: 17 additions & 6 deletions src/olympia/abuse/tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
DECISION_ACTIONS,
ILLEGAL_CATEGORIES,
ILLEGAL_SUBCATEGORIES,
POLICY_EXPOSURE,
)
from olympia.files.models import File
from olympia.reviewers.models import NeedsHumanReview, ReviewActionReason, UsageTier
Expand Down Expand Up @@ -1168,7 +1169,7 @@ def test_orphaned_policies_deleted_or_marked_as_deleted(self):
uuid=uuid.uuid4().hex,
name='Existing policy',
text='Existing policy with no decision or ReviewActionReason but exposed',
expose_in_reviewer_tools=True,
expose_in_reviewer_tools=POLICY_EXPOSURE.BOTH,
)

# This policy should be kept because it's still present in Cinder
Expand All @@ -1183,13 +1184,13 @@ def test_orphaned_policies_deleted_or_marked_as_deleted(self):
name='Now archived',
text='Existing policy but now archived',
status_in_cinder=CinderPolicy.POLICY_STATUSES.PUBLISHED,
expose_in_reviewer_tools=True,
expose_in_reviewer_tools=POLICY_EXPOSURE.BOTH,
)
existing_reviewer_policy = CinderPolicy.objects.create(
uuid=uuid.uuid4().hex,
name='Reviewers',
text='Existing policy in reviewer tools',
expose_in_reviewer_tools=True,
expose_in_reviewer_tools=POLICY_EXPOSURE.BOTH,
)
# we're returning 4 policies
data = [
Expand Down Expand Up @@ -1245,7 +1246,10 @@ def test_orphaned_policies_deleted_or_marked_as_deleted(self):
updated_policy_but_now_archived.reload().status_in_cinder
== CinderPolicy.POLICY_STATUSES.ARCHIVED
)
assert updated_policy_but_now_archived.expose_in_reviewer_tools is False
assert (
updated_policy_but_now_archived.expose_in_reviewer_tools
== POLICY_EXPOSURE.NONE
)

# all the policies deleted from Cinder remain but are marked deleted
assert (
Expand All @@ -1265,9 +1269,16 @@ def test_orphaned_policies_deleted_or_marked_as_deleted(self):
== CinderPolicy.POLICY_STATUSES.DELETED_WAS_REVIEWER
)
# And they're all not exposed in reviewer tools now
assert CinderPolicy.objects.filter(expose_in_reviewer_tools=True).count() == 1
assert (
CinderPolicy.objects.filter(expose_in_reviewer_tools=True).get()
CinderPolicy.objects.filter(
expose_in_reviewer_tools=POLICY_EXPOSURE.BOTH
).count()
== 1
)
assert (
CinderPolicy.objects.filter(
expose_in_reviewer_tools=POLICY_EXPOSURE.BOTH
).get()
== existing_reviewer_policy
)

Expand Down
11 changes: 11 additions & 0 deletions src/olympia/constants/abuse.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,14 @@ class DECISION_SOURCES(StrEnum):
LEGAL = 'Legal'
TASKUS = 'TaskUs'
MANUAL = 'Manual'


class POLICY_EXPOSURE(EnumChoices):
NONE = 0, 'Not exposed'
BOTH = 1, 'All add-ons'
EXTENSION = 2, 'Extensions'
THEME = 3, 'Themes'


POLICY_EXPOSURE.add_subset('FOR_EXTENSIONS', ('BOTH', 'EXTENSION'))
POLICY_EXPOSURE.add_subset('FOR_THEMES', ('BOTH', 'THEME'))
9 changes: 7 additions & 2 deletions src/olympia/reviewers/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from olympia.access import acl
from olympia.addons.models import Addon
from olympia.amo.forms import AMOModelForm
from olympia.constants.abuse import DECISION_ACTIONS
from olympia.constants.abuse import DECISION_ACTIONS, POLICY_EXPOSURE
from olympia.constants.reviewers import (
HELD_DECISION_CHOICES,
REVIEWER_DELAYED_REJECTION_PERIOD_DAYS_DEFAULT,
Expand Down Expand Up @@ -842,8 +842,13 @@ def __init__(self, *args, **kw):
].queryset = self.helper.unresolved_cinderjob_qs

# Set the queryset for policies to show as options
policy_choices = (
POLICY_EXPOSURE.FOR_THEMES
if self.helper.addon.type == amo.ADDON_STATICTHEME
else POLICY_EXPOSURE.FOR_EXTENSIONS
)
self.fields['cinder_policies'].queryset = CinderPolicy.objects.filter(
expose_in_reviewer_tools=True
expose_in_reviewer_tools__in=policy_choices
).select_related('parent')

# Pass on the reviewer tools actions so we can set the show/hide on policies
Expand Down
6 changes: 3 additions & 3 deletions src/olympia/reviewers/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
version_review_flags_factory,
)
from olympia.amo.utils import days_ago
from olympia.constants.abuse import DECISION_ACTIONS
from olympia.constants.abuse import DECISION_ACTIONS, POLICY_EXPOSURE
from olympia.constants.promoted import PROMOTED_GROUP_CHOICES
from olympia.constants.scanners import DELAY_AUTO_APPROVAL, NARC, YARA
from olympia.files.models import FileManifest, FileValidation
Expand Down Expand Up @@ -772,7 +772,7 @@ def check_assertions():
reject_policy = CinderPolicy.objects.create(
enforcement_actions=[DECISION_ACTIONS.AMO_REJECT_VERSION_ADDON.api_value],
name='Reject Policy',
expose_in_reviewer_tools=True,
expose_in_reviewer_tools=POLICY_EXPOSURE.BOTH,
uuid=uuid.uuid4().hex,
)
ScannerRule.objects.create(
Expand Down Expand Up @@ -824,7 +824,7 @@ def check_assertions():
reject_policy = CinderPolicy.objects.create(
enforcement_actions=[DECISION_ACTIONS.AMO_DISABLE_ADDON.api_value],
name='Disable Policy',
expose_in_reviewer_tools=True,
expose_in_reviewer_tools=POLICY_EXPOSURE.BOTH,
uuid=uuid.uuid4().hex,
)
ScannerRule.objects.create(
Expand Down
Loading
Loading