Skip to content

Commit

Permalink
Merge pull request #1848 from uktrade/uat
Browse files Browse the repository at this point in the history
PROD release
  • Loading branch information
hnryjmes authored Feb 28, 2024
2 parents a86154a + 4d6f87f commit e127df4
Show file tree
Hide file tree
Showing 13 changed files with 305 additions and 180 deletions.
320 changes: 166 additions & 154 deletions Pipfile.lock

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,7 @@ EXPORTER_USERS='[{"email"=>"[email protected]"}]' ./manage.py seedexporterusers
```

## Running background tasks

We currently have two mechanisms for background tasks in LITE;
- django-background-tasks: `pipenv run ./manage.py process_tasks` will run all background tasks
We currently use celery for async tasks and scheduling in LITE;
- celery: a celery container is running by default when using docker-compose. If a working copy
"on the metal" without docker, run celery with `watchmedo auto-restart -d . -R -p '*.py' -- celery -A api.conf worker -l info`
- celery-scheduler: a celery container is running by default when using docker-compose. This is to monitor any scheduled tasks If a working copy
Expand Down
9 changes: 9 additions & 0 deletions api/conf/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
GOV_NOTIFY_ENABLED=(bool, False),
DOCUMENT_SIGNING_ENABLED=(bool, False),
GIT_COMMIT=(str, ""),
MOCK_VIRUS_SCAN_ACTIVATE_ENDPOINTS=(bool, False),
)

# Quick-start development settings - unsuitable for production
Expand Down Expand Up @@ -121,6 +122,14 @@
"api.document_data",
]

MOCK_VIRUS_SCAN_ACTIVATE_ENDPOINTS = env("MOCK_VIRUS_SCAN_ACTIVATE_ENDPOINTS")

if "MOCK_VIRUS_SCAN_ACTIVATE_ENDPOINTS":
INSTALLED_APPS += [
"mock_virus_scan",
]


MIDDLEWARE = [
"allow_cidr.middleware.AllowCIDRMiddleware",
"django.middleware.security.SecurityMiddleware",
Expand Down
3 changes: 3 additions & 0 deletions api/conf/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@

if settings.ENABLE_DJANGO_SILK:
urlpatterns += [path("silk/", include("silk.urls", namespace="silk"))]

if settings.MOCK_VIRUS_SCAN_ACTIVATE_ENDPOINTS:
urlpatterns += [path("mock_virus_scan/", include("mock_virus_scan.urls"))]
4 changes: 2 additions & 2 deletions api/letter_templates/templates/letter_templates/nlr.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ <h2>Where to find further information</h2>

The following items do not require a licence:
{% endfilter %}
<table class="govuk-table">
<table id="nlr-products-table" class="govuk-table">
<thead class="govuk-table__head">
<tr class="govuk-table__row">
<th class="govuk-table__header">#</th>
Expand All @@ -82,7 +82,7 @@ <h2>Where to find further information</h2>
</thead>
<tbody>
{% for good in goods.no_licence_required %}
<tr class="govuk-table__row">
<tr id="nlr-product-row-{{ forloop.counter }}" class="govuk-table__row">
<td class="govuk-table__cell">{{ forloop.counter }}</td>
<td class="govuk-table__cell">{{ good.good.name }}</td>
<td class="govuk-table__cell">{{ good.good.description }}</td>
Expand Down
34 changes: 17 additions & 17 deletions api/letter_templates/templates/letter_templates/siel.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,21 @@ <h1>Standard Individual Export Licence</h1>
<strong>Registered No. {{ organisation.registration_number}}</strong>
</span>
</td>
<td class="border-black" colspan="3">
<td id="licence-reference-column" class="border-black" colspan="3">
<span class="cell__heading">2. Licence number</span>
<span class="cell__uppercase">{{ licence.reference_code }}</span>
</td>
<td class="border-black" colspan="3">
<td id="licence-start-date" class="border-black" colspan="3">
<span class="cell__heading">3a. Commencement date</span>
<span class="cell__uppercase">{{ licence.start_date }}</span>
</td>
</tr>
<tr>
<td class="border-black" colspan="3">
<td id="application-reference-code" class="border-black" colspan="3">
<span class="cell__heading">2a. Application number</span>
<span class="cell__uppercase">{{ case_reference }}</span>
</td>
<td class="border-black" colspan="3">
<td id="licence-end-date" class="border-black" colspan="3">
<span class="cell__heading">3b. Expiry date</span>
<span class="cell__uppercase">{{ licence.end_date }}</span>
</td>
Expand Down Expand Up @@ -80,7 +80,7 @@ <h1>Standard Individual Export Licence</h1>
</td>
</tr>
<tr>
<td class="border-black" colspan="5">
<td id="consignee-details" class="border-black" colspan="5">
<span class="cell__heading">5. Consignee(s)/Destination(s)</span>
<span class="cell__uppercase">
<span class="cell__uppercase">
Expand Down Expand Up @@ -133,7 +133,7 @@ <h1>Standard Individual Export Licence</h1>
</td>
</tr>
<tr>
<td class="border-black" colspan="5" rowspan="3">
<td id="end-user-details" class="border-black" colspan="5" rowspan="3">
<span class="cell__heading">10. End User(if different from consignee)</span>
<span class="cell__uppercase">
{{ end_user.name }}<br>
Expand Down Expand Up @@ -203,7 +203,7 @@ <h1>Standard Individual Export Licence</h1>
<span class="cell__heading">20. Contract date</span>
<span class="cell__uppercase">{{ current_date }}</span>
</td>
<td class="border-black" colspan="3">
<td id="export-type" class="border-black" colspan="3">
<span class="cell__heading">21. Customs export procedure(Type of licence)</span>
<span class="cell__uppercase">{{ details.export_type }}</span>
</td>
Expand All @@ -217,7 +217,7 @@ <h1>Standard Individual Export Licence</h1>
</table>

<p style="page-break-before: always">
<table>
<table id="products-table">
<tr>
<td class="border-top-black padding-none align-centre" rowspan="999">
<div class="text-large border-left-black border-bottom-black">1</div>
Expand Down Expand Up @@ -276,11 +276,11 @@ <h1>Standard Individual Export Licence</h1>
</tr>
{% for good_item in goods.approve %}
{% with good=good_item.good firearm_details=good_item.firearm_details %}
<tr>
<tr id="product-row-{{ forloop.counter}}" >
<td class="border-black" colspan="1">{{ forloop.counter }}</td>
<td class="border-black" colspan="5">
<td id="row-{{ forloop.counter}}-description" class="border-black" colspan="5">
<table border="0">
<tr>
<tr id="row-{{ forloop.counter}}-description-name">
<td><strong>Name:</strong></td>
<td>{% if good.name %}{{ good.name }}{% else %}{{ good.description }}{% endif %}
</td>
Expand All @@ -289,32 +289,32 @@ <h1>Standard Individual Export Licence</h1>
<td><strong>Description:</strong></td>
<td>{{ good.description|default_na }}</td>
</tr>
<tr>
<tr id="row-{{ forloop.counter}}-description-part-number">
<td><strong>Part number:</strong></td>
<td>{{ good.part_number|default_na }}</td>
</tr>
{% if firearm_details and firearm_details.has_serial_numbers %}
<tr>
<tr id="row-{{ forloop.counter}}-description-serial-numbers">
<td><strong>Serial number:</strong></td>
<td>
{{ firearm_details.serial_numbers|format_serial_numbers|join:"<br>" }}
</td>
</tr>
{% else %}
<tr>
<tr id="row-{{ forloop.counter}}-description-serial-numbers-na">
<td><strong>Serial number:</strong></td>
<td>N/A</td>
</tr>
{% endif %}
</table>
</td>
<td class="border-black" colspan="2">
<td id="row-{{ forloop.counter}}-control-list-entries" class="border-black" colspan="2">
{% for clc in good.control_list_entries %}
{{ clc }}{% if not forloop.last %},{% endif %}
{% endfor %}
</td>
<td class="border-black" colspan="2">{{ good_item.value }}</td>
<td class="border-black" colspan="2">{{ good_item.quantity }}</td>
<td id="row-{{ forloop.counter}}-value" class="border-black" colspan="2">{{ good_item.value }}</td>
<td id="row-{{ forloop.counter}}-quantity" class="border-black" colspan="2">{{ good_item.quantity }}</td>
</tr>
{% endwith %}
{% endfor %}
Expand Down
3 changes: 2 additions & 1 deletion ci.env
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ AWS_SECRET_ACCESS_KEY=AWS_SECRET_ACCESS_KEY
AWS_STORAGE_BUCKET_NAME=AWS_STORAGE_BUCKET_NAME
AWS_REGION=eu-west-2
# AV
AV_SERVICE_URL=AV_SERVICE_URL
MOCK_VIRUS_SCAN_ACTIVATE_ENDPOINTS = True
AV_SERVICE_URL=http://localhost:8100/mock_virus_scan/scan
AV_SERVICE_USERNAME=AV_SERVICE_USERNAME
AV_SERVICE_PASSWORD=AV_SERVICE_PASSWORD
# HMRC Integration
Expand Down
12 changes: 9 additions & 3 deletions local.env
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ AWS_STORAGE_BUCKET_NAME=uploads
AWS_REGION=eu-west-2

# AV
AV_SERVICE_URL=<<FROM_VAULT>>
AV_SERVICE_USERNAME=<<FROM_VAULT>>
AV_SERVICE_PASSWORD=<<FROM_VAULT>>
AV_SERVICE_URL=http://localhost:8100/mock_virus_scan/scan
AV_SERVICE_USERNAME=DUMMY
AV_SERVICE_PASSWORD=DUMMY
MOCK_VIRUS_SCAN_ACTIVATE_ENDPOINTS=True

# HMRC Integration
LITE_HMRC_INTEGRATION_ENABLED=<<FROM_VAULT>>
Expand Down Expand Up @@ -87,3 +88,8 @@ ELASTICSEARCH_HOST=http://host.docker.internal:9200
# ELASTICSEARCH_HOST=http://localhost:9200

FEATURE_SIEL_COMPLIANCE_ENABLED=False

# Running celery non async for development
CELERY_ALWAYS_EAGER=True
CELERY_TASK_ALWAYS_EAGER=True
CELERY_TASK_STORE_EAGER_RESULT=True
12 changes: 12 additions & 0 deletions mock_virus_scan/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Mock Virus Scan

The purpose of mock virus scan is to give us the ability to swap out the external third party virus scan services that we use.

Swapping this out is useful when:

- we are running end-to-end tests and we don't want to rely on external services
- we want to develop locally without an internet connection

This mock replaces the virus scanning of documents that are uploaded. It's a dummy and not intented to provide any of the functionaity. It supports a postive scan using the ECIR pattern.

To enable set MOCK_VIRUS_SCAN_ACTIVATE_ENDPOINTS = True
Empty file added mock_virus_scan/__init__.py
Empty file.
56 changes: 56 additions & 0 deletions mock_virus_scan/tests/test_mock_virus_scan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from django.urls import reverse
from django.test import Client
from django.core.files.uploadedfile import SimpleUploadedFile

from rest_framework.test import APITestCase, override_settings
from test_helpers.helpers import reload_urlconf


@override_settings(MOCK_VIRUS_SCAN_ACTIVATE_ENDPOINTS=True)
class TestMockVirusScan(APITestCase):
def setUp(self):
super().setUp()

reload_urlconf()
self.url = reverse("mock_virus_scan:scan")
self.client = Client()

def test_mock_scan_virus_file(self):

# eicar standard industry pattern used to test positive virus
eicar_content = b"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*"

response = self.client.post(
self.url,
{
"file": [
SimpleUploadedFile("file 1", eicar_content),
]
},
)

self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {"malware": True})

def test_mock_scan_no_virus_file(self):

response = self.client.post(
self.url,
{
"file": [
SimpleUploadedFile("file 1", b"no virus"),
]
},
)

self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {"malware": False})

def test_mock_scan_no_file(self):

response = self.client.post(
self.url,
data={},
)

self.assertEqual(response.status_code, 400)
8 changes: 8 additions & 0 deletions mock_virus_scan/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.urls import path
from mock_virus_scan import views

app_name = "mock_virus_scan"

urlpatterns = [
path("scan", views.Scan.as_view(), name="scan"),
]
20 changes: 20 additions & 0 deletions mock_virus_scan/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from rest_framework.views import APIView
from rest_framework.parsers import MultiPartParser

from django.http import JsonResponse, HttpResponseBadRequest


class Scan(APIView):
parser_classes = (MultiPartParser,)
EICAR_TEST = b"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*" # noqa

def post(self, request):
if len(request.FILES) != 1:
return HttpResponseBadRequest("Provide a single file")

uploaded_file = request.FILES["file"]

if uploaded_file.file.read() == self.EICAR_TEST:
return JsonResponse({"malware": True})

return JsonResponse({"malware": False})

0 comments on commit e127df4

Please sign in to comment.