Repository: saleor/saleor

Issue:
The fetch vat rates button should not be a GET method
### What I'm trying to achieve
Not to allow GET methods to fetch vat rates.

### Steps to reproduce the problem
1. Go to configuration -> Taxes ;
2. The fetch tax rates button, is a GET button.

### What I expected to happen
Get a POST instead of a GET, which is safer against attacks.

### Describe a proposed solution
Drop the button link on the dashboard for a submit button or a modal.



Discussion:

Resolving patch:
diff --git a/saleor/dashboard/taxes/views.py b/saleor/dashboard/taxes/views.py
--- a/saleor/dashboard/taxes/views.py
+++ b/saleor/dashboard/taxes/views.py
@@ -8,6 +8,7 @@
 from django.shortcuts import get_object_or_404, redirect
 from django.template.response import TemplateResponse
 from django.utils.translation import pgettext_lazy
+from django.views.decorators.http import require_POST
 from django_countries.fields import Country
 from django_prices_vatlayer.models import VAT
 
@@ -61,6 +62,7 @@ def configure_taxes(request):
 
 
 @staff_member_required
+@require_POST
 @permission_required('site.manage_settings')
 def fetch_tax_rates(request):
     try:


Test patch:
diff --git a/tests/dashboard/test_taxes.py b/tests/dashboard/test_taxes.py
--- a/tests/dashboard/test_taxes.py
+++ b/tests/dashboard/test_taxes.py
@@ -1,4 +1,7 @@
+from unittest import mock
+
 import pytest
+from django.core.exceptions import ImproperlyConfigured
 from django.urls import reverse
 from django_countries.fields import Country
 from django_prices_vatlayer.models import VAT
@@ -54,6 +57,55 @@ def test_configure_taxes(admin_client, site_settings):
     assert not site_settings.charge_taxes_on_shipping
 
 
+@mock.patch('saleor.dashboard.taxes.views.messages', create=True)
+@mock.patch('saleor.dashboard.taxes.views.call_command', create=True)
+def test_fetch_tax_rates(
+        mocked_call_command, mocked_messages, admin_client):
+    """Ensure a valid fetch VAT rates request is correctly handled,
+    and is leading to the proper VAT fetching command being invoked."""
+    url = reverse('dashboard:fetch-tax-rates')
+    response = admin_client.post(url)
+
+    # Ensure the request was successful
+    assert response.status_code == 302
+    assert get_redirect_location(response) == reverse('dashboard:taxes')
+    assert mocked_messages.success.call_count == 1
+
+    # Ensure the get VAT rates (mocked) command was invoked
+    mocked_call_command.assert_called_once_with('get_vat_rates')
+
+
+@mock.patch('saleor.dashboard.taxes.views.messages', create=True)
+@mock.patch('saleor.dashboard.taxes.views.logger', create=True)
+@mock.patch(
+    'saleor.dashboard.taxes.views.call_command',
+    side_effect=ImproperlyConfigured('Test'), create=True)
+def test_fetch_tax_rates_improperly_configured(
+        mocked_call_command, mocked_logger, mocked_messages, admin_client):
+    """Ensure a failing VAT rate fetching is leading to an error being
+    returned, and that error is handled."""
+    url = reverse('dashboard:fetch-tax-rates')
+    response = admin_client.post(url)
+
+    # Ensure the request was successful
+    assert response.status_code == 302
+    assert get_redirect_location(response) == reverse('dashboard:taxes')
+
+    # Ensure error was logged to the logger
+    # and the error was returned to the client
+    assert mocked_logger.exception.call_count == 1
+    assert mocked_messages.warning.call_count == 1
+
+    # Ensure the get VAT rates (mocked) command was invoked
+    mocked_call_command.assert_called_once_with('get_vat_rates')
+
+
+def test_fetch_tax_rates_invalid_method(admin_client):
+    """Ensure the GET method is not allowed for tax rates fetching"""
+    url = reverse('dashboard:fetch-tax-rates')
+    assert admin_client.get(url).status_code == 405
+
+
 def test_tax_list_filters_empty(admin_client, vatlayer):
     qs = VAT.objects.order_by('country_code')
     url = reverse('dashboard:taxes')