tighten key_type validation on letters api

when in research mode or test key, dont send letters via api - instead,
just put them straight to success state

when using a team key, flat out reject the request (403)
This commit is contained in:
Leo Hemsted
2017-07-31 18:28:00 +01:00
parent 76ea0dbc76
commit 075d2a3346
5 changed files with 78 additions and 17 deletions

View File

@@ -29,10 +29,10 @@ class RateLimitError(InvalidRequest):
class BadRequestError(InvalidRequest): class BadRequestError(InvalidRequest):
status_code = 400
message = "An error occurred" message = "An error occurred"
def __init__(self, fields=[], message=None): def __init__(self, fields=[], message=None, status_code=400):
self.status_code = status_code
self.fields = fields self.fields = fields
self.message = message if message else self.message self.message = message if message else self.message

View File

@@ -4,8 +4,8 @@ from flask import request, jsonify, current_app, abort
from app import api_user, authenticated_service from app import api_user, authenticated_service
from app.config import QueueNames from app.config import QueueNames
from app.models import SMS_TYPE, EMAIL_TYPE, LETTER_TYPE, PRIORITY from app.models import SMS_TYPE, EMAIL_TYPE, LETTER_TYPE, PRIORITY, KEY_TYPE_TEST, KEY_TYPE_TEAM
from app.celery.tasks import build_dvla_file from app.celery.tasks import build_dvla_file, update_job_to_sent_to_dvla
from app.notifications.process_notifications import ( from app.notifications.process_notifications import (
persist_notification, persist_notification,
send_notification_to_queue, send_notification_to_queue,
@@ -23,6 +23,7 @@ from app.notifications.validators import (
validate_template validate_template
) )
from app.schema_validation import validate from app.schema_validation import validate
from app.v2.errors import BadRequestError
from app.v2.notifications import v2_notification_blueprint from app.v2.notifications import v2_notification_blueprint
from app.v2.notifications.notification_schemas import ( from app.v2.notifications.notification_schemas import (
post_sms_request, post_sms_request,
@@ -145,9 +146,16 @@ def process_sms_or_email_notification(*, form, notification_type, api_key, templ
def process_letter_notification(*, letter_data, api_key, template): def process_letter_notification(*, letter_data, api_key, template):
if api_key.key_type == KEY_TYPE_TEAM:
raise BadRequestError(message='Cannot send letters with a team api key', status_code=403)
job = create_letter_api_job(template) job = create_letter_api_job(template)
notification = create_letter_notification(letter_data, job, api_key) notification = create_letter_notification(letter_data, job, api_key)
build_dvla_file.apply_async([str(job.id)], queue=QueueNames.JOBS) if api_key.service.research_mode or api_key.key_type == KEY_TYPE_TEST:
update_job_to_sent_to_dvla.apply_async([str(job.id)], queue=QueueNames.RESEARCH_MODE)
else:
build_dvla_file.apply_async([str(job.id)], queue=QueueNames.JOBS)
current_app.logger.info("send job {} for api notification {} to build-dvla-file in the process-job queue".format( current_app.logger.info("send job {} for api notification {} to build-dvla-file in the process-job queue".format(
job.id, job.id,
notification.id notification.id

View File

@@ -50,7 +50,9 @@ def create_service(
service_id=None, service_id=None,
restricted=False, restricted=False,
service_permissions=[EMAIL_TYPE, SMS_TYPE], service_permissions=[EMAIL_TYPE, SMS_TYPE],
sms_sender='testing' sms_sender='testing',
research_mode=False,
active=True,
): ):
service = Service( service = Service(
name=service_name, name=service_name,
@@ -58,9 +60,13 @@ def create_service(
restricted=restricted, restricted=restricted,
email_from=service_name.lower().replace(' ', '.'), email_from=service_name.lower().replace(' ', '.'),
created_by=user or create_user(), created_by=user or create_user(),
sms_sender=sms_sender sms_sender=sms_sender,
) )
dao_create_service(service, service.created_by, service_id, service_permissions=service_permissions) dao_create_service(service, service.created_by, service_id, service_permissions=service_permissions)
service.active = active
service.research_mode = research_mode
return service return service

View File

@@ -580,17 +580,12 @@ def test_should_send_email_if_team_api_key_and_a_service_user(client, sample_ema
'to': sample_email_template.service.created_by.email_address, 'to': sample_email_template.service.created_by.email_address,
'template': sample_email_template.id 'template': sample_email_template.id
} }
api_key = ApiKey(service=sample_email_template.service, auth_header = create_authorization_header(service_id=sample_email_template.service_id, key_type=KEY_TYPE_TEAM)
name='team_key',
created_by=sample_email_template.created_by,
key_type=KEY_TYPE_TEAM)
save_model_api_key(api_key)
auth_header = create_jwt_token(secret=api_key.secret, client_id=str(api_key.service_id))
response = client.post( response = client.post(
path='/notifications/email', path='/notifications/email',
data=json.dumps(data), data=json.dumps(data),
headers=[('Content-Type', 'application/json'), ('Authorization', 'Bearer {}'.format(auth_header))]) headers=[('Content-Type', 'application/json'), auth_header])
app.celery.provider_tasks.deliver_email.apply_async.assert_called_once_with( app.celery.provider_tasks.deliver_email.apply_async.assert_called_once_with(
[fake_uuid], [fake_uuid],

View File

@@ -6,22 +6,27 @@ import pytest
from app.models import EMAIL_TYPE from app.models import EMAIL_TYPE
from app.models import Job from app.models import Job
from app.models import KEY_TYPE_NORMAL
from app.models import KEY_TYPE_TEAM
from app.models import KEY_TYPE_TEST
from app.models import LETTER_TYPE from app.models import LETTER_TYPE
from app.models import Notification from app.models import Notification
from app.models import SMS_TYPE from app.models import SMS_TYPE
from app.v2.errors import RateLimitError from app.v2.errors import RateLimitError
from app.v2.notifications.post_notifications import process_letter_notification
from tests import create_authorization_header from tests import create_authorization_header
from tests.app.db import create_service from tests.app.db import create_service
from tests.app.db import create_template from tests.app.db import create_template
def letter_request(client, data, service_id, _expected_status=201): def letter_request(client, data, service_id, key_type=KEY_TYPE_NORMAL, _expected_status=201):
resp = client.post( resp = client.post(
url_for('v2_notifications.post_notification', notification_type='letter'), url_for('v2_notifications.post_notification', notification_type='letter'),
data=json.dumps(data), data=json.dumps(data),
headers=[('Content-Type', 'application/json'), create_authorization_header(service_id=service_id)] headers=[
('Content-Type', 'application/json'),
create_authorization_header(service_id=service_id, key_type=key_type)
]
) )
json_resp = json.loads(resp.get_data(as_text=True)) json_resp = json.loads(resp.get_data(as_text=True))
assert resp.status_code == _expected_status, json_resp assert resp.status_code == _expected_status, json_resp
@@ -170,3 +175,50 @@ def test_post_letter_notification_returns_403_if_not_allowed_to_send_notificatio
assert error_json['errors'] == [ assert error_json['errors'] == [
{'error': 'BadRequestError', 'message': 'Cannot send letters'} {'error': 'BadRequestError', 'message': 'Cannot send letters'}
] ]
@pytest.mark.parametrize('research_mode, key_type', [
(True, KEY_TYPE_NORMAL),
(False, KEY_TYPE_TEST)
])
def test_post_letter_notification_doesnt_queue_task(
client,
notify_db_session,
mocker,
research_mode,
key_type
):
real_task = mocker.patch('app.celery.tasks.build_dvla_file.apply_async')
fake_task = mocker.patch('app.celery.tasks.update_job_to_sent_to_dvla.apply_async')
service = create_service(research_mode=research_mode, service_permissions=[LETTER_TYPE])
template = create_template(service, template_type=LETTER_TYPE)
data = {
'template_id': str(template.id),
'personalisation': {'address_line_1': 'Foo', 'postcode': 'Bar'}
}
letter_request(client, data, service_id=service.id, key_type=key_type)
job = Job.query.one()
assert not real_task.called
fake_task.assert_called_once_with([str(job.id)], queue='research-mode-tasks')
def test_post_letter_notification_doesnt_accept_team_key(client, sample_letter_template):
data = {
'template_id': str(sample_letter_template.id),
'personalisation': {'address_line_1': 'Foo', 'postcode': 'Bar'}
}
error_json = letter_request(
client,
data,
sample_letter_template.service_id,
key_type=KEY_TYPE_TEAM,
_expected_status=403
)
assert error_json['status_code'] == 403
assert error_json['errors'] == [{'error': 'BadRequestError', 'message': 'Cannot send letters with a team api key'}]