import json import logging import os from unittest import mock import pytest from django.db.models import Q from django.urls import reverse from assets.models.assets import Asset from console.models import Connection, ConnectionType from console.tests.test_utils import PermApiBaseTest, wait_db_element from core.utils import fmt_input, dtnow from networkmap.api import (auto_netmap_protocol_filter, auto_netmap_time_filter, auto_netmap_assets_filter, apply_auto_netmap_filters) from networkmap.models import NetworkMap, NetworkMapBackgroundImage from networkmap.serializers import AutoNetmapElementsSerializer, AutoNetmapConnectionsSerializer from networkmap.tests.netmap_test_utils import get_mocked_elk_data, create_new_background_image from perms.models import Perm _log = logging.getLogger() BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) ELK_TEST_DATA = os.path.join(BASE_DIR, "tests", "elk_test_data.json") UPDATE_CONNECTION_API_ELK_DATA = os.path.join(BASE_DIR, "tests", "test_data/connection_update_api_data.json") CONNECTION_PROTOCOL_TEST_DATA = os.path.join(BASE_DIR, "tests", "test_data/connection_protocol_data.json") IMAGE_BOUNDS_DATA = {"bounds": {"x": 42, "y": 98, "width": 1110, "height": 484}, "locked": True, "name": "test_pic_name", "visible": True} netmap_test_permission = Perm.can_view_network TEST_LINKS = [ [[netmap_test_permission], 'netmap-maps-list'], [[netmap_test_permission], 'netmap-elements-list'], [[netmap_test_permission], 'netmap-groups-list'], [[netmap_test_permission], 'netmap-in-danger-list'], [[netmap_test_permission], 'auto-netmap-elements-list'], [[netmap_test_permission], 'auto-netmap-connections-list'], # [[netmap_test_permission], 'api_handle_filter_form', dict(form_type='assets')], # [[netmap_test_permission], 'api_handle_filter_form', dict(form_type='time')], # [[netmap_test_permission], 'api_handle_filter_form', dict(form_type='protocol')], # [[netmap_test_permission], 'api_handle_filter_form', dict(form_type='reset')], [[netmap_test_permission], 'background_images-list'], ] TEST_ASSETS_IP = [ [], ['192.168.1.20'], ['192.168.1.21'], ['192.168.1.20', '192.168.1.21'], ] TEST_IMAGE = os.path.join(BASE_DIR, "tests", "test_data/controller.png") # Don't touch skip tests here, until we start new device map @pytest.mark.django_db class TestApiTwo: @pytest.fixture(autouse=True) def setup_tests(self, client, add_user_with_permissions): """ Fixture for creating basic setup for following import/export tests """ username = 'user_admin' password = 'bar' client.logout() admin = add_user_with_permissions(username=username, password=password, is_superuser=True) client.force_login(admin) @pytest.mark.unit @pytest.mark.skip def test_check_connections_creation_from_elk_data(self, create_elk_function_test_data): """ Test for checking if task for creating connection type and connections between assets works correctly and created 5 connections between testing assets :return: assert 1: true if connection DB state before not equals to connection DB state after which means that connections are being created assert 2: true if 5 connection instances being created as intended assert 3: true if connection type DB state changed assert 4: true if 1 connection type instance being created as intended """ with mock.patch("elasticsearch.Elasticsearch.search") as mocked_search, \ mock.patch("elasticsearch.Elasticsearch.update_by_query") as mocked_update: connections_before = Connection.objects.all() get_mocked_elk_data(ELK_TEST_DATA, mocked_search, mocked_update) connections_after = Connection.objects.all() assert connections_after != connections_before assert len(connections_after) == 5 @pytest.mark.unit @pytest.mark.skip def test_check_connections_update_from_elk_data(self, create_elk_function_test_data): """ Test for checking if test connection is being updated after execution of update/create task :return: assert: true if test connection update field is changed after execution of task """ # Create connection, which will be updated by the task test_connection_type = ConnectionType.objects.create(name='TCP') test_connection = Connection.objects.create(src_asset=Asset.objects.get(ip='192.168.1.21'), dst_asset=Asset.objects.get(ip='10.10.1.21'), connection_protocol=test_connection_type) test_connection_update_time_before = test_connection.updated with mock.patch("elasticsearch.Elasticsearch.search") as mocked_search, mock.patch( "elasticsearch.Elasticsearch.update_by_query") as mocked_update: get_mocked_elk_data(ELK_TEST_DATA, mocked_search, mocked_update) test_connection_after_test = Connection.objects.get(src_asset=Asset.objects.get(ip='192.168.1.21'), dst_asset=Asset.objects.get(ip='10.10.1.21')) test_connection_update_time_after = test_connection_after_test.updated assert test_connection_update_time_before != test_connection_update_time_after @pytest.mark.unit def test_auto_netmap_assist_filter_without_neighbours(self, create_filter_test_data): """ Test for checking the correct execution of asset filter function, without neighbours :param create_filter_test_data: fixture for creating filters testing database :return: assert: true if testing assets ID's matches filter result ID's. Result of filter functions work is looking like this: [filtered_assets_queryset, filtered_connections_queryset]. """ test_queryset = Asset.objects.filter(ip__in=['192.168.1.21', '192.168.1.22']) test_connections = Connection.objects.filter(Q(src_asset__in=test_queryset) & Q(dst_asset__in=test_queryset)) assets_ips = list(test_queryset.values_list('id', flat=True)) result_list = auto_netmap_assets_filter(assets_ips, False) assert list(result_list[0].values_list('id', flat=True)) == assets_ips assert list(result_list[1].values_list('id', flat=True)) == list(test_connections.values_list('id', flat=True)) @pytest.mark.unit def test_auto_netmap_assist_filter_with_neighbours(self, create_filter_test_data): """ Test for checking the correct execution of asset filter function, with neighbours :param create_filter_test_data: fixture for creating filters testing database :return: assert: true if testing assets ID's matches filter result ID's Result of filter functions work is looking like this: [filtered_assets_queryset, filtered_connections_queryset]. """ test_selected_by_user_queryset = Asset.objects.filter(ip__in=['192.168.1.21', '192.168.1.22']) assets_ips = list(test_selected_by_user_queryset.values_list('id', flat=True)) result_list = auto_netmap_assets_filter(assets_ips, True) # List of assets PK, which should be in results of work of filter function expected_filter_result = Asset.objects.filter( ip__in=['192.168.1.21', '192.168.1.22', '192.168.1.23', '192.168.1.24']).values_list('id', flat=True) assert set(result_list[0].values_list('id', flat=True)) == set(expected_filter_result) @pytest.mark.unit def test_auto_netmap_protocol_filter(self, create_filter_test_data): """ Test for checking the correct execution of protocol filter function :param create_filter_test_data: fixture for creating filters testing database :return: assert: true if testing connections ID's matches filter result ID's Result of filter functions work is looking like this: [filtered_assets_queryset, filtered_connections_queryset]. """ test_selected_protocol = Connection.ProtocolType.UDP result_list = auto_netmap_protocol_filter([test_selected_protocol]) # PK of connection, which should be in results of work of filter function expected_filter_result = Connection.objects.filter(connection_protocol=test_selected_protocol).values_list('id', flat=True) assert set(result_list[1].values_list('id', flat=True)) == set(expected_filter_result) @pytest.mark.unit def test_auto_netmap_time_range_filter(self, create_filter_test_data): """ Test for checking the correct execution of time range filter function :param create_filter_test_data: fixture for creating filters testing database :return: assert: true if testing connections ID's matches filter result ID's Result of filter functions work is looking like this: [filtered_assets_queryset, filtered_connections_queryset]. """ time_test_input = f'{fmt_input(dtnow(days=9))} - {fmt_input(dtnow(days=11))}' result_list = auto_netmap_time_filter(time_test_input) # PK of connection, which should be in results of work of filter function expected_filter_result = Connection.objects.filter( connection_protocol=ConnectionType.objects.get(name='UDP')).values_list('id', flat=True) assert set(result_list[1].values_list('id', flat=True)) == set(expected_filter_result) @pytest.mark.unit def test_auto_netmap_combined_filters(self, create_filter_test_data): """ Test for checking the work of applying multiple filters function :param create_filter_test_data: fixture for creating filters testing database :return: assert: true if execution result data is equal to expected """ input_filter_assets = list( Asset.objects.filter(ip__in=['192.168.1.22', '192.168.1.24']).values_list('id', flat=True)) input_protocol_filter = Connection.ProtocolType.UDP input_time_filter = f'{fmt_input(dtnow(days=9))} - {fmt_input(dtnow(days=11))}' filter_result_data = { 'assets_filter': { 'assets': input_filter_assets, 'include_neighbours': True }, 'protocol_filter': [input_protocol_filter], 'time_filter': input_time_filter } result_list = apply_auto_netmap_filters(filter_result_data) kek = Asset.objects.filter(pk__in=input_filter_assets).reverse() result_asset_data = AutoNetmapElementsSerializer(kek, many=True) result_connection_data = AutoNetmapConnectionsSerializer( Connection.objects.filter(connection_protocol=input_protocol_filter), many=True) assert_data = { 'edges': result_connection_data.data, 'nodes': result_asset_data.data, } assert result_list == assert_data @pytest.mark.unit @pytest.mark.skip @pytest.mark.parametrize('pre_loaded_assets', TEST_ASSETS_IP) def test_assets_created_from_update_connection_api(self, pre_loaded_assets): """ This test checks if update connection api creates proper assets if ELK data has IP's of assets, that are not in DB. Also checks if API does not create duplicate assets """ # Creating assets if len(pre_loaded_assets) > 0: for asset_ip in pre_loaded_assets: Asset.objects.create(ip=asset_ip, sensor='armaif_1') with mock.patch("elasticsearch.Elasticsearch.search") as mocked_search, \ mock.patch("elasticsearch.Elasticsearch.update_by_query") as mocked_update: connections_before = Connection.objects.all() assets_before = Asset.objects.all() get_mocked_elk_data(UPDATE_CONNECTION_API_ELK_DATA, mocked_search, mocked_update) connections_after = Connection.objects.all() assets_after = Asset.objects.all() assert assets_before != assets_after assert assets_after.count() == 2 assert connections_after != connections_before assert connections_after.count() == 1 @pytest.mark.unit @pytest.mark.skip def test_upd_tcp_protocol_connection_creation(self): """ Test for checking if events parsed correctly and creates both TCP and UDP connections """ with mock.patch("elasticsearch.Elasticsearch.search") as mocked_search, \ mock.patch("elasticsearch.Elasticsearch.update_by_query") as mocked_update: connections_before = Connection.objects.all() assets_before = Asset.objects.all() get_mocked_elk_data(CONNECTION_PROTOCOL_TEST_DATA, mocked_search, mocked_update) connections_after = Connection.objects.all() assets_after = Asset.objects.all() assert assets_before != assets_after assert assets_after.count() == 4 assert connections_after != connections_before assert connections_after.count() == 2 assert Connection.objects.get(connection_protocol=Connection.ProtocolType.UDP) assert Connection.objects.get(connection_protocol=Connection.ProtocolType.TCP) @pytest.mark.skip('Must be rewrite for API') def test_update_background_image_for_networkmap(self, client, test_server): test_map = NetworkMap.objects.create(name='test_map') create_new_background_image(test_map, client, 'test_pic_name') netmap_before = wait_db_element(lambda: NetworkMapBackgroundImage.objects.get(name='test_pic_name')) support_images_data = json.dumps(IMAGE_BOUNDS_DATA) url = reverse('background_images-update-image-data') user_input = {'current_map_id': test_map.pk, 'image_data': support_images_data} client.post(url, user_input, follow=True) netmap_after = wait_db_element(lambda: NetworkMapBackgroundImage.objects.get(name='test_pic_name')) assert netmap_after.bounds != netmap_before.bounds assert netmap_after.locked != netmap_before.locked assert netmap_after.visible != netmap_before.visible @pytest.mark.skip('Must be rewrite for API') def test_add_new_background_image_for_networkmap(self, client, test_server): test_map = NetworkMap.objects.create(name='test_map') create_new_background_image(test_map, client, 'test_pic_name') assert wait_db_element(lambda: NetworkMapBackgroundImage.objects.get(name='test_pic_name')) @pytest.mark.skip('Must be rewrite for API') def test_add_new_background_image_with_same_name_for_networkmap(self, client, test_server, caplog): test_map = NetworkMap.objects.create(name='test_map') create_new_background_image(test_map, client, 'test_pic_name') create_new_background_image(test_map, client, 'test_pic_name') assert f'Background image with this name already exist for current network' in caplog.text @pytest.mark.skip('Must be rewrite for API') def test_delete_network_background_image(self, client, test_server): test_map = NetworkMap.objects.create(name='test_map') create_new_background_image(test_map, client, 'test_pic_name_1') create_new_background_image(test_map, client, 'test_pic_name_2') background_images_before = NetworkMapBackgroundImage.objects.all().count() test_image = wait_db_element(lambda: NetworkMapBackgroundImage.objects.get(name='test_pic_name_1')) url = reverse('background_images-delete-background-image') user_input = {'current_map_id': test_map.pk, 'image_data': test_image.pk} client.post(url, user_input, follow=True) background_images_after = NetworkMapBackgroundImage.objects.all().count() assert background_images_after != background_images_before @pytest.mark.skip('Must be rewrite for API') def test_corrupted_image_data_when_updating_background_image_in_db(self, client, test_server): test_map = NetworkMap.objects.create(name='test_map') create_new_background_image(test_map, client, 'test_pic_name') wait_db_element(lambda: NetworkMapBackgroundImage.objects.get(name='test_pic_name')) support_images_data = 'kdasod11' url = reverse('background_images-update-image-data') user_input = {'current_map_id': test_map.pk, 'image_data': support_images_data} response = client.post(url, user_input, follow=True) assert '"error_message": "Image data is not valid"' in response.content.decode("utf-8") @pytest.mark.skip('Must be rewrite for API') def test_correct_post_arguments_check_for_background_image_update_api_map(self, client, caplog): test_map = NetworkMap.objects.create(name='test_map') create_new_background_image(test_map, client, 'test_pic_name') wait_db_element(lambda: NetworkMapBackgroundImage.objects.get(name='test_pic_name')) support_images_data = json.dumps(IMAGE_BOUNDS_DATA) url = reverse('background_images-update-image-data') user_input = {'image_data': support_images_data} response = client.post(url, user_input, follow=True) assert '"error_message": "Necessary data for request not provided or corrupted"' in response.content.decode( "utf-8") @pytest.mark.skip('Must be rewrite for API') def test_correct_post_arguments_check_for_background_image_update_api_image(self, client, caplog): test_map = NetworkMap.objects.create(name='test_map') create_new_background_image(test_map, client, 'test_pic_name') wait_db_element(lambda: NetworkMapBackgroundImage.objects.get(name='test_pic_name')) url = reverse('background_images-update-image-data') user_input = {'current_map_id': test_map.pk} response = client.post(url, user_input, follow=True) assert '"error_message": "Some mandatory arguments are missing"' in response.content.decode("utf-8") @pytest.mark.skip('Must be rewrite for API') def test_correct_post_arguments_check_for_background_image_delete_api_map(self, client, caplog): test_map = NetworkMap.objects.create(name='test_map') create_new_background_image(test_map, client, 'test_pic_name') test_image = wait_db_element(lambda: NetworkMapBackgroundImage.objects.get(name='test_pic_name')) support_images_data = test_image.pk url = reverse('background_images-delete-background-image') user_input = {'image_data': support_images_data} response = client.post(url, user_input, follow=True) assert '"error_message": "Necessary data for request not provided or corrupted"' in response.content.decode( "utf-8") @pytest.mark.skip('Must be rewrite for API') def test_correct_post_arguments_check_for_background_image_delete_api_image(self, client, caplog): test_map = NetworkMap.objects.create(name='test_map') create_new_background_image(test_map, client, 'test_pic_name') wait_db_element(lambda: NetworkMapBackgroundImage.objects.get(name='test_pic_name')) url = reverse('background_images-delete-background-image') user_input = {'current_map_id': test_map.pk} response = client.post(url, user_input, follow=True) assert '"error_message": "Necessary data for request not provided or corrupted"' in response.content.decode( "utf-8")