diff --git a/cypress/integration/origin-save.spec.js b/cypress/integration/origin-save.spec.js --- a/cypress/integration/origin-save.spec.js +++ b/cypress/integration/origin-save.spec.js @@ -13,7 +13,8 @@ 'warning': 'The "save code now" request has been put in pending state and may be accepted for processing after manual review.', 'rejected': 'The "save code now" request has been rejected because the provided origin url is blacklisted.', 'rateLimit': 'The rate limit for "save code now" requests has been reached. Please try again later.', - 'unknownErr': 'An unexpected error happened when submitting the "save code now request' + 'unknownError': 'An unexpected error happened when submitting the "save code now request', + 'csrfError': 'CSRF Failed: Referrer checking failed - no Referrer.' }; function makeOriginSaveRequest(originType, originUrl) { @@ -34,12 +35,19 @@ } // Stub requests to save an origin -function stubSaveRequest(requestUrl, objectType, status, originUrl, taskStatus, responseStatus = 200) { +function stubSaveRequest(requestUrl, objectType, status, originUrl, taskStatus, + responseStatus = 200, errorMessage = '') { + let response; + if (responseStatus !== 200 && errorMessage) { + response = {'detail': errorMessage}; + } else { + response = genOriginSaveResponse(objectType, status, originUrl, Date().toString(), taskStatus); + } cy.route({ method: 'POST', status: responseStatus, url: requestUrl, - response: genOriginSaveResponse(objectType, status, originUrl, Date().toString(), taskStatus) + response: response }).as('saveRequest'); } @@ -91,9 +99,20 @@ }); }); + it('should show error when csrf validation failed (status: 403)', function() { + stubSaveRequest(this.originSaveUrl, origin.type, 'rejected', + origin.url, 'not created', 403, saveCodeMsg['csrfError']); + + makeOriginSaveRequest(origin.type, origin.url); + + cy.wait('@saveRequest').then(() => { + checkAlertVisible('danger', saveCodeMsg['csrfError']); + }); + }); + it('should show error when origin is rejected (status: 403)', function() { stubSaveRequest(this.originSaveUrl, origin.type, 'rejected', - origin.url, 'not created', 403); + origin.url, 'not created', 403, saveCodeMsg['rejected']); makeOriginSaveRequest(origin.type, origin.url); @@ -121,7 +140,7 @@ makeOriginSaveRequest(origin.type, origin.url); cy.wait('@saveRequest').then(() => { - checkAlertVisible('danger', saveCodeMsg['unknownErr']); + checkAlertVisible('danger', saveCodeMsg['unknownError']); }); }); diff --git a/swh/web/assets/src/bundles/save/index.js b/swh/web/assets/src/bundles/save/index.js --- a/swh/web/assets/src/bundles/save/index.js +++ b/swh/web/assets/src/bundles/save/index.js @@ -32,7 +32,9 @@ }) .catch(response => { $('.swh-processing-save-request').css('display', 'none'); - errorCallback(response.status); + response.json().then(errorData => { + errorCallback(response.status, errorData); + }); }); } @@ -138,11 +140,6 @@ 'The "save code now" request has been put in pending state and may be accepted for processing after manual review.' ); - let saveRequestRejectedAlert = htmlAlert( - 'danger', - 'The "save code now" request has been rejected because the provided origin url is blacklisted.' - ); - let saveRequestRateLimitedAlert = htmlAlert( 'danger', 'The rate limit for "save code now" requests has been reached. Please try again later.' @@ -150,7 +147,7 @@ let saveRequestUnknownErrorAlert = htmlAlert( 'danger', - 'An unexpected error happened when submitting the "save code now request' + 'An unexpected error happened when submitting the "save code now request".' ); $('#swh-save-origin-form').submit(event => { @@ -165,10 +162,11 @@ originSaveRequest(originType, originUrl, () => $('#swh-origin-save-request-status').html(saveRequestAcceptedAlert), () => $('#swh-origin-save-request-status').html(saveRequestPendingAlert), - (statusCode) => { + (statusCode, errorData) => { $('#swh-origin-save-request-status').css('color', 'red'); if (statusCode === 403) { - $('#swh-origin-save-request-status').html(saveRequestRejectedAlert); + const errorAlert = htmlAlert('danger', `Error: ${errorData['detail']}`); + $('#swh-origin-save-request-status').html(errorAlert); } else if (statusCode === 429) { $('#swh-origin-save-request-status').html(saveRequestRateLimitedAlert); } else { @@ -256,11 +254,6 @@ 'The "take new snapshot" request has been put in pending state and may be accepted for processing after manual review.' ); - let newSnapshotRequestRejectedAlert = htmlAlert( - 'danger', - 'The "take new snapshot" request has been rejected.' - ); - let newSnapshotRequestRateLimitAlert = htmlAlert( 'danger', 'The rate limit for "take new snapshot" requests has been reached. Please try again later.' @@ -282,10 +275,11 @@ originSaveRequest(originType, originUrl, () => $('#swh-take-new-snapshot-request-status').html(newSnapshotRequestAcceptedAlert), () => $('#swh-take-new-snapshot-request-status').html(newSnapshotRequestPendingAlert), - (statusCode) => { + (statusCode, errorData) => { $('#swh-take-new-snapshot-request-status').css('color', 'red'); if (statusCode === 403) { - $('#swh-take-new-snapshot-request-status').html(newSnapshotRequestRejectedAlert); + const errorAlert = htmlAlert('danger', `Error: ${errorData['detail']}`); + $('#swh-take-new-snapshot-request-status').html(errorAlert); } else if (statusCode === 429) { $('#swh-take-new-snapshot-request-status').html(newSnapshotRequestRateLimitAlert); } else { diff --git a/swh/web/common/origin_save.py b/swh/web/common/origin_save.py --- a/swh/web/common/origin_save.py +++ b/swh/web/common/origin_save.py @@ -350,8 +350,8 @@ status=save_request_status) if save_request_status == SAVE_REQUEST_REJECTED: - raise ForbiddenExc('The origin url is blacklisted and will not be ' - 'loaded into the archive.') + raise ForbiddenExc(('The "save code now" request has been rejected ' + 'because the provided origin url is blacklisted.')) return _save_request_dict(sor, task) diff --git a/swh/web/misc/origin_save.py b/swh/web/misc/origin_save.py --- a/swh/web/misc/origin_save.py +++ b/swh/web/misc/origin_save.py @@ -43,7 +43,8 @@ separators=(',', ': ')) return HttpResponse(response, content_type='application/json') except ForbiddenExc as exc: - return HttpResponseForbidden(str(exc)) + return HttpResponseForbidden(json.dumps({'detail': str(exc)}), + content_type='application/json') def _visit_save_types_list(request):