Skip to content

Commit

Permalink
send cinder emails to targets from reviewer tools (#21807)
Browse files Browse the repository at this point in the history
* send cinder emails to targets from reviewer tools

* add tests

* update test_resolve_job_in_cinder_exception

* add docstring to CinderActions
  • Loading branch information
eviljeff committed Feb 6, 2024
1 parent 1e90d4b commit 2a514e8
Show file tree
Hide file tree
Showing 9 changed files with 354 additions and 113 deletions.
29 changes: 24 additions & 5 deletions src/olympia/abuse/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,9 @@ def process_decision(
],
)
self.policies.add(*CinderPolicy.objects.filter(uuid__in=policy_ids))
self.get_action_helper(existing_decision, override=override).process()
action_helper = self.get_action_helper(existing_decision, override=override)
if action_helper.process_action():
action_helper.process_notifications()

def appeal(self, *, abuse_report, appeal_text, user, is_reporter):
appealer_entity = None
Expand Down Expand Up @@ -386,12 +388,20 @@ def appeal(self, *, abuse_report, appeal_text, user, is_reporter):
reporter_appeal_date=datetime.now(), appellant_job=appeal_job
)

def resolve_job(self, reasoning, decision, policies):
def resolve_job(self, *, decision, log_entry):
"""This is called for reviewer tools originated decisions.
See process_decision for cinder originated decisions."""
entity_helper = self.get_entity_helper(self.abuse_reports[0])
policies = list(
{
review_action.reason.cinder_policy
for review_action in log_entry.reviewactionreasonlog_set.all()
if review_action.reason.cinder_policy_id
}
)
decision_id = entity_helper.create_decision(
reasoning=reasoning, policy_uuids=[policy.uuid for policy in policies]
reasoning=log_entry.details.get('comments', ''),
policy_uuids=[policy.uuid for policy in policies],
)
existing_decision = (self.appealed_jobs.first() or self).decision_action
with atomic():
Expand All @@ -402,8 +412,17 @@ def resolve_job(self, reasoning, decision, policies):
)
self.policies.set(policies)
action_helper = self.get_action_helper(existing_decision)
action_helper.notify_reporters()
entity_helper.close_job(job_id=self.job_id)
# FIXME: pass down the log_entry id to where it's needed in a less hacky way
action_helper.log_entry_id = log_entry.id
# FIXME: pass down the versions that are being rejected in a less hacky way
action_helper.affected_versions = [
version_log.version for version_log in log_entry.versionlog_set.all()
]
action_helper.process_notifications(
policy_text=log_entry.details.get('comments')
)
if (report := self.initial_abuse_report) and report.is_handled_by_reviewers:
entity_helper.close_job(job_id=self.job_id)


class AbuseReportQuerySet(BaseQuerySet):
Expand Down
7 changes: 4 additions & 3 deletions src/olympia/abuse/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from django_statsd.clients import statsd

from olympia import amo
from olympia.activity.models import ActivityLog
from olympia.addons.models import Addon
from olympia.amo.celery import task
from olympia.amo.decorators import use_primary_db
Expand Down Expand Up @@ -108,11 +109,11 @@ def appeal_to_cinder(

@task
@use_primary_db
def resolve_job_in_cinder(*, cinder_job_id, reasoning, decision, policy_ids):
def resolve_job_in_cinder(*, cinder_job_id, decision, log_entry_id):
try:
cinder_job = CinderJob.objects.get(id=cinder_job_id)
policies = CinderPolicy.objects.filter(id__in=policy_ids)
cinder_job.resolve_job(reasoning, decision, policies)
log_entry = ActivityLog.objects.get(id=log_entry_id)
cinder_job.resolve_job(decision=decision, log_entry=log_entry)
except Exception:
statsd.incr('abuse.tasks.resolve_job_in_cinder.failure')
raise
Expand Down
45 changes: 35 additions & 10 deletions src/olympia/abuse/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
import pytest
import responses

from olympia import amo
from olympia.activity.models import ActivityLog
from olympia.amo.tests import TestCase, addon_factory, collection_factory, user_factory
from olympia.constants.abuse import APPEAL_EXPIRATION_DAYS
from olympia.ratings.models import Rating
from olympia.reviewers.models import ReviewActionReason

from ..cinder import (
CinderAddon,
Expand Down Expand Up @@ -644,7 +647,12 @@ def test_process_decision(self):
policy_a = CinderPolicy.objects.create(uuid='123-45', name='aaa', text='AAA')
policy_b = CinderPolicy.objects.create(uuid='678-90', name='bbb', text='BBB')

with mock.patch.object(CinderActionBanUser, 'process') as cinder_action_mock:
with mock.patch.object(
CinderActionBanUser, 'process_action'
) as action_mock, mock.patch.object(
CinderActionBanUser, 'process_notifications'
) as notify_mock:
action_mock.return_value = True
cinder_job.process_decision(
decision_id='12345',
decision_date=new_date,
Expand All @@ -656,7 +664,8 @@ def test_process_decision(self):
assert cinder_job.decision_date == new_date
assert cinder_job.decision_action == CinderJob.DECISION_ACTIONS.AMO_BAN_USER
assert cinder_job.decision_notes == 'teh notes'
assert cinder_action_mock.call_count == 1
assert action_mock.call_count == 1
assert notify_mock.call_count == 1
assert list(cinder_job.policies.all()) == [policy_a, policy_b]

def test_appeal_as_target(self):
Expand Down Expand Up @@ -762,10 +771,11 @@ def test_appeal_improperly_configured_author(self):

def test_resolve_job(self):
cinder_job = CinderJob.objects.create(job_id='999')
addon_developer = user_factory()
abuse_report = AbuseReport.objects.create(
guid=addon_factory().guid,
guid=addon_factory(users=[addon_developer]).guid,
reason=AbuseReport.REASONS.POLICY_VIOLATION,
location=AbuseReport.LOCATION.AMO,
location=AbuseReport.LOCATION.ADDON,
cinder_job=cinder_job,
reporter=user_factory(),
)
Expand All @@ -782,26 +792,41 @@ def test_resolve_job(self):
status=200,
)
policies = [CinderPolicy.objects.create(name='policy', uuid='12345678')]
review_action_reason = ReviewActionReason.objects.create(
cinder_policy=policies[0]
)

log_entry = ActivityLog.objects.create(
amo.LOG.REJECT_VERSION,
abuse_report.target,
abuse_report.target.current_version,
review_action_reason,
details={'comments': 'some review text'},
user=user_factory(),
)

cinder_job.resolve_job(
'some text',
CinderJob.DECISION_ACTIONS.AMO_DISABLE_ADDON,
policies,
decision=CinderJob.DECISION_ACTIONS.AMO_REJECT_VERSION_ADDON,
log_entry=log_entry,
)

request = responses.calls[0].request
request_body = json.loads(request.body)
assert request_body['policy_uuids'] == ['12345678']
assert request_body['reasoning'] == 'some text'
assert request_body['reasoning'] == 'some review text'
assert request_body['entity']['id'] == str(abuse_report.target.id)
cinder_job.reload()
assert cinder_job.decision_action == (
CinderJob.DECISION_ACTIONS.AMO_DISABLE_ADDON
CinderJob.DECISION_ACTIONS.AMO_REJECT_VERSION_ADDON
)
self.assertCloseToNow(cinder_job.decision_date)
assert list(cinder_job.policies.all()) == policies
assert len(mail.outbox) == 1
assert len(mail.outbox) == 2
assert mail.outbox[0].to == [abuse_report.reporter.email]
assert mail.outbox[1].to == [addon_developer.email]
assert str(log_entry.id) in mail.outbox[1].extra_headers['Message-ID']
assert 'some review text' in mail.outbox[1].body
assert str(abuse_report.target.current_version.version) in mail.outbox[1].body

def test_abuse_reports(self):
job = CinderJob.objects.create(job_id='fake_job_id')
Expand Down
36 changes: 27 additions & 9 deletions src/olympia/abuse/tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@

from olympia import amo
from olympia.abuse.tasks import flag_high_abuse_reports_addons_according_to_review_tier
from olympia.activity.models import ActivityLog
from olympia.amo.tests import TestCase, addon_factory, days_ago, user_factory
from olympia.constants.reviewers import EXTRA_REVIEW_TARGET_PER_DAY_CONFIG_KEY
from olympia.files.models import File
from olympia.reviewers.models import NeedsHumanReview, UsageTier
from olympia.reviewers.models import NeedsHumanReview, ReviewActionReason, UsageTier
from olympia.versions.models import Version
from olympia.zadmin.models import set_config

Expand Down Expand Up @@ -604,20 +605,29 @@ def test_resolve_job_in_cinder(statsd_incr_mock):
json={'external_id': cinder_job.job_id},
status=200,
)
policy = CinderPolicy.objects.create(name='policy', uuid='12345678')
statsd_incr_mock.reset_mock()
review_action_reason = ReviewActionReason.objects.create(
cinder_policy=CinderPolicy.objects.create(name='policy', uuid='12345678')
)
log_entry = ActivityLog.objects.create(
amo.LOG.FORCE_DISABLE,
abuse_report.target,
abuse_report.target.current_version,
review_action_reason,
details={'comments': 'some review text'},
user=user_factory(),
)

resolve_job_in_cinder.delay(
cinder_job_id=cinder_job.id,
reasoning='some text',
decision=CinderJob.DECISION_ACTIONS.AMO_DISABLE_ADDON,
policy_ids=[policy.id],
log_entry_id=log_entry.id,
)

request = responses.calls[0].request
request_body = json.loads(request.body)
assert request_body['policy_uuids'] == ['12345678']
assert request_body['reasoning'] == 'some text'
assert request_body['reasoning'] == 'some review text'
assert request_body['entity']['id'] == str(abuse_report.target.id)
cinder_job.reload()
assert cinder_job.decision_action == CinderJob.DECISION_ACTIONS.AMO_DISABLE_ADDON
Expand All @@ -632,7 +642,7 @@ def test_resolve_job_in_cinder(statsd_incr_mock):
@mock.patch('olympia.abuse.tasks.statsd.incr')
def test_resolve_job_in_cinder_exception(statsd_incr_mock):
cinder_job = CinderJob.objects.create(job_id='999')
AbuseReport.objects.create(
abuse_report = AbuseReport.objects.create(
guid=addon_factory().guid,
reason=AbuseReport.REASONS.POLICY_VIOLATION,
location=AbuseReport.LOCATION.AMO,
Expand All @@ -644,15 +654,23 @@ def test_resolve_job_in_cinder_exception(statsd_incr_mock):
json={'uuid': '123'},
status=500,
)
policy = CinderPolicy.objects.create(name='policy', uuid='12345678')
log_entry = ActivityLog.objects.create(
amo.LOG.FORCE_DISABLE,
abuse_report.target,
abuse_report.target.current_version,
ReviewActionReason.objects.create(
cinder_policy=CinderPolicy.objects.create(name='policy', uuid='12345678')
),
details={'comments': 'some review text'},
user=user_factory(),
)
statsd_incr_mock.reset_mock()

with pytest.raises(ConnectionError):
resolve_job_in_cinder.delay(
cinder_job_id=cinder_job.id,
reasoning='some text',
decision=CinderJob.DECISION_ACTIONS.AMO_DISABLE_ADDON,
policy_ids=[policy.id],
log_entry_id=log_entry.id,
)

assert statsd_incr_mock.call_count == 1
Expand Down

0 comments on commit 2a514e8

Please sign in to comment.