import base64 import json import logging from django.shortcuts import get_object_or_404 from rest_framework.exceptions import APIException from devices.models.endpoint_device import EndpointModel from devices.serializers.endpoint_serializers import EndpointConfigSerializer from devices.services.endpoint.endpoint_get_status import EndpointStatusService from devices.services.endpoint.endpoint_redis import RedisInterface from devices.services.vector import VectorService _log = logging.getLogger(__name__) class EndpointException(APIException): status_code = 400 default_detail = 'Cannot create endpoint.' class EndpointManagementService: """Service to management endpoint.""" def __init__(self, instance: EndpointModel) -> None: self.instance = instance self._log_service = VectorService(instance) def create(self): _log.debug(f'Create endpoint logger config') try: self._log_service.update_config() except Exception as err: _log.error(f'Cannot create log file: {err}') raise EndpointException(str(err)) def update(self): _log.debug(f'Update endpoint logger config') try: self._log_service.update_config() except Exception as err: _log.error(f'Cannot update logger config') raise EndpointException(str(err)) def destroy(self): _log.debug(f'Destroy endpoint logger config') try: self._log_service.delete_config() except Exception as err: _log.error(f'Cannot delete logger config') raise EndpointException(str(err)) class EndpointKepAliveService: def __init__(self, pk: int, data) -> None: self.endpoint = EndpointModel.objects.get(pk=pk) self.data = json.loads(data.decode('utf-8')) def get_response(self) -> dict: redis_interface = RedisInterface() redis_interface.set_keepalive(self.endpoint.pk) status = self.data['status'] if status == 'error': self.endpoint.config_errors = self.data['errors'] self.endpoint.incorrect_settings = True self.endpoint.save() if self.endpoint.settings_changed and self.data['status'] == 'ok': self.endpoint.settings_changed = False self.endpoint.incorrect_settings = False self.endpoint.config_errors = None self.endpoint.save() return {'status': 'ok', 'command': 'setting change'} if self.endpoint.request_config: return {'status': 'ok', 'command': 'upload'} return {'status': 'ok'} class EndpointDownloadConfigService: def __init__(self, pk: int) -> None: self.endpoint = get_object_or_404(EndpointModel, pk=pk) def _dict_to_json(self, config: dict) -> bytes: json_str = json.dumps(config, ensure_ascii=False, indent=4, sort_keys=True).encode("utf-8") return json_str def setup_endpoint(self) -> None: if self.endpoint.antivirus_start_scan: self.endpoint.antivirus_start_scan = False self.endpoint.save() def download(self) -> dict: config = self.save_endpoint_settings_to_dict() # Serialize data to string json_str = self._dict_to_json(config) base64_bytes = base64.b64encode(json_str) base64_message = base64_bytes.decode("utf-8") response = { 'status': 'ok', 'config': base64_message } return response def save_endpoint_settings_to_dict(self) -> dict: """Convert endpoint model to dict""" integrity_control_scan_paths = self.endpoint.scan_paths if self.endpoint.scan_paths else [] white_list_paths = self.endpoint.white_list_paths if self.endpoint.white_list_paths else [] antivirus_paths = self.endpoint.antivirus_paths if self.endpoint.antivirus_paths else [] config = { "device_control": { "cd": { "deny_read": self.endpoint.prohibit_cd_access, "deny_write": self.endpoint.prohibit_cd_access }, "enabled": self.endpoint.device_control_enabled, }, "integrity_control": { "control_path": integrity_control_scan_paths, "enabled": self.endpoint.integrity_control_enabled, "timeout": f'{self.endpoint.integrity_control_timeout}s', }, "white_list": { "enabled": self.endpoint.whitelist_enabled, "local_admin": self.endpoint.whitelist_admin, "path": white_list_paths, }, "usb_control": { "enabled": self.endpoint.usb_control_enabled, }, "antivirus": { "start_scan": self.endpoint.antivirus_start_scan, "scan_path": antivirus_paths, "enabled": self.endpoint.antivirus_enabled, "remove_infected_files": self.endpoint.antivirus_remove_infected_files, }, } rotation = { 'type': self.endpoint.event_rotation_type, # Int 'size': self.endpoint.event_rotation_size, # Int 'period': self.endpoint.event_rotation_period, # Int 'time': self.endpoint.event_rotation_time, # String in format: 01:00:00 } config['rotation'] = rotation return config def download_as_file(self) -> tuple: config = self.save_endpoint_settings_to_dict() filename = f'endpoint_config_{self.endpoint.pk}.json' return self._dict_to_json(config), filename class EndpointUploadConfigService: def __init__(self, pk: int, data) -> None: self.endpoint = EndpointModel.objects.get(pk=pk) self.data = json.loads(data.decode('utf-8')) self.prepare_data() def prepare_data(self): """Data preparation, mapping endpoint fields to DB names.""" mapping_fields = { # Endpoint/MC 'dc_enabled': 'device_control_enabled', 'ic_enabled': 'integrity_control_enabled', 'scan_folders': 'scan_paths', 'ic_timeout': 'integrity_control_timeout', 'wl_enable': 'whitelist_enabled', 'wl_admin': 'whitelist_admin', 'white_list': 'white_list_paths', 'clamav_enabled': 'antivirus_enabled', 'clamav_paths': 'antivirus_paths', 'clamav_remove_infected_files': 'antivirus_remove_infected_files', 'clamav_start_scan': 'antivirus_start_scan', } self.data['ip'] = self.endpoint.ip for alias, field in mapping_fields.items(): try: self.data[field] = self.data[alias] except KeyError: continue def upload(self): serializer = EndpointConfigSerializer(self.endpoint, data=self.data) self.endpoint.request_config = False if not serializer.is_valid(): _log.error(serializer.errors) self.endpoint.is_requested_config_correct = False self.endpoint.save() return {'status': 'error', 'error_message': serializer.errors} serializer.update(instance=self.endpoint, validated_data=serializer.validated_data) self.endpoint.is_requested_config_correct = True self.endpoint.incorrect_settings = False self.endpoint.config_errors = None self.endpoint.save() _log.info(f'Endpoint [{self.endpoint.pk}] updated form endpoint instance') return {'status': 'ok'} class EndpointUpdateService: """Service for update config from endpoint.""" def __init__(self, pk: int) -> None: self.endpoint = get_object_or_404(EndpointModel, pk=pk) self.status_service = EndpointStatusService(self.endpoint) def _update(self) -> None: self.endpoint.request_config = True self.endpoint.save() def update(self) -> dict: status = self.status_service.get_status()['status'] if status == 'offline': raise EndpointException('Endpoint is offline') self._update() return {'status': 'ok'}