diff --git a/securedrop/journalist_app/api.py b/securedrop/journalist_app/api.py index e7467e91fb..b12e741482 100644 --- a/securedrop/journalist_app/api.py +++ b/securedrop/journalist_app/api.py @@ -181,6 +181,16 @@ def flag(source_uuid: str) -> Tuple[flask.Response, int]: db.session.commit() return jsonify({'message': 'Source flagged for reply'}), 200 + @api.route('/sources//conversation', methods=['DELETE']) + @token_required + def source_conversation(source_uuid: str) -> Tuple[flask.Response, int]: + if request.method == 'DELETE': + source = get_or_404(Source, source_uuid, column=Source.uuid) + utils.delete_source_files(source.filesystem_id) + return jsonify({'message': 'Source data deleted'}), 200 + else: + abort(405) + @api.route('/sources//submissions', methods=['GET']) @token_required def all_source_submissions(source_uuid: str) -> Tuple[flask.Response, int]: diff --git a/securedrop/tests/test_journalist_api.py b/securedrop/tests/test_journalist_api.py index 03c7267a1d..542e0a9375 100644 --- a/securedrop/tests/test_journalist_api.py +++ b/securedrop/tests/test_journalist_api.py @@ -181,6 +181,7 @@ def test_user_without_token_cannot_del_protected_endpoints(journalist_app, url_for('api.single_submission', source_uuid=uuid, submission_uuid=test_submissions['submissions'][0].uuid), url_for('api.remove_star', source_uuid=uuid), + url_for('api.source_conversation', source_uuid=uuid), ] with journalist_app.test_client() as app: @@ -572,6 +573,40 @@ def test_authorized_user_can_delete_single_reply(journalist_app, test_files, assert Reply.query.filter(Reply.uuid == reply_uuid).all() == [] +def test_authorized_user_can_delete_source_conversation(journalist_app, + test_files, + journalist_api_token): + with journalist_app.test_client() as app: + uuid = test_files['source'].uuid + source_id = test_files['source'].id + + # Submissions and Replies both exist + assert not Submission.query.filter(source_id == source_id).all() == [] + assert not Reply.query.filter(source_id == source_id).all() == [] + + response = app.delete(url_for('api.source_conversation', source_uuid=uuid), + headers=get_api_headers(journalist_api_token)) + + assert response.status_code == 200 + + # Submissions and Replies do not exist + assert Submission.query.filter(source_id == source_id).all() == [] + assert Reply.query.filter(source_id == source_id).all() == [] + + # Source still exists + assert not Source.query.filter(uuid == uuid).all() == [] + + +def test_source_conversation_does_not_support_get(journalist_app, test_source, + journalist_api_token): + with journalist_app.test_client() as app: + uuid = test_source['source'].uuid + response = app.get(url_for('api.source_conversation', source_uuid=uuid), + headers=get_api_headers(journalist_api_token)) + + assert response.status_code == 405 + + def test_authorized_user_can_delete_source_collection(journalist_app, test_source, journalist_api_token):