Форматирование всего серверного кода по стандартам (black)

This commit is contained in:
parent e61ffd793c
commit 06a5a758a3
28 changed files with 611 additions and 305 deletions

View file

@ -3,20 +3,28 @@ from scapy.sendrecv import srp
import netifaces
from netaddr import IPNetwork
class NetworkScanner:
"""Иницилазицая сетевого сканера"""
def __init__(self, interface=None):
"""Инициализация параметров интерфейсов """
self.interface = interface or netifaces.gateways()['default'][netifaces.AF_INET][1]
"""Инициализация параметров интерфейсов"""
self.interface = (
interface or netifaces.gateways()["default"][netifaces.AF_INET][1]
)
self.ip_info = netifaces.ifaddresses(self.interface).get(netifaces.AF_INET, [])
self.networks = [IPNetwork(f"{ip['addr']}/{ip['netmask']}") for ip in self.ip_info]
self.local_mac = netifaces.ifaddresses(self.interface)[netifaces.AF_LINK][0]['addr']
self.networks = [
IPNetwork(f"{ip['addr']}/{ip['netmask']}") for ip in self.ip_info
]
self.local_mac = netifaces.ifaddresses(self.interface)[netifaces.AF_LINK][0][
"addr"
]
def _create_arp_packet(self, target_ip):
"""Создание пакета ARP для отправки"""
arp = ARP(pdst=target_ip)
ether = Ether(dst="ff:ff:ff:ff:ff:ff")
return ether/arp
return ether / arp
def scan_network(self, attempts=3, timeout=2):
"""Подготовка к сканированию сети"""
@ -28,9 +36,11 @@ class NetworkScanner:
clients = []
for _ in range(attempts):
result = srp(packet, timeout=timeout, iface=self.interface, verbose=0)[0]
result = srp(packet, timeout=timeout, iface=self.interface, verbose=0)[
0
]
for _, received in result:
client = {'ip': received.psrc, 'mac': received.hwsrc}
client = {"ip": received.psrc, "mac": received.hwsrc}
if client not in clients: # Избегаем дублирования клиентов
clients.append(client)
@ -38,7 +48,7 @@ class NetworkScanner:
# Добавляет все локальные IP-адреса интерфейса как клиенты
for ip_info in self.ip_info:
local_client = {'ip': ip_info['addr'], 'mac': self.local_mac}
local_client = {"ip": ip_info["addr"], "mac": self.local_mac}
if local_client not in all_clients:
all_clients.append(local_client)
@ -49,10 +59,11 @@ class NetworkScanner:
def print_clients(self, clients):
"""Лист адресов клиентов"""
print("Доступные устройства в сети:")
print("IP" + " "*18+"MAC")
for client in sorted(clients, key=lambda x: x['ip']):
print("IP" + " " * 18 + "MAC")
for client in sorted(clients, key=lambda x: x["ip"]):
print(f"{client['ip']:<16} {client['mac']}")
if __name__ == "__main__":
scanner = NetworkScanner()
clients = scanner.scan_network()

View file

@ -26,8 +26,10 @@ ACCESS_TOKEN_EXPIRE_MINUTES = 30
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
class AuthService:
"""Сервис для регистрации, авторизации и управления сессиями"""
def __init__(self):
self.storage = JsonStorage()
self.session_storage = SessionStorage()
@ -57,10 +59,14 @@ class AuthService:
db_user = self.storage.create_user(user)
return db_user
except ValueError as e:
logger.error(f"Ошибка при регистрации пользователя {user.username}: {str(e)}")
logger.error(
f"Ошибка при регистрации пользователя {user.username}: {str(e)}"
)
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error(f"Ошибка при регистрации пользователя {user.username}: {type(e).__name__} - {str(e)}")
logger.error(
f"Ошибка при регистрации пользователя {user.username}: {type(e).__name__} - {str(e)}"
)
raise HTTPException(status_code=500, detail="Ошибка регистрации")
def login(self, user: UserLogin, client_id: str) -> dict:
@ -70,20 +76,30 @@ class AuthService:
if not db_user:
logger.error(f"Пользователь {user.username} не найден")
raise HTTPException(status_code=401, detail="Неверный логин или пароль")
if not self.storage.verify_password(user.password, db_user["password_hash"]):
if not self.storage.verify_password(
user.password, db_user["password_hash"]
):
logger.error(f"Неверный пароль для пользователя {user.username}")
raise HTTPException(status_code=401, detail="Неверный логин или пароль")
access_token = self.create_access_token(data={"sub": user.username})
session_id = self.session_storage.create_session(user.username, access_token, client_id)
session_id = self.session_storage.create_session(
user.username, access_token, client_id
)
logger.info(f"Пользователь {user.username} успешно авторизован")
return {
"access_token": access_token,
"token_type": "bearer",
"session_id": session_id,
"user": UserResponse(id=db_user["id"], username=db_user["username"], is_admin=db_user["is_admin"])
"user": UserResponse(
id=db_user["id"],
username=db_user["username"],
is_admin=db_user["is_admin"],
),
}
except Exception as e:
logger.error(f"Ошибка при авторизации пользователя {user.username}: {type(e).__name__} - {str(e)}")
logger.error(
f"Ошибка при авторизации пользователя {user.username}: {type(e).__name__} - {str(e)}"
)
raise HTTPException(status_code=500, detail="Ошибка авторизации")
def get_current_user(self, token: str = Depends(oauth2_scheme)) -> UserResponse:
@ -97,7 +113,9 @@ class AuthService:
if not db_user:
logger.error(f"Пользователь {username} не найден")
raise HTTPException(status_code=401, detail="Пользователь не найден")
return UserResponse(id=db_user["id"], username=db_user["username"], is_admin=db_user["is_admin"])
return UserResponse(
id=db_user["id"], username=db_user["username"], is_admin=db_user["is_admin"]
)
def get_user_by_session(self, client_id: str) -> dict:
"""Получает пользователя и токен по client_id"""
@ -112,8 +130,12 @@ class AuthService:
logger.error(f"Пользователь {username} не найден")
raise HTTPException(status_code=401, detail="Пользователь не найден")
return {
"user": UserResponse(id=db_user["id"], username=db_user["username"], is_admin=db_user["is_admin"]),
"access_token": token
"user": UserResponse(
id=db_user["id"],
username=db_user["username"],
is_admin=db_user["is_admin"],
),
"access_token": token,
}
def logout(self, client_id: str):
@ -122,8 +144,10 @@ class AuthService:
session = self.session_storage.get_latest_session(client_id)
if session:
self.session_storage.delete_session(session["session_id"])
logger.info(f"Пользователь успешно вышел из системы для client_id {client_id}")
logger.info(
f"Пользователь успешно вышел из системы для client_id {client_id}"
)
return {"message": "Успешный выход из системы"}
except Exception as e:
logger.error(f"Ошибка при выходе из системы: {type(e).__name__} - {str(e)}")
raise HTTPException(status_code=500, detail="Ошибка при выходе из системы")
raise HTTPException(status_code=500, detail="Ошибка при выходе из системы")

View file

@ -67,10 +67,9 @@ class FirewalldService:
print(f"Timeout: {timeout}")
# Get the zone interface
zone_proxy = self.bus.get(
"org.fedoraproject.FirewallD1",
"/org/fedoraproject/FirewallD1"
"org.fedoraproject.FirewallD1", "/org/fedoraproject/FirewallD1"
)
zone_iface = zone_proxy['org.fedoraproject.FirewallD1.zone']
zone_iface = zone_proxy["org.fedoraproject.FirewallD1.zone"]
# Convert port to string
port_str = str(port)
# Convert timeout to integer

View file

@ -10,34 +10,39 @@ import sdbus
from sdbus_async.networkmanager.settings import ConnectionProfile
from services.arp_service import NetworkScanner
class IPService:
"""
Сервис проверки доступности IP-адреса и применения настроек сети
"""
def __init__(self):
self.arp_scanner = NetworkScanner()
self.bus = sdbus.sd_bus_open_system()
sdbus.set_default_bus(self.bus)
def _reset_bus(self):
""" Перезапускает соединение с системной шиной D-Bus """
"""Перезапускает соединение с системной шиной D-Bus"""
new_bus = sdbus.sd_bus_open_system()
sdbus.set_default_bus(new_bus)
if hasattr(self, 'bus') and self.bus is not None:
if hasattr(self, "bus") and self.bus is not None:
self.bus = None
self.bus = new_bus
async def check_ip_config(self, args: Dict[str, Any]) -> Dict[str, Any]:
""" Проверяет доступность IP-адреса перед его применением """
"""Проверяет доступность IP-адреса перед его применением"""
self._reset_bus()
clients = self.arp_scanner.scan_network()
new_ip = args["ipv4_updates"]["ipv4.address-data"][0]["address"]
if any(client['ip'] == new_ip for client in clients):
if any(client["ip"] == new_ip for client in clients):
return {"available": False, "message": f"IP address {new_ip} уже занят."}
return {"available": True, "message": "Network profile settings are available for use."}
return {
"available": True,
"message": "Network profile settings are available for use.",
}
async def apply_ip_config(self, args: Dict[str, Any]) -> None:
""" Применяет проверенные настройки IP """
"""Применяет проверенные настройки IP"""
self._reset_bus()
nm = NetworkManager()
settings = NetworkManagerSettings()
@ -51,7 +56,7 @@ class IPService:
settings_dict = profile.to_settings_dict()
for key_path, value in args["ipv4_updates"].items():
section, *props = key_path.split('.')
section, *props = key_path.split(".")
current = settings_dict[section]
for prop in props[:-1]:
current = current.setdefault(prop, {})
@ -61,14 +66,16 @@ class IPService:
await connection.update(new_profile.to_dbus())
print(f"Обновлены настройки для {args['connection_id']}:")
interface_name = settings_dict['connection']['interface-name']
interface_name = settings_dict["connection"]["interface-name"]
devices = await nm.get_devices()
for device_path in devices:
device = NetworkDeviceGeneric(device_path)
if await device.interface == interface_name:
await device.disconnect()
print(f"Отключён от {interface_name}")
await nm.activate_connection(connection=connection_paths[0], device=device_path)
await nm.activate_connection(
connection=connection_paths[0], device=device_path
)
print(f"Переподключён к {args['connection_id']}")
return

View file

@ -1,26 +1,27 @@
from typing import List, Dict, Tuple, Union, Optional
from sdbus_async.networkmanager import (
NetworkManager,
NetworkDeviceGeneric,
IPv4Config,
IPv6Config,
NetworkManagerSettings,
NetworkDeviceWireless,
AccessPoint,
DeviceType
NetworkManager,
NetworkDeviceGeneric,
IPv4Config,
IPv6Config,
NetworkManagerSettings,
NetworkDeviceWireless,
AccessPoint,
DeviceType,
)
from sdbus import sd_bus_open_system
from models.network import (
NetworkInterface,
IPv4Route,
IPv6Route,
IPConfig,
IndividualIPConfig,
NetworkInterfacesContainer,
WiFiDevice
NetworkInterface,
IPv4Route,
IPv6Route,
IPConfig,
IndividualIPConfig,
NetworkInterfacesContainer,
WiFiDevice,
)
import subprocess
class NetworkService:
"""Сервис для взаимодействия с NetworkManager через D-Bus"""
@ -34,71 +35,91 @@ class NetworkService:
try:
config = await device.get_applied_connection()
settings = config[0].get(ip_type, {})
return str(settings.get("method", ('s', 'unknown'))[1])
return str(settings.get("method", ("s", "unknown"))[1])
except Exception:
return 'unknown'
return "unknown"
async def get_connection_name(self, device: NetworkDeviceGeneric) -> str:
"""Извлекает имя соединения (GENERAL.CONNECTION) для устройства"""
try:
applied_connection = await device.get_applied_connection()
connection_settings = applied_connection[0]
connection_info = connection_settings.get('connection', {})
return str(connection_info.get('id', ('s', 'unknown'))[1])
connection_info = connection_settings.get("connection", {})
return str(connection_info.get("id", ("s", "unknown"))[1])
except Exception as e:
# print(f"Ошибка при получении соединения для устройства {await device.interface}: {e}")
# срёт ошибки дл отключенных интерфейсов
# добавить потом условия для отключенных интерфейсов
return 'unknown'
return "unknown"
async def get_ip_config(self, device: NetworkDeviceGeneric, ip_version: str) -> List[IndividualIPConfig]:
async def get_ip_config(
self, device: NetworkDeviceGeneric, ip_version: str
) -> List[IndividualIPConfig]:
"""Получает конфигурацию IP для устройства"""
configs = []
device_ip = await getattr(device, f'ip{ip_version}_config')
if device_ip != '/':
ip_config = globals()[f'IPv{ip_version}Config'](device_ip, self.system_bus)
device_ip = await getattr(device, f"ip{ip_version}_config")
if device_ip != "/":
ip_config = globals()[f"IPv{ip_version}Config"](device_ip, self.system_bus)
address_data = await ip_config.address_data
for address in address_data:
config = IndividualIPConfig(
address=str(address['address'][1]),
address=str(address["address"][1]),
routes=[
globals()[f'IPv{ip_version}Route'](
destination=route.get('dest', ('s', '0.0.0.0' if ip_version == '4' else '::'))[1],
prefix=route.get('prefix', ('u', 0))[1],
next_hop=route.get('next-hop', ('s', '0.0.0.0' if ip_version == '4' else '::'))[1],
metric=route.get('metric', ('u', 0))[1]
) for route in await ip_config.route_data
globals()[f"IPv{ip_version}Route"](
destination=route.get(
"dest", ("s", "0.0.0.0" if ip_version == "4" else "::")
)[1],
prefix=route.get("prefix", ("u", 0))[1],
next_hop=route.get(
"next-hop",
("s", "0.0.0.0" if ip_version == "4" else "::"),
)[1],
metric=route.get("metric", ("u", 0))[1],
)
for route in await ip_config.route_data
],
method=await self.get_ip_method(device, f'ipv{ip_version}'),
method=await self.get_ip_method(device, f"ipv{ip_version}"),
gateway=str(await ip_config.gateway),
dns=[str(ns['address'][1]) if ip_version == '4' else str(ns) for ns in await getattr(ip_config, 'nameserver_data' if ip_version == '4' else 'nameservers')]
dns=[
str(ns["address"][1]) if ip_version == "4" else str(ns)
for ns in await getattr(
ip_config,
"nameserver_data" if ip_version == "4" else "nameservers",
)
],
)
configs.append(config)
return configs
async def get_wifi_info(self, wifi_device: NetworkDeviceWireless) -> Dict[str, Optional[Union[str, int]]]:
async def get_wifi_info(
self, wifi_device: NetworkDeviceWireless
) -> Dict[str, Optional[Union[str, int]]]:
"""Получает информацию о Wi-Fi устройстве"""
active_ap = await wifi_device.active_access_point
wifi_info = {
'ssid': None,
'bssid': None,
'bitrate': await wifi_device.bitrate,
'strength': None,
'frequency': None,
'mode': None
"ssid": None,
"bssid": None,
"bitrate": await wifi_device.bitrate,
"strength": None,
"frequency": None,
"mode": None,
}
if active_ap != '/':
if active_ap != "/":
ap = AccessPoint(active_ap, self.system_bus)
wifi_info.update({
'ssid': await ap.ssid,
'bssid': await ap.hw_address,
'strength': await ap.strength,
'frequency': await ap.frequency,
'mode': await ap.mode
})
wifi_info.update(
{
"ssid": await ap.ssid,
"bssid": await ap.hw_address,
"strength": await ap.strength,
"frequency": await ap.frequency,
"mode": await ap.mode,
}
)
return wifi_info
async def get_network_interfaces(self, initial_positions: Dict[str, Tuple[int, int]] = None) -> List[Union[NetworkInterface, WiFiDevice]]:
async def get_network_interfaces(
self, initial_positions: Dict[str, Tuple[int, int]] = None
) -> List[Union[NetworkInterface, WiFiDevice]]:
"""Получает список сетевых интерфейсов"""
devices_paths = await self.nm.get_devices()
network_interfaces = []
@ -109,24 +130,28 @@ class NetworkService:
interface_name = str(await generic_device.interface)
device_type = DeviceType(await generic_device.device_type).name
if device_type == 'LOOPBACK':
if device_type == "LOOPBACK":
continue
device_state = str(await generic_device.state)
if device_state == '10':
if device_state == "10":
continue
common_data = {
'name': interface_name,
'type': device_type,
'device_state': device_state,
'active_connection': await self.get_connection_name(generic_device),
'mac_address': str(await generic_device.hw_address),
'device_mtu': str(await generic_device.mtu),
'ipv4': IPConfig(configurations=await self.get_ip_config(generic_device, '4')),
'ipv6': IPConfig(configurations=await self.get_ip_config(generic_device, '6')),
'x': initial_positions.get(interface_name, (100, 100))[0],
'y': initial_positions.get(interface_name, (100, 100))[1],
"name": interface_name,
"type": device_type,
"device_state": device_state,
"active_connection": await self.get_connection_name(generic_device),
"mac_address": str(await generic_device.hw_address),
"device_mtu": str(await generic_device.mtu),
"ipv4": IPConfig(
configurations=await self.get_ip_config(generic_device, "4")
),
"ipv6": IPConfig(
configurations=await self.get_ip_config(generic_device, "6")
),
"x": initial_positions.get(interface_name, (100, 100))[0],
"y": initial_positions.get(interface_name, (100, 100))[1],
}
if device_type == "WIFI":
@ -141,7 +166,9 @@ class NetworkService:
async def get_hostname(self) -> Dict[str, str]:
"""Возвращает имя хоста из команды hostname в формате JSON"""
try:
result = subprocess.run(['hostname'], capture_output=True, text=True, check=True)
result = subprocess.run(
["hostname"], capture_output=True, text=True, check=True
)
hostname = result.stdout.strip()
return {"hostname": hostname}
except subprocess.CalledProcessError as e:
@ -149,52 +176,77 @@ class NetworkService:
except Exception as e:
raise RuntimeError(f"Error processing hostname output: {e}")
async def get_interfaces_by_state(self, state: str, initial_positions: Dict[str, Tuple[int, int]] = None) -> List[NetworkInterface]:
async def get_interfaces_by_state(
self, state: str, initial_positions: Dict[str, Tuple[int, int]] = None
) -> List[NetworkInterface]:
"""
Получает список интерфейсов по состоянию
"""
all_interfaces = await self.get_network_interfaces(initial_positions)
return [interface for interface in all_interfaces if interface.device_state == state]
return [
interface for interface in all_interfaces if interface.device_state == state
]
async def get_interfaces_container(self, state: Optional[str] = None, initial_positions: Dict[str, Tuple[int, int]] = None, container_x: int = 0, container_y: int = 0) -> NetworkInterfacesContainer:
""" Получает объект, содержащий список интерфейсов и координаты контейнера """
async def get_interfaces_container(
self,
state: Optional[str] = None,
initial_positions: Dict[str, Tuple[int, int]] = None,
container_x: int = 0,
container_y: int = 0,
) -> NetworkInterfacesContainer:
"""Получает объект, содержащий список интерфейсов и координаты контейнера"""
if state:
interfaces = await self.get_interfaces_by_state(state, initial_positions)
else:
interfaces = await self.get_network_interfaces(initial_positions)
return NetworkInterfacesContainer(
interfaces=interfaces,
container_x=container_x,
container_y=container_y
interfaces=interfaces, container_x=container_x, container_y=container_y
)
async def get_network_interfaces_container(self, initial_positions: Optional[Dict[str, Tuple[int, int]]] = None, ) -> NetworkInterfacesContainer:
""" Получает контейнер с сетевыми интерфейсами и координатами контейнера """
async def get_network_interfaces_container(
self,
initial_positions: Optional[Dict[str, Tuple[int, int]]] = None,
) -> NetworkInterfacesContainer:
"""Получает контейнер с сетевыми интерфейсами и координатами контейнера"""
interfaces = await self.get_network_interfaces(initial_positions)
return NetworkInterfacesContainer(
interfaces=interfaces,
container_x=0,
container_y=0
interfaces=interfaces, container_x=0, container_y=0
)
async def get_activated_interfaces(self, initial_positions: Dict[str, Tuple[int, int]] = None) -> List[NetworkInterface]:
""" Получает список активных сетевых интерфейсов """
return await self.get_interfaces_by_state('100', initial_positions)
async def get_activated_interfaces(
self, initial_positions: Dict[str, Tuple[int, int]] = None
) -> List[NetworkInterface]:
"""Получает список активных сетевых интерфейсов"""
return await self.get_interfaces_by_state("100", initial_positions)
async def get_deactivated_interfaces(self, initial_positions: Dict[str, Tuple[int, int]] = None) -> List[NetworkInterface]:
""" Получает список неактивных сетевых интерфейсов """
async def get_deactivated_interfaces(
self, initial_positions: Dict[str, Tuple[int, int]] = None
) -> List[NetworkInterface]:
"""Получает список неактивных сетевых интерфейсов"""
all_interfaces = await self.get_network_interfaces(initial_positions)
return [interface for interface in all_interfaces if interface.device_state != '100']
return [
interface for interface in all_interfaces if interface.device_state != "100"
]
async def get_activated_interfaces_container(self, initial_positions: Dict[str, Tuple[int, int]] = None, container_x: int = 0, container_y: int = 0) -> NetworkInterfacesContainer:
""" Получает объект, содержащий список активных интерфейсов и координаты контейнера """
return await self.get_interfaces_container('100', initial_positions, container_x, container_y)
async def get_activated_interfaces_container(
self,
initial_positions: Dict[str, Tuple[int, int]] = None,
container_x: int = 0,
container_y: int = 0,
) -> NetworkInterfacesContainer:
"""Получает объект, содержащий список активных интерфейсов и координаты контейнера"""
return await self.get_interfaces_container(
"100", initial_positions, container_x, container_y
)
async def get_deactivated_interfaces_container(self, initial_positions: Dict[str, Tuple[int, int]] = None, container_x: int = 0, container_y: int = 0) -> NetworkInterfacesContainer:
""" Получает объект, содержащий список неактивных интерфейсов и координаты контейнера """
async def get_deactivated_interfaces_container(
self,
initial_positions: Dict[str, Tuple[int, int]] = None,
container_x: int = 0,
container_y: int = 0,
) -> NetworkInterfacesContainer:
"""Получает объект, содержащий список неактивных интерфейсов и координаты контейнера"""
interfaces = await self.get_deactivated_interfaces(initial_positions)
return NetworkInterfacesContainer(
interfaces=interfaces,
container_x=container_x,
container_y=container_y
interfaces=interfaces, container_x=container_x, container_y=container_y
)

View file

@ -19,9 +19,12 @@ from scapy.layers.inet import IP, TCP
from scapy.layers.l2 import ARP, Ether
from scapy.sendrecv import srp, sr1
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
class NetworkScanner:
def __init__(self, interface: str):
self.interface = interface
@ -38,6 +41,7 @@ class NetworkScanner:
logger.error(f"Ошибка ARP-сканирования подсети {subnet}: {str(e)}")
return []
class PortScanner:
def __init__(self, interface: str):
self.interface = interface
@ -51,7 +55,7 @@ class PortScanner:
IP(dst=ip) / TCP(dport=port, flags="S"),
timeout=2,
verbose=False,
iface=self.interface
iface=self.interface,
)
if pkt and pkt.haslayer(TCP) and pkt[TCP].flags == 0x12:
return port_config.get_port_name(port)
@ -71,6 +75,7 @@ class PortScanner:
logger.warning(f"Ошибка при сканировании портов для IP {ip}: {str(e)}")
return []
class DeviceInfoCollector:
def __init__(self):
self.mac_lookup = MacLookup()
@ -80,7 +85,9 @@ class DeviceInfoCollector:
try:
return self.mac_lookup.lookup(mac)
except Exception as e:
logger.warning(f"Не удалось определить производителя для MAC {mac}: {str(e)}")
logger.warning(
f"Не удалось определить производителя для MAC {mac}: {str(e)}"
)
return "Unknown"
def get_hostname(self, ip: str) -> Optional[str]:
@ -89,6 +96,7 @@ class DeviceInfoCollector:
except:
return None
class DeviceStorage:
def __init__(self, storage_path: str = "storage/devices/devices.json"):
self.storage_file = Path(storage_path)
@ -137,6 +145,7 @@ class DeviceStorage:
except Exception as e:
logger.error(f"Ошибка при сохранении устройств: {str(e)}")
class SubnetService:
def __init__(self):
self.interface = self._get_default_interface()
@ -187,12 +196,14 @@ class SubnetService:
return Device(
ip=ip,
mac=mac,
hostname=self.device_info.get_hostname(ip) or existing_device.hostname,
hostname=self.device_info.get_hostname(ip)
or existing_device.hostname,
vendor=self.device_info.get_vendor(mac),
open_ports=self.port_scanner.scan(ip) or existing_device.open_ports,
open_ports=self.port_scanner.scan(ip)
or existing_device.open_ports,
status="online",
last_seen=now,
note=existing_device.note
note=existing_device.note,
)
vendor = self.device_info.get_vendor(mac)
hostname = self.device_info.get_hostname(ip)
@ -205,7 +216,7 @@ class SubnetService:
open_ports=open_ports,
status="online",
last_seen=now,
note=None
note=None,
)
with ThreadPoolExecutor(max_workers=10) as executor:
@ -251,7 +262,9 @@ class SubnetService:
vendor = self.device_info.get_vendor(mac)
hostname = self.device_info.get_hostname(ip)
open_ports = self.port_scanner.scan(ip)
logger.debug(f"Локальное устройство: IP={ip}, MAC={mac}, Ports={open_ports}")
logger.debug(
f"Локальное устройство: IP={ip}, MAC={mac}, Ports={open_ports}"
)
return Device(
ip=ip,
mac=mac,
@ -260,7 +273,7 @@ class SubnetService:
open_ports=open_ports,
status="online",
last_seen=now,
note=None
note=None,
)
logger.warning(f"Локальное устройство не найдено в подсети {subnet}")
return None
@ -358,4 +371,4 @@ class SubnetService:
return True, None
except Exception as e:
logger.error(f"Ошибка при удалении заметки для MAC {mac}: {str(e)}")
return False, f"Внутренняя ошибка сервера: {str(e)}"
return False, f"Внутренняя ошибка сервера: {str(e)}"

View file

@ -9,9 +9,12 @@ import pywifi
from pywifi import Profile
from models.wifi import WifiAccessPoint, WifiScanResult
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logging.basicConfig(
level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
class WifiService:
"""Сервис для сканирования текущих Wi-Fi точек доступа"""
@ -28,11 +31,15 @@ class WifiService:
logger.error("Не найдено сетевых интерфейсов")
return None
logger.debug(f"Доступные интерфейсы: {[iface.name() for iface in interfaces]}")
logger.debug(
f"Доступные интерфейсы: {[iface.name() for iface in interfaces]}"
)
for iface in interfaces:
iface_name = iface.name()
if 'p2p' in iface_name.lower(): # Исключаю сетевые интерфейсы типа p2p из выбора, возможно сделать решение получше нежели исключать интерфейсы
if (
"p2p" in iface_name.lower()
): # Исключаю сетевые интерфейсы типа p2p из выбора, возможно сделать решение получше нежели исключать интерфейсы
logger.debug(f"Пропущен интерфейс {iface_name} (P2P)")
continue
try:
@ -42,7 +49,9 @@ class WifiService:
if results:
logger.info(f"Выбран Wi-Fi-интерфейс: {iface_name}")
return iface
logger.debug(f"Интерфейс {iface_name} не вернул результатов сканирования")
logger.debug(
f"Интерфейс {iface_name} не вернул результатов сканирования"
)
except Exception as e:
logger.debug(f"Интерфейс {iface_name} неактивен: {str(e)}")
@ -57,7 +66,9 @@ class WifiService:
try:
return self.mac_lookup.lookup(mac)
except Exception as e:
logger.warning(f"Не удалось определить производителя для MAC {mac}: {str(e)}")
logger.warning(
f"Не удалось определить производителя для MAC {mac}: {str(e)}"
)
return "Unknown"
def _get_encryption_from_iw(self, ap_info: Dict) -> str:
@ -86,7 +97,9 @@ class WifiService:
logger.debug(f"Частота {frequency} вне диапазона 2.4/5 GHz")
return None
except Exception as e:
logger.debug(f"Ошибка при определении канала для частоты {frequency}: {str(e)}")
logger.debug(
f"Ошибка при определении канала для частоты {frequency}: {str(e)}"
)
return None
def _get_frequency(self, frequency: Optional[int]) -> Optional[str]:
@ -101,7 +114,9 @@ class WifiService:
logger.debug(f"Частота {frequency} вне диапазона 2.4/5 GHz")
return None
except Exception as e:
logger.debug(f"Ошибка при определении диапазона для частоты {frequency}: {str(e)}")
logger.debug(
f"Ошибка при определении диапазона для частоты {frequency}: {str(e)}"
)
return None
def _get_iw_scan_data(self) -> List[Dict]:
@ -137,7 +152,9 @@ class WifiService:
current_ap = {"bssid": bssid_match.group(1).lower(), "security": ""}
elif current_ap:
if ssid_match := ssid_pattern.match(line):
current_ap["ssid"] = ssid_match.group(1) if ssid_match.group(1).strip() else ""
current_ap["ssid"] = (
ssid_match.group(1) if ssid_match.group(1).strip() else ""
)
elif freq_match := freq_pattern.match(line):
current_ap["frequency"] = int(freq_match.group(1))
elif signal_match := signal_pattern.match(line):
@ -152,7 +169,9 @@ class WifiService:
if current_ap:
aps.append(current_ap)
logger.debug(f"Найдено {len(aps)} точек через iw: {[ap['bssid'] for ap in aps]}")
logger.debug(
f"Найдено {len(aps)} точек через iw: {[ap['bssid'] for ap in aps]}"
)
for ap in aps:
logger.debug(f"Данные точки {ap['bssid']}: {ap}")
return aps
@ -176,16 +195,22 @@ class WifiService:
return WifiScanResult(access_points=aps)
try:
logger.info(f"Начало сканирования Wi-Fi на интерфейсе {self.interface.name()}")
logger.info(
f"Начало сканирования Wi-Fi на интерфейсе {self.interface.name()}"
)
self.interface.scan()
time.sleep(10) # Нативное значение для wifi интерфейса предназначенное на сканирование и сбор результата. Оптимальное решение от 5 до 15 секунд
time.sleep(
10
) # Нативное значение для wifi интерфейса предназначенное на сканирование и сбор результата. Оптимальное решение от 5 до 15 секунд
py_results = self.interface.scan_results()
logger.debug(f"Найдено {len(py_results)} точек через pywifi")
iw_results = self._get_iw_scan_data()
iw_by_bssid = {ap['bssid']: ap for ap in iw_results if ap.get('bssid')}
iw_by_bssid = {ap["bssid"]: ap for ap in iw_results if ap.get("bssid")}
best_aps_by_ssid: Dict[str, WifiAccessPoint] = {} # Сделал словарь, чтобы агрегировать точки доступа у сетей для выявления лучшего
best_aps_by_ssid: Dict[str, WifiAccessPoint] = (
{}
) # Сделал словарь, чтобы агрегировать точки доступа у сетей для выявления лучшего
for ap in py_results:
bssid = ap.bssid.lower() if ap.bssid else None
if not bssid:
@ -203,7 +228,8 @@ class WifiService:
vendor = self.get_vendor(bssid)
logger.debug(
f"Точка {bssid}: freq={frequency}, channel={channel}, encryption={encryption}, signal={signal}")
f"Точка {bssid}: freq={frequency}, channel={channel}, encryption={encryption}, signal={signal}"
)
current_ap = WifiAccessPoint(
ssid=ssid,
@ -213,10 +239,14 @@ class WifiService:
encryption=encryption,
frequency=freq_band,
vendor=vendor,
hidden=hidden
hidden=hidden,
)
if ssid not in best_aps_by_ssid or (current_ap.signal and best_aps_by_ssid[ssid].signal and current_ap.signal > best_aps_by_ssid[ssid].signal):
if ssid not in best_aps_by_ssid or (
current_ap.signal
and best_aps_by_ssid[ssid].signal
and current_ap.signal > best_aps_by_ssid[ssid].signal
):
best_aps_by_ssid[ssid] = current_ap
aps = list(best_aps_by_ssid.values())
@ -229,4 +259,4 @@ class WifiService:
except Exception as e:
logger.error(f"Ошибка при сканировании Wi-Fi: {str(e)}")
return WifiScanResult(access_points=aps)
return WifiScanResult(access_points=aps)