From 46290cea04fd09b7c4079e7b666e7d4e1675c05e Mon Sep 17 00:00:00 2001 From: ampotos Date: Wed, 21 Sep 2016 18:36:28 +0100 Subject: [PATCH 1/2] fix #152, handle multiple version of the same prog models history django-simple-history Add in txt and finish model Update requirements.txt --- module_com_bdd | 2 +- program_scanner | 2 +- requirements.txt | 1 + vigilate_backend/models.py | 17 ++-- vigilate_backend/serializers.py | 22 +---- vigilate_backend/settings.py | 2 + vigilate_backend/tests/test_Alert.py | 2 - vigilate_backend/tests/test_Alert_data.py | 10 +-- .../tests/test_ScannerToken_data.py | 4 +- vigilate_backend/tests/test_UserPrograms.py | 7 +- .../tests/test_UserPrograms_data.py | 6 +- vigilate_backend/utils.py | 35 +++++++- vigilate_backend/views.py | 88 +++++++++---------- 13 files changed, 106 insertions(+), 92 deletions(-) diff --git a/module_com_bdd b/module_com_bdd index e349ab2..ad042aa 160000 --- a/module_com_bdd +++ b/module_com_bdd @@ -1 +1 @@ -Subproject commit e349ab2291a7baf1fa2a8cbd05f1f6708d24c76a +Subproject commit ad042aa4536e6c5a6817ef78c5899b5f677f08c4 diff --git a/program_scanner b/program_scanner index 4eca3b8..8bcea58 160000 --- a/program_scanner +++ b/program_scanner @@ -1 +1 @@ -Subproject commit 4eca3b831c91c69ab7cd22c1037fcdeb96464474 +Subproject commit 8bcea5813bb50457f2d4ef709c51ee7da943889e diff --git a/requirements.txt b/requirements.txt index e1a3f54..f212565 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,3 +26,4 @@ coveralls==1.1 django-phonenumber-field==1.1.0 django-bulk-update==1.1.10 stripe==1.37.0 +django-simple-history diff --git a/vigilate_backend/models.py b/vigilate_backend/models.py index ba8374f..959b987 100644 --- a/vigilate_backend/models.py +++ b/vigilate_backend/models.py @@ -10,7 +10,7 @@ from django.utils.crypto import get_random_string from django.conf import settings from phonenumber_field.modelfields import PhoneNumberField - +from simple_history.models import HistoricalRecords # Create your models here. def longer_password(sender, *args, **kwargs): @@ -70,6 +70,7 @@ class User(AbstractBaseUser): id_dealer = models.IntegerField(default=0) plan = models.ForeignKey('Plans', default=None, null=True, on_delete=models.SET_DEFAULT) plan_purchase_date = models.DateTimeField(auto_now=True) + history = HistoricalRecords() is_superuser = models.BooleanField(default=False) is_active = True @@ -121,7 +122,8 @@ def set_password(self, password): hsh = PyArgon2.Hash_pwd(password.encode(), salt.encode()) hsh = (salt.encode()+b"$"+binascii.hexlify(hsh)).decode("utf8") self.password = hsh - + + def check_password(self, pwd): """Check if the given password match the real one """ @@ -166,7 +168,7 @@ class UserPrograms(models.Model): web_score = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(10)], default=0) web_enabled = models.BooleanField(default=True) alert_type_default = models.BooleanField(default=True) - + history = HistoricalRecords() def alert_id(self): return Alert.objects.filter(program=self.id).values_list('id', flat=True).first() @@ -183,7 +185,8 @@ class Alert(models.Model): program = models.ForeignKey('UserPrograms') cve = models.ManyToManyField('vulnerability_manager.Cve') view = models.BooleanField(default=False) - + history = HistoricalRecords() + def max_cvss(self): if self.cve.values_list("cvss_score", flat=True): return max(self.cve.values_list("cvss_score", flat=True)) @@ -226,7 +229,8 @@ def get_random_token(): user = models.ForeignKey('User') name = models.CharField(max_length=100) disabled = models.BooleanField(default=False) - + history = HistoricalRecords() + def generate_token(self): self.token = get_random_token() @@ -243,7 +247,7 @@ def get_random_token(): token = models.CharField(max_length=50, default=get_random_token, primary_key=True, unique=True) user = models.ForeignKey('User', null=True, default=None) date = models.DateTimeField(auto_now=True) - + history = HistoricalRecords() def is_valid(self): delta = now() - self.date @@ -252,3 +256,4 @@ def is_valid(self): return True class Meta: verbose_name_plural = "Sessions" + diff --git a/vigilate_backend/serializers.py b/vigilate_backend/serializers.py index dbcd45a..412b522 100644 --- a/vigilate_backend/serializers.py +++ b/vigilate_backend/serializers.py @@ -28,7 +28,7 @@ def create(self, validated_data): def validate(self, data): if not hasattr(self.instance, "id"): return data - + prev_state = models.User.objects.get(id=self.instance.id) if 'default_alert_type' in data and data['default_alert_type'] == models.User.SMS and\ (('phone' in data and not data['phone']) or (not 'phone' in data and not prev_state.phone)): @@ -59,6 +59,7 @@ class Meta: def create(self, validated_data): """Create an user program """ + validated_data['program_version'] = validated_data['program_version'][0] return models.UserPrograms.objects.create(**validated_data) def validate(self, data): @@ -67,25 +68,6 @@ def validate(self, data): raise serializers.ValidationError({'sms_enabled': ['Cannot enable sms alert for an user without a phone number registered']}) return data - def update(self, instance, validated_data): - """Update an user program - """ - instance.id = validated_data.get('id', instance.id) - instance.program_name = validated_data.get('program_name', instance.program_name) - instance.program_version = validated_data.get('program_version', instance.program_version) - instance.minimum_score = validated_data.get('minimum_score', instance.minimum_score) - instance.user = validated_data.get('user', instance.user) - instance.poste = validated_data.get('poste', instance.poste) - instance.sms_score = validated_data.get('sms_score', instance.sms_score) - instance.email_score = validated_data.get('email_score', instance.email_score) - instance.web_score = validated_data.get('web_score', instance.web_score) - instance.sms_enabled = validated_data.get('sms_enabled', instance.sms_enabled) - instance.email_enabled = validated_data.get('email_enabled', instance.email_enabled) - instance.web_enabled = validated_data.get('web_enabled', instance.web_enabled) - instance.alert_type_default = validated_data.get('alert_type_default', instance.alert_type_default) - instance.save() - return instance - class AlertSerializer(serializers.ModelSerializer): """Serialisation of user alerts """ diff --git a/vigilate_backend/settings.py b/vigilate_backend/settings.py index fe53a12..2c1a696 100644 --- a/vigilate_backend/settings.py +++ b/vigilate_backend/settings.py @@ -45,6 +45,7 @@ 'vigilate_backend', 'vulnerability_manager', 'django_nose', + 'simple_history', ) MIDDLEWARE_CLASSES = ( @@ -57,6 +58,7 @@ 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', 'middleware.vigilateMiddleware.VigilateMiddleware', + 'simple_history.moddleware.HistoryRequestMiddleware', ) ROOT_URLCONF = 'vigilate_backend.urls' diff --git a/vigilate_backend/tests/test_Alert.py b/vigilate_backend/tests/test_Alert.py index 37e55f5..2a6c9d8 100644 --- a/vigilate_backend/tests/test_Alert.py +++ b/vigilate_backend/tests/test_Alert.py @@ -134,8 +134,6 @@ def test_alert_when_prog_vuln_update(self): json.dumps(test_Alert_data.prog_vuln), content_type="application/json") - print(resp.content) - self.assertEqual(resp.status_code, 200) resp = self.client.get(basic_data.api_routes['alerts']) data = json.loads(resp.content.decode("utf8")) diff --git a/vigilate_backend/tests/test_Alert_data.py b/vigilate_backend/tests/test_Alert_data.py index d83baef..26242ba 100644 --- a/vigilate_backend/tests/test_Alert_data.py +++ b/vigilate_backend/tests/test_Alert_data.py @@ -1,12 +1,12 @@ from dateutil.parser import parse as parse_date -prog_not_vuln = {"program_name" : "firefox", "program_version" : "1337", "minimum_score": 0,"poste": "changed"} -prog_vuln = {"program_name" : "bzip2", "program_version" : "1.0.6", "minimum_score": 1,"poste": "changed"} -prog_vuln2 = {"program_name" : "glibc", "program_version" : "2.23", "minimum_score": 1,"poste": "changed"} +prog_not_vuln = {"program_name" : "firefox", "program_version" : ["1337"], "minimum_score": 0,"poste": "changed"} +prog_vuln = {"program_name" : "bzip2", "program_version" : ["1.0.6"], "minimum_score": 1,"poste": "changed"} +prog_vuln2 = {"program_name" : "glibc", "program_version" : ["2.23"], "minimum_score": 1,"poste": "changed"} prog_vuln_multi = [prog_vuln, prog_vuln2] -prog_vuln_sms = {"program_name" : "bzip2", "program_version" : "1.0.6", "minimum_score": 1,"poste": "changed", "alert_type_default": False, "sms_enabled": True, "sms_score": 0} +prog_vuln_sms = {"program_name" : "bzip2", "program_version" : ["1.0.6"], "minimum_score": 1,"poste": "changed", "alert_type_default": False, "sms_enabled": True, "sms_score": 0} -prog_vuln_before_update = {"program_name" : "bzip2", "program_version" : "1.0.5", "minimum_score": 0,"poste": "changed"} +prog_vuln_before_update = {"program_name" : "bzip2", "program_version" : ["1.0.5"], "minimum_score": 0,"poste": "changed"} proglist_vuln = {"programs_list": [prog_vuln], "poste": "changed"} proglist_vuln_multi = {"programs_list": prog_vuln_multi, "poste": "changed"} diff --git a/vigilate_backend/tests/test_ScannerToken_data.py b/vigilate_backend/tests/test_ScannerToken_data.py index 7269112..319087f 100644 --- a/vigilate_backend/tests/test_ScannerToken_data.py +++ b/vigilate_backend/tests/test_ScannerToken_data.py @@ -1,7 +1,7 @@ prog_list_to_submit = {"programs_list" : [ - {"program_name" : "mozilla firefox", "program_version" : "31.0"}, - {"program_name" : "blabla", "program_version" : "2.0.1"} + {"program_name" : "mozilla firefox", "program_version" : ["31.0"]}, + {"program_name" : "blabla", "program_version" : ["2.0.1"]} ], "poste" : "changed"} scanner = {"name": "Test station"} diff --git a/vigilate_backend/tests/test_UserPrograms.py b/vigilate_backend/tests/test_UserPrograms.py index 0d52a0c..eecc902 100644 --- a/vigilate_backend/tests/test_UserPrograms.py +++ b/vigilate_backend/tests/test_UserPrograms.py @@ -45,7 +45,7 @@ def test_submit_one_program(self): user_progs = models.UserPrograms.objects.filter(user=self.new_user["id"]) prog_saved = {"program_name" : user_progs[0].program_name, "program_version" : user_progs[0].program_version} - prog_sent = {"program_name" : prog["program_name"], "program_version" : prog["program_version"]} + prog_sent = {"program_name" : prog["program_name"], "program_version" : prog["program_version"][0]} self.assertEqual(prog_sent, prog_saved) @@ -54,12 +54,12 @@ def test_submit_one_program_json_encoded(self): resp = self.client.post(basic_data.api_routes['programs'], json.dumps(prog), content_type="application/json") - + self.assertEqual(resp.status_code, 200) user_progs = models.UserPrograms.objects.filter(user=self.new_user["id"]) prog_saved = {"program_name" : user_progs[0].program_name, "program_version" : user_progs[0].program_version} - prog_sent = {"program_name" : prog["program_name"], "program_version" : prog["program_version"]} + prog_sent = {"program_name" : prog["program_name"], "program_version" : prog["program_version"][0]} self.assertEqual(prog_sent, prog_saved) @@ -76,4 +76,5 @@ def test_submit_multiples_programs(self): database_programs_json.append(elem) for sent in prog_list['programs_list']: + sent['program_version'] = sent['program_version'][0] self.assertTrue(sent in database_programs_json) diff --git a/vigilate_backend/tests/test_UserPrograms_data.py b/vigilate_backend/tests/test_UserPrograms_data.py index 3559d26..b6aafaf 100644 --- a/vigilate_backend/tests/test_UserPrograms_data.py +++ b/vigilate_backend/tests/test_UserPrograms_data.py @@ -1,8 +1,8 @@ -prog_to_submit = [{"program_name" : "Google Chrome", "program_version" : "51.0", "minimum_score": 0,"poste": "changed"}] +prog_to_submit = [{"program_name" : "Google Chrome", "program_version" : ["51.0"], "minimum_score": 0,"poste": "changed"}] prog_list_to_submit = [{"programs_list" : [ - {"program_name" : "mozilla firefox", "program_version" : "31.0"}, - {"program_name" : "blabla", "program_version" : "2.0.1"} + {"program_name" : "mozilla firefox", "program_version" : ["31.0"]}, + {"program_name" : "blabla", "program_version" : ["2.0.1"]} ], "poste" : "changed"}] scanner = {"name": "Test station"} diff --git a/vigilate_backend/utils.py b/vigilate_backend/utils.py index 945baa1..2703af3 100644 --- a/vigilate_backend/utils.py +++ b/vigilate_backend/utils.py @@ -1,12 +1,15 @@ import json import base64 -from vigilate_backend.models import Station, User, Plans +from vigilate_backend.models import Station, User, Plans, UserPrograms from django.utils import timezone +from vigilate_backend.models import UserPrograms +from vigilate_backend import alerts +from vulnerability_manager import cpe_updater def get_query(request): """Parse a query """ - if request.method == "POST": + if request.method == "POST" or request.method == "PATCH": if "application/json" in request.content_type: return request.data query = list(request.data)[0] @@ -130,3 +133,31 @@ def check_expired_plan(user): user.plan = Plans.objects.filter(default=True).first() user.save() update_contrat(user) + +def add_progs(elem, versions, user, station, extra_field, up_to_date): + for version in versions: + (cpe, up_to_date) = cpe_updater.get_cpe_from_name_version(elem['program_name'], version, up_to_date) + new_prog = UserPrograms(user=user, minimum_score=1, poste=station, + program_name=elem['program_name'], program_version=version, cpe=cpe) + if 'minimum_score' in elem: + new_prog.minimum_score = int(elem['minimum_score']) + for f in extra_field: + setattr(new_prog, f, int(extra_field[f])) + + new_prog.save() + alerts.check_prog(new_prog, user) + +def maj_progs(progs, elem, versions, user, up_to_date): + for (prog, version) in zip(progs, versions): + prog_changed = False + if prog.program_version != version: + prog_changed = True + prog.program_version = version + (cpe, up_to_date) = cpe_updater.get_cpe_from_name_version(elem['program_name'], version, up_to_date) + prog.cpe = cpe + if 'minimum_score' in elem and prog.minimum_score != int(elem['minimum_score']): + prog_changed = True + prog.minimum_score = int(elem['minimum_score']) + if prog_changed: + prog.save() + alerts.check_prog(prog, user) diff --git a/vigilate_backend/views.py b/vigilate_backend/views.py index 218dbd3..924a85a 100644 --- a/vigilate_backend/views.py +++ b/vigilate_backend/views.py @@ -14,7 +14,7 @@ from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.exceptions import AuthenticationFailed from pkg_resources import parse_version -from vigilate_backend.utils import get_query, parse_cpe, get_token, get_scanner_cred, nb_station_over_quota, update_contrat +from vigilate_backend.utils import get_query, parse_cpe, get_token, get_scanner_cred, nb_station_over_quota, update_contrat, add_progs, maj_progs from vigilate_backend.models import User, UserPrograms, Alert, Station, Session, Plans from vigilate_backend.serializers import UserSerializer, UserProgramsSerializer, AlertSerializer, AlertSerializerDetail, StationSerializer, SessionSerializer, PlansSerializer from vigilate_backend import alerts @@ -171,9 +171,6 @@ def create(self, request): extra_field[k] = query[k] query['programs_list'] = [elem] - if UserPrograms.objects.filter(user=request.user.id, program_name=elem['program_name'], poste=station).exists(): - ret = {"program_name": ["A program with this name already exists for station %s" % (station.name)]} - return Response(ret, status=status.HTTP_400_BAD_REQUEST) for elem in query['programs_list']: if not all(x in elem for x in ['program_version', 'program_name']): @@ -184,60 +181,57 @@ def create(self, request): up_to_date = True for elem in query['programs_list']: - prog = UserPrograms.objects.filter(user=request.user.id, program_name=elem['program_name'], poste=station) + progs = UserPrograms.objects.filter(user=request.user.id, program_name=elem['program_name'], poste=station) # if prog, user is already monitoring the given program, update is needed - if prog: + if progs: - prog = prog[0] - prog_changed = False - if prog.program_version != elem['program_version']: - prog_changed = True - prog.program_version = elem['program_version'] - (cpe, up_to_date) = cpe_updater.get_cpe_from_name_version(elem['program_name'], elem['program_version'], up_to_date) - prog.cpe = cpe - if 'minimum_score' in elem and prog.minimum_score != int(elem['minimum_score']): - prog_changed = True - prog.minimum_score = int(elem['minimum_score']) - if prog_changed: - prog.save() - alerts.check_prog(prog, request.user) + if len(progs) == len(elem['program_version']): + maj_progs(progs, elem, elem['program_version'], request.user, up_to_date) + elif len(progs) > len(elem['program_version']): + # maj as much progs as tehy are in the data from the scanner + maj_progs(progs[:len(elem['program_version'])], elem, elem['program_version'], request.user, up_to_date) + # delete other progs with same name + for prog in progs[len(elem['program_version']):]: + prog.delete() + else: + # maj as much progs as tehy are in db + maj_progs(progs, elem, elem['program_version'][:len(progs)], request.user, up_to_date) + # create the other ones + add_progs(elem, elem['program_version'][len(progs):], request.user, station, extra_field, up_to_date) else: - #else: add a new program + #else: add new programs + add_progs(elem, elem['program_version'], request.user, station, extra_field, up_to_date) - (cpe, up_to_date) = cpe_updater.get_cpe_from_name_version(elem['program_name'], elem['program_version'], up_to_date) - - new_prog = UserPrograms(user=request.user, minimum_score=1, poste=station, - program_name=elem['program_name'], program_version=elem['program_version'], cpe=cpe) - if 'minimum_score' in elem: - new_prog.minimum_score = int(elem['minimum_score']) - for f in extra_field: - setattr(new_prog, f, int(extra_field[f])) - - new_prog.save() - alerts.check_prog(new_prog, request.user) - - if only_one_program: - obj = UserPrograms.objects.get(user=request.user.id, program_name=elem['program_name'], poste=station) + if only_one_program: # this sould happen only when the request came from the frontend + obj = UserPrograms.objects.get(user=request.user.id, program_name=elem['program_name'], poste=station, program_version=elem['program_version'][0]) serializer = self.get_serializer(obj) return Response(serializer.data, status=status.HTTP_200_OK) return Response(status=status.HTTP_200_OK) - - def perform_update(self, serializer): - orig = UserPrograms.objects.get(id=serializer.instance.id) + def update(self, request, pk=None, partial=None): + prog = UserPrograms.objects.get(id=pk) check = False - if orig.program_version != serializer.validated_data['program_version'] or \ - orig.program_name != serializer.validated_data['program_name']: - check = True - instance = serializer.save() - (cpe, _) = cpe_updater.get_cpe_from_name_version(instance.program_name, instance.program_version, True) - instance.cpe = cpe - instance.save(update_fields=["cpe"]) - if check: - alerts.check_prog(instance, self.request.user) - + query = get_query(request) + if query: + if prog.program_version != query['program_version'][0] or \ + prog.program_name != query['program_name']: + check = True + prog.program_version = query['program_version'][0] + prog.program_name = query['program_name'] + for k in ['sms_score', 'sms_enabled', 'email_score', 'email_enabled', + 'web_score', 'web_enabled', 'alert_type_default']: + if k in query: + setattr(prog, k, query[k]) + (cpe, _) = cpe_updater.get_cpe_from_name_version(prog.program_name, prog.program_version, True) + prog.cpe = cpe + prog.save() + if check: + alerts.check_prog(prog, self.request.user) + serializer = self.get_serializer(prog) + return Response(serializer.data, status=status.HTTP_200_OK) + return Response(status=status.HTTP_200_OK) class AlertViewSet(viewsets.ModelViewSet): """View for alerts From 56370e787e8c7bbf897d3fcb723d63a73b418e92 Mon Sep 17 00:00:00 2001 From: Vigilate Date: Thu, 24 Nov 2016 17:49:43 +0000 Subject: [PATCH 2/2] fix settings --- vigilate_backend/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vigilate_backend/settings.py b/vigilate_backend/settings.py index 2c1a696..46eab41 100644 --- a/vigilate_backend/settings.py +++ b/vigilate_backend/settings.py @@ -58,7 +58,7 @@ 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', 'middleware.vigilateMiddleware.VigilateMiddleware', - 'simple_history.moddleware.HistoryRequestMiddleware', + 'simple_history.middleware.HistoryRequestMiddleware', ) ROOT_URLCONF = 'vigilate_backend.urls'