Skip to content

Commit

Permalink
Merge pull request #907 from intuitem/CA-482-Add-version-check-during…
Browse files Browse the repository at this point in the history
…-backup-import

Add version check during backup import
  • Loading branch information
Mohamed-Hacene authored Oct 11, 2024
2 parents f90e850 + f8701b0 commit 3a1939a
Show file tree
Hide file tree
Showing 14 changed files with 166 additions and 33 deletions.
123 changes: 104 additions & 19 deletions backend/serdes/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import io
import json
import sys
import re
from datetime import datetime

from django.core import management
Expand All @@ -12,9 +13,15 @@
from rest_framework.response import Response
from rest_framework.views import APIView

from ciso_assistant.settings import VERSION
from ciso_assistant.settings import VERSION, SQLITE_FILE
from serdes.serializers import LoadBackupSerializer

import structlog

logger = structlog.get_logger(__name__)

GZIP_MAGIC_NUMBER = b"\x1f\x8b"


class ExportBackupView(APIView):
def get(self, request, *args, **kwargs):
Expand Down Expand Up @@ -54,22 +61,14 @@ class LoadBackupView(APIView):
parser_classes = (FileUploadParser,)
serializer_class = LoadBackupSerializer

def post(self, request, *args, **kwargs):
if not request.user.has_backup_permission:
return Response(status=status.HTTP_403_FORBIDDEN)
if request.data:
backup_file = request.data["file"]
is_json = backup_file.name.split(".")[-1].lower() == "json"
data = backup_file.read()
decompressed_data = data if is_json else gzip.decompress(data)
# Performances could be improved (by avoiding the json.loads + json.dumps calls with a direct raw manipulation on the JSON body)
# But performances of the backup loading is not that much important.
decompressed_data = json.loads(decompressed_data)[1]
decompressed_data = json.dumps(decompressed_data)

sys.stdin = io.StringIO(decompressed_data)
request.session.flush()
management.call_command("flush", interactive=False)
def load_backup(self, request, decompressed_data, backup_version, current_version):
with open(SQLITE_FILE, "rb") as database_file:
database_recover_data = database_file.read()

sys.stdin = io.StringIO(decompressed_data)
request.session.flush()
management.call_command("flush", interactive=False)
try:
# Here we load the data from stdin
management.call_command(
loaddata.Command(),
Expand All @@ -84,5 +83,91 @@ def post(self, request, *args, **kwargs):
"knox.authtoken",
],
)
return Response(status=status.HTTP_200_OK)
return Response(status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
logger.error("Error while loading backup", exc_info=e)
with open(SQLITE_FILE, "wb") as database_file:
database_file.write(database_recover_data)

if backup_version != current_version:
logger.error("Backup version different than current version")
return Response(
{"error": "LowerBackupVersion"},
status=status.HTTP_400_BAD_REQUEST,
)
return Response({}, status=status.HTTP_400_BAD_REQUEST)
return Response({}, status=status.HTTP_200_OK)

def post(self, request, *args, **kwargs):
if not request.user.has_backup_permission:
logger.error("Unauthorized user tried to load a backup", user=request.user)
return Response({}, status=status.HTTP_403_FORBIDDEN)
if not request.data:
logger.error("Request has no data")

return Response(
{"error": "backupLoadNoData"}, status=status.HTTP_400_BAD_REQUEST
)
backup_file = request.data["file"]
data = backup_file.read()
is_gzip = data.startswith(GZIP_MAGIC_NUMBER)
full_decompressed_data = gzip.decompress(data) if is_gzip else data
# Performances could be improved (by avoiding the json.loads + json.dumps calls with a direct raw manipulation on the JSON body)
# But performances of the backup loading is not that much important.
full_decompressed_data = json.loads(full_decompressed_data)
metadata, decompressed_data = full_decompressed_data
metadata = metadata["meta"]

backup_version = None
for metadata_part in metadata:
backup_version = metadata_part.get("media_version")
if backup_version is not None:
break

if backup_version is None:
logger.error("Backup malformed: no version found")
return Response(
{"erroe": "errorBackupNoVersion"},
status=status.HTTP_400_BAD_REQUEST,
)

if backup_version.lower() == "dev":
backup_version = "v0.0.0"

VERSION_REGEX = r"^v[0-9]+\.[0-9]+\.[0-9]+"
match = re.match(VERSION_REGEX, backup_version)
if match is None:
logger.error(
"Backup malformed: invalid version",
backup_version=backup_version,
current_version=VERSION,
)
return Response(
{"error": "errorBackupInvalidVersion"},
status=status.HTTP_400_BAD_REQUEST,
)

backup_version = match.group()
current_version = VERSION.split("-")[0]

if current_version.lower() == "dev":
current_version = "v0.0.0"

backup_version = [int(num) for num in backup_version.lstrip("v").split(".")]
current_version = [int(num) for num in current_version.lstrip("v").split(".")]
# All versions are composed of 3 numbers (see git tag)
for i in range(3):
if backup_version[i] > current_version[i]:
logger.error(
"Backup version greater than current version",
version=backup_version,
)
# Refuse to import the backup and ask to update the instance before importing the backup
return Response(
{"error": "GreaterBackupVersion"},
status=status.HTTP_400_BAD_REQUEST,
)

decompressed_data = json.dumps(decompressed_data)
return self.load_backup(
request, decompressed_data, backup_version, current_version
)
5 changes: 4 additions & 1 deletion frontend/messages/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -634,5 +634,8 @@
"integrity": "النزاهة",
"availability": "التوفر",
"authenticity": "الأصالة",
"waitingRiskAcceptances": "مرحبًا! لديك حاليًا {number} مخاطرة معلقة .يمكنك العثور عليها في علامة التبويب المخاطر."
"waitingRiskAcceptances": "مرحبًا! لديك حاليًا {number} مخاطرة معلقة .يمكنك العثور عليها في علامة التبويب المخاطر.",
"backupLoadingError": "حدث خطأ أثناء تحميل النسخة الاحتياطية.",
"backupGreaterVersionError": "لا يمكن تحميل النسخة الاحتياطية، إصدار النسخة الاحتياطية أعلى من الإصدار الحالي للتطبيق الخاص بك.",
"backupLowerVersionError": "حدث خطأ، قد تكون نسخة النسخ الاحتياطي قديمة جدًا، إذا كان الأمر كذلك فيجب تحديثها قبل إعادة المحاولة."
}
5 changes: 4 additions & 1 deletion frontend/messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -671,5 +671,8 @@
"observationSemiColon": "Beobachtung:",
"tableMode": "Tabellenmodus",
"owner": "Eigentümer",
"waitingRiskAcceptances": "Hallo! Sie haben derzeit {number} Risiko{s} zur Annahme ausstehend. Sie finden sie auf der Registerkarte „Risiken“."
"waitingRiskAcceptances": "Hallo! Sie haben derzeit {number} Risiko{s} zur Annahme ausstehend. Sie finden sie auf der Registerkarte „Risiken“.",
"backupLoadingError": "Beim Laden der Sicherung ist ein Fehler aufgetreten.",
"backupGreaterVersionError": "Das Backup kann nicht geladen werden, die Version des Backups ist höher als die aktuelle Version Ihrer Anwendung.",
"backupLowerVersionError": "Ein Fehler ist aufgetreten. Die Sicherungsversion ist möglicherweise zu alt. Wenn dies der Fall ist, muss sie vor einem erneuten Versuch aktualisiert werden."
}
3 changes: 3 additions & 0 deletions frontend/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,9 @@
"theFollowingControlsWillBeAddedColon": "The following controls will be added:",
"ShowAllNodesMessage": "Show all",
"ShowOnlyAssessable": "Only assessable",
"backupLoadingError": "An error occurred while loading the backup.",
"backupGreaterVersionError": "Can't load the backup, the version of the backup is higher than the current version of your application.",
"backupLowerVersionError": "An error occurred, the backup version may be too old, if so it must be updated before retrying.",
"NoPreviewMessage": "No preview available.",
"entityAssessmentEvidenceHelpText": "An external questionnaire",
"associatedEntities": "Associated entities",
Expand Down
5 changes: 4 additions & 1 deletion frontend/messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -671,5 +671,8 @@
"observationSemiColon": "Observación:",
"tableMode": "Modo de tabla",
"owner": "Titular",
"waitingRiskAcceptances": "Hola! Actualmente tienes {number} riesgo{s} aceptación pendiente. Puedes encontrarlos en la pestaña de riesgo."
"waitingRiskAcceptances": "Hola! Actualmente tienes {number} riesgo{s} aceptación pendiente. Puedes encontrarlos en la pestaña de riesgo.",
"backupLoadingError": "Se produjo un error al cargar la copia de seguridad.",
"backupGreaterVersionError": "No se puede cargar la copia de seguridad, la versión de la copia de seguridad es superior a la versión actual de la aplicación.",
"backupLowerVersionError": "Se produjo un error, la versión de respaldo puede ser demasiado antigua, si es así debe actualizarse antes de volver a intentarlo."
}
5 changes: 4 additions & 1 deletion frontend/messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -682,5 +682,8 @@
"createAppliedControlsFromSuggestionsConfirmMessage": "{count} mesures appliquées seront créées à partir des suggestions. Voulez-vous continuer ?",
"theFollowingControlsWillBeAddedColon": "Les mesures suivantes seront appliquées :",
"ShowAllNodesMessage": "Tout afficher",
"ShowOnlyAssessable": "Uniquement évaluables"
"ShowOnlyAssessable": "Uniquement évaluables",
"backupLoadingError": "Une erreur s'est produite lors du chargement de la sauvegarde.",
"backupGreaterVersionError": "Impossible de charger la sauvegarde, la version de la sauvegarde est supérieure à la version actuelle de votre application.",
"backupLowerVersionError": "Une erreur s'est produite, la version de sauvegarde est peut-être trop ancienne, si c'est le cas, elle doit être mise à jour avant de réessayer."
}
5 changes: 4 additions & 1 deletion frontend/messages/hi.json
Original file line number Diff line number Diff line change
Expand Up @@ -680,5 +680,8 @@
"availability": "उपलब्धता",
"authenticity": "प्रामाणिकता",
"owner": "मालिक",
"waitingRiskAcceptances": "नमस्ते! आपके पास वर्तमान में {number} जोखिम स्वीकृति{s} लंबित हैं। आप उन्हें जोखिम टैब में पा सकते हैं।"
"waitingRiskAcceptances": "नमस्ते! आपके पास वर्तमान में {number} जोखिम स्वीकृति{s} लंबित हैं। आप उन्हें जोखिम टैब में पा सकते हैं।",
"backupLoadingError": "बैकअप लोड करते समय एक त्रुटि हुई.",
"backupGreaterVersionError": "बैकअप लोड नहीं किया जा सकता, बैकअप का संस्करण आपके एप्लिकेशन के वर्तमान संस्करण से अधिक है.",
"backupLowerVersionError": "कोई त्रुटि हुई, बैकअप संस्करण बहुत पुराना हो सकता है, यदि ऐसा है तो पुनः प्रयास करने से पहले उसे अद्यतन करना आवश्यक है।"
}
5 changes: 4 additions & 1 deletion frontend/messages/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -671,5 +671,8 @@
"observationSemiColon": "Osservazione:",
"tableMode": "Modalità tabella",
"owner": "Proprietario",
"waitingRiskAcceptances": "Ciao! Al momento hai {number} rischi{s} in attesa di accettazione. Puoi trovarli nella scheda rischi."
"waitingRiskAcceptances": "Ciao! Al momento hai {number} rischi{s} in attesa di accettazione. Puoi trovarli nella scheda rischi.",
"backupLoadingError": "Si è verificato un errore durante il caricamento del backup.",
"backupGreaterVersionError": "Impossibile caricare il backup, la versione del backup è successiva alla versione corrente dell'applicazione.",
"backupLowerVersionError": "Si è verificato un errore, la versione di backup potrebbe essere troppo vecchia. In tal caso, è necessario aggiornarla prima di riprovare."
}
5 changes: 4 additions & 1 deletion frontend/messages/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -671,5 +671,8 @@
"observationSemiColon": "Observatie:",
"tableMode": "Tabelmodus",
"owner": "Eigenaar",
"waitingRiskAcceptances": "Hallo! U hebt momenteel {number} risico{s} acceptatie in behandeling. U kunt ze vinden in het tabblad risico."
"waitingRiskAcceptances": "Hallo! U hebt momenteel {number} risico{s} acceptatie in behandeling. U kunt ze vinden in het tabblad risico.",
"backupLoadingError": "Er is een fout opgetreden tijdens het laden van de back-up.",
"backupGreaterVersionError": "De back-up kan niet worden geladen. De versie van de back-up is hoger dan de huidige versie van uw applicatie.",
"backupLowerVersionError": "Er is een fout opgetreden. Mogelijk is de back-upversie te oud. Als dat zo is, moet u deze bijwerken voordat u het opnieuw probeert."
}
5 changes: 4 additions & 1 deletion frontend/messages/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -708,5 +708,8 @@
"observationSemiColon": "Obserwacja:",
"tableMode": "Tryb tabeli",
"owner": "Właściciel",
"waitingRiskAcceptances": "Cześć! Obecnie masz {number} ryzyko{s} oczekujące na akceptację. Możesz je znaleźć w zakładce ryzyko."
"waitingRiskAcceptances": "Cześć! Obecnie masz {number} ryzyko{s} oczekujące na akceptację. Możesz je znaleźć w zakładce ryzyko.",
"backupLoadingError": "Wystąpił błąd podczas ładowania kopii zapasowej.",
"backupGreaterVersionError": "Nie można załadować kopii zapasowej. Wersja kopii zapasowej jest nowsza niż bieżąca wersja aplikacji.",
"backupLowerVersionError": "Wystąpił błąd. Wersja kopii zapasowej może być za stara. Jeśli tak, należy ją zaktualizować przed ponowną próbą."
}
5 changes: 4 additions & 1 deletion frontend/messages/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -671,5 +671,8 @@
"observationSemiColon": "Observação:",
"tableMode": "Modo de tabela",
"owner": "Proprietário",
"waitingRiskAcceptances": "Olá! No momento, você tem {number} aceitação de risco{s} pendente. Você pode encontrá-los na aba de risco."
"waitingRiskAcceptances": "Olá! No momento, você tem {number} aceitação de risco{s} pendente. Você pode encontrá-los na aba de risco.",
"backupLoadingError": "Ocorreu um erro ao carregar o backup.",
"backupGreaterVersionError": "Não é possível carregar o backup, a versão do backup é superior à versão atual do seu aplicativo.",
"backupLowerVersionError": "Ocorreu um erro, a versão de backup pode ser muito antiga. Se for o caso, ela deve ser atualizada antes de tentar novamente."
}
5 changes: 4 additions & 1 deletion frontend/messages/ro.json
Original file line number Diff line number Diff line change
Expand Up @@ -676,5 +676,8 @@
"integrity": "Integritate",
"availability": "Accesibilitate",
"authenticity": "Autenticitate",
"waitingRiskAcceptances": "Buna ziua! În prezent, aveți {number} riscul{s} în așteptare. Le puteți găsi în fila de riscuri."
"waitingRiskAcceptances": "Buna ziua! În prezent, aveți {number} riscul{s} în așteptare. Le puteți găsi în fila de riscuri.",
"backupLoadingError": "A apărut o eroare la încărcarea copiei de rezervă.",
"backupGreaterVersionError": "Nu se poate încărca copia de rezervă, versiunea copiei de rezervă este mai mare decât versiunea curentă a aplicației dvs.",
"backupLowerVersionError": "A apărut o eroare, versiunea de rezervă poate fi prea veche, dacă da, trebuie actualizată înainte de a reîncerca."
}
5 changes: 4 additions & 1 deletion frontend/messages/ur.json
Original file line number Diff line number Diff line change
Expand Up @@ -680,5 +680,8 @@
"availability": "دستیابی",
"authenticity": "صداقت",
"owner": "مالک",
"waitingRiskAcceptances": "ہیلو! آپ کے پاس فی الحال {number} خطرے کی منظوری {s} زیر التواء ہیں۔ آپ انہیں خطرے والے ٹیب میں پا سکتے ہیں۔"
"waitingRiskAcceptances": "ہیلو! آپ کے پاس فی الحال {number} خطرے کی منظوری {s} زیر التواء ہیں۔ آپ انہیں خطرے والے ٹیب میں پا سکتے ہیں۔",
"backupLoadingError": "بیک اپ لوڈ کرتے وقت ایک خرابی پیش آگئی۔",
"backupGreaterVersionError": "بیک اپ لوڈ نہیں ہو سکتا، بیک اپ کا ورژن آپ کی ایپلیکیشن کے موجودہ ورژن سے زیادہ ہے۔",
"backupLowerVersionError": "ایک خرابی پیش آ گئی، بیک اپ ورژن بہت پرانا ہو سکتا ہے، اگر ایسا ہے تو اسے دوبارہ کوشش کرنے سے پہلے اپ ڈیٹ کرنا ضروری ہے۔"
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { BASE_API_URL } from '$lib/utils/constants';
import type { Actions } from '@sveltejs/kit';
import { fail } from 'assert';
import { setFlash } from 'sveltekit-flash-message/server';
import * as m from '$paraglide/messages';

export const actions: Actions = {
default: async ({ request, fetch }) => {
default: async (event) => {
const { request, fetch } = event;
const formData = Object.fromEntries(await request.formData());
if (!(formData.file as File).name || (formData.file as File).name === 'undefined') {
return fail(400, {
Expand All @@ -23,10 +26,19 @@ export const actions: Actions = {
},
body: file
});
const data = await response.text();
const data = await response.json();

if (response.status >= 400 && !data.error) {
setFlash({ type: 'error', message: m.backupLoadingError() }, event);
} else if (data.error === 'GreaterBackupVersion') {
setFlash({ type: 'error', message: m.backupGreaterVersionError() }, event);
} else if (data.error === 'LowerBackupVersion') {
setFlash({ type: 'error', message: m.backupLowerVersionError() }, event);
}

return {
status: response.status,
body: data
body: JSON.stringify(data)
};
}
};

0 comments on commit 3a1939a

Please sign in to comment.