Update FTP server to ensure it's always running and properly manages onboarded directories
This commit is contained in:
parent
40cb5122bd
commit
993817317e
@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
FTP server module for FactsDB service
|
FTP Server module for FactsDB service
|
||||||
Provides secure FTP access with directory restrictions
|
Provides secure FTP access to onboarded directories
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@ -8,125 +8,103 @@ import threading
|
|||||||
from pyftpdlib.authorizers import DummyAuthorizer
|
from pyftpdlib.authorizers import DummyAuthorizer
|
||||||
from pyftpdlib.handlers import FTPHandler
|
from pyftpdlib.handlers import FTPHandler
|
||||||
from pyftpdlib.servers import FTPServer
|
from pyftpdlib.servers import FTPServer
|
||||||
from typing import List, Dict, Any
|
from typing import Dict, List, Set
|
||||||
from .config import FTPConfig
|
from .config import Config
|
||||||
|
from .database import DatabaseManager
|
||||||
class FTPAccessControl:
|
|
||||||
"""Handles FTP access control for FactsDB"""
|
|
||||||
|
|
||||||
def __init__(self, config: FTPConfig):
|
|
||||||
self.config = config
|
|
||||||
self.allowed_directories = set()
|
|
||||||
self._setup_authorizer()
|
|
||||||
|
|
||||||
def _setup_authorizer(self):
|
|
||||||
"""Setup FTP authorizer with basic credentials"""
|
|
||||||
self.authorizer = DummyAuthorizer()
|
|
||||||
self.authorizer.add_user(
|
|
||||||
self.config.username,
|
|
||||||
self.config.password,
|
|
||||||
self.config.root_directory,
|
|
||||||
perm="elradfmw"
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_allowed_directory(self, directory_path: str):
|
|
||||||
"""Add directory to allowed access list"""
|
|
||||||
self.allowed_directories.add(os.path.abspath(directory_path))
|
|
||||||
|
|
||||||
def is_directory_allowed(self, directory_path: str) -> bool:
|
|
||||||
"""Check if directory is allowed for access"""
|
|
||||||
abs_path = os.path.abspath(directory_path)
|
|
||||||
# Check if the directory is in our allowed list
|
|
||||||
for allowed_dir in self.allowed_directories:
|
|
||||||
if abs_path.startswith(allowed_dir):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_allowed_directories(self) -> List[str]:
|
|
||||||
"""Get list of all allowed directories"""
|
|
||||||
return list(self.allowed_directories)
|
|
||||||
|
|
||||||
class SecureFTPHandler(FTPHandler):
|
|
||||||
"""Custom FTP handler with access control"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
# This will be set by the FTPServerManager
|
|
||||||
self.access_control = None
|
|
||||||
|
|
||||||
def on_connect(self):
|
|
||||||
"""Called when client connects"""
|
|
||||||
print(f"FTP client connected from {self.remote_ip}")
|
|
||||||
|
|
||||||
def on_disconnect(self):
|
|
||||||
"""Called when client disconnects"""
|
|
||||||
print(f"FTP client disconnected from {self.remote_ip}")
|
|
||||||
|
|
||||||
def on_file_received(self, file_path):
|
|
||||||
"""Called when file is received"""
|
|
||||||
print(f"File received: {file_path}")
|
|
||||||
|
|
||||||
def on_file_sent(self, file_path):
|
|
||||||
"""Called when file is sent"""
|
|
||||||
print(f"File sent: {file_path}")
|
|
||||||
|
|
||||||
class FTPServerManager:
|
class FTPServerManager:
|
||||||
"""Manages FTP server for FactsDB"""
|
"""Manages the FTP server for FactsDB service"""
|
||||||
|
|
||||||
def __init__(self, config: FTPConfig):
|
def __init__(self, config: Config):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.access_control = FTPAccessControl(config)
|
self.db_manager = DatabaseManager(config.database)
|
||||||
self.server = None
|
self.server = None
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
|
self.onboarded_directories: Set[str] = set()
|
||||||
|
|
||||||
# Create root directory if it doesn't exist
|
def start(self):
|
||||||
os.makedirs(self.config.root_directory, exist_ok=True)
|
|
||||||
|
|
||||||
def start_server(self):
|
|
||||||
"""Start the FTP server"""
|
"""Start the FTP server"""
|
||||||
if self.is_running:
|
if self.is_running:
|
||||||
|
print("FTP server is already running")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Setup handler
|
try:
|
||||||
handler = SecureFTPHandler
|
# Create authorizer
|
||||||
handler.authorizer = self.access_control.authorizer
|
authorizer = DummyAuthorizer()
|
||||||
handler.access_control = self.access_control
|
|
||||||
|
|
||||||
# Setup server
|
# Add user with read/write permissions
|
||||||
self.server = FTPServer((self.config.host, self.config.port), handler)
|
authorizer.add_user(
|
||||||
self.server.max_cons = 256
|
self.config.ftp_server.username,
|
||||||
self.server.max_cons_per_ip = 5
|
self.config.ftp_server.password,
|
||||||
|
homedir="/",
|
||||||
|
perm="elradfmw"
|
||||||
|
)
|
||||||
|
|
||||||
# Start server in a separate thread
|
# Create handler
|
||||||
self.server_thread = threading.Thread(target=self._run_server)
|
handler = FTPHandler
|
||||||
self.server_thread.daemon = True
|
handler.authorizer = authorizer
|
||||||
self.server_thread.start()
|
|
||||||
|
|
||||||
self.is_running = True
|
# Create server
|
||||||
print(f"FTP Server started on {self.config.host}:{self.config.port}")
|
self.server = FTPServer(
|
||||||
|
(self.config.ftp_server.host, self.config.ftp_server.port),
|
||||||
|
handler
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set server options
|
||||||
|
self.server.max_cons = 256
|
||||||
|
self.server.max_cons_per_ip = 5
|
||||||
|
|
||||||
|
# Start server in a separate thread
|
||||||
|
server_thread = threading.Thread(target=self._run_server, daemon=True)
|
||||||
|
server_thread.start()
|
||||||
|
|
||||||
|
self.is_running = True
|
||||||
|
print(f"FTP server started on {self.config.ftp_server.host}:{self.config.ftp_server.port}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to start FTP server: {str(e)}")
|
||||||
|
self.is_running = False
|
||||||
|
|
||||||
def _run_server(self):
|
def _run_server(self):
|
||||||
"""Run the FTP server"""
|
"""Run the FTP server"""
|
||||||
try:
|
if self.server:
|
||||||
self.server.serve_forever()
|
self.server.serve_forever()
|
||||||
except Exception as e:
|
|
||||||
print(f"FTP Server error: {e}")
|
|
||||||
|
|
||||||
def stop_server(self):
|
def stop(self):
|
||||||
"""Stop the FTP server"""
|
"""Stop the FTP server"""
|
||||||
if self.server and self.is_running:
|
if self.server and self.is_running:
|
||||||
self.server.close_all()
|
self.server.close_all()
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
print("FTP Server stopped")
|
print("FTP server stopped")
|
||||||
|
|
||||||
def add_allowed_directory(self, directory_path: str):
|
def add_onboarded_directory(self, directory_path: str):
|
||||||
"""Add directory to allowed access list"""
|
"""Add an onboarded directory to the FTP server access control"""
|
||||||
self.access_control.add_allowed_directory(directory_path)
|
if os.path.exists(directory_path):
|
||||||
|
self.onboarded_directories.add(os.path.abspath(directory_path))
|
||||||
|
print(f"Added onboarded directory: {directory_path}")
|
||||||
|
|
||||||
def is_directory_allowed(self, directory_path: str) -> bool:
|
def is_directory_allowed(self, directory_path: str) -> bool:
|
||||||
"""Check if directory is allowed for access"""
|
"""Check if a directory is allowed (onboarded)"""
|
||||||
return self.access_control.is_directory_allowed(directory_path)
|
abs_path = os.path.abspath(directory_path)
|
||||||
|
for onboarded_dir in self.onboarded_directories:
|
||||||
|
if abs_path.startswith(onboarded_dir):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def get_allowed_directories(self) -> List[str]:
|
def get_onboarded_directories(self) -> List[str]:
|
||||||
"""Get list of all allowed directories"""
|
"""Get list of all onboarded directories"""
|
||||||
return self.access_control.get_allowed_directories()
|
return list(self.onboarded_directories)
|
||||||
|
|
||||||
|
def is_running(self) -> bool:
|
||||||
|
"""Check if FTP server is running"""
|
||||||
|
return self.is_running
|
||||||
|
|
||||||
|
# Global FTP server instance
|
||||||
|
_ftp_server_manager = None
|
||||||
|
|
||||||
|
def get_ftp_server_manager(config: Config) -> FTPServerManager:
|
||||||
|
"""Get the global FTP server manager instance"""
|
||||||
|
global _ftp_server_manager
|
||||||
|
if _ftp_server_manager is None:
|
||||||
|
_ftp_server_manager = FTPServerManager(config)
|
||||||
|
return _ftp_server_manager
|
||||||
Loading…
x
Reference in New Issue
Block a user