Skip to content

Commit

Permalink
Merge pull request #194 from multiflexi/sftp_client
Browse files Browse the repository at this point in the history
SFTP  publisher
  • Loading branch information
milankowww authored Nov 29, 2023
2 parents 1b4da69 + 1d4b0aa commit 96838df
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 3 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ build/
.env.local
.env.*.local
src/.env
*.pem
src/publishers/crypto
!src/publishers/crypto/README.md
*.key
*.log
*.crt
*.asc
*.pub
*.pem
*.p12
local/

# settings of editors
Expand Down
50 changes: 49 additions & 1 deletion src/publishers/managers/publishers_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
"""
This module provides functionality for managing publishers in Taranis-NG.
The publishers manager is responsible for registering publishers, retrieving information about registered publishers,
and publishing data using the appropriate publisher based on the input.
The module defines the following functions:
- initialize(): Initializes the publishers by registering them.
- register_publisher(publisher): Registers a publisher.
- get_registered_publishers_info(): Retrieves information about the registered publishers.
- publish(publisher_input_json): Publishes the given input using the appropriate publisher.
The module also imports the following classes:
- FTPPublisher: Represents an FTP publisher.
- SFTPPublisher: Represents an SFTP publisher.
- EMAILPublisher: Represents an email publisher.
- TWITTERPublisher: Represents a Twitter publisher.
- WORDPRESSPublisher: Represents a WordPress publisher.
- MISPPublisher: Represents a MISP publisher.
The module imports the following modules:
- PublisherInputSchema: Provides a schema for validating the input JSON for publishers.
- log_manager: Provides functionality for logging critical messages.
"""

from publishers.ftp_publisher import FTPPublisher
from publishers.sftp_publisher import SFTPPublisher
from publishers.email_publisher import EMAILPublisher
from publishers.twitter_publisher import TWITTERPublisher
from publishers.wordpress_publisher import WORDPRESSPublisher
Expand All @@ -10,28 +36,50 @@


def initialize():
"""Initialize the publishers by registering them."""
register_publisher(FTPPublisher())
register_publisher(SFTPPublisher())
register_publisher(EMAILPublisher())
register_publisher(TWITTERPublisher())
register_publisher(WORDPRESSPublisher())
register_publisher(MISPPublisher())


def register_publisher(publisher):
"""
Register a publisher.
Args:
publisher: The publisher object to register.
"""
publishers[publisher.type] = publisher


def get_registered_publishers_info():
"""
Retrieve information about the registered publishers.
Returns:
A list of dictionaries containing information about each registered publisher.
"""
publishers_info = []
for key in publishers:
publishers_info.append(publishers[key].get_info())
log_manager.log_critical(publishers_info)


return publishers_info


def publish(publisher_input_json):
"""
Publish the given input using the appropriate publisher.
Args:
publisher_input_json: The JSON input for the publisher.
Raises:
ValidationError: If the input JSON is invalid.
"""
publisher_input_schema = PublisherInputSchema()
publisher_input = publisher_input_schema.load(publisher_input_json)
publishers[publisher_input.type].publish(publisher_input)
16 changes: 15 additions & 1 deletion src/publishers/publishers/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
__all__ = ['base_publisher', 'ftp_publisher', 'email_publisher', 'twitter_publisher', 'wordpress_publisher']
"""
This module contains the publishers package.
The publishers package provides various classes for publishing data to different platforms, including FTP, SFTP, email,
Twitter, and WordPress.
Classes:
- base_publisher: The base class for all publishers.
- ftp_publisher: A publisher for FTP.
- sftp_publisher: A publisher for SFTP.
- email_publisher: A publisher for email.
- twitter_publisher: A publisher for Twitter.
- wordpress_publisher: A publisher for WordPress.
"""
__all__ = ["base_publisher", "ftp_publisher", "sftp_publisher", "email_publisher", "twitter_publisher", "wordpress_publisher"]
154 changes: 154 additions & 0 deletions src/publishers/publishers/sftp_publisher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
"""Publisher for SFTP server."""
from datetime import datetime
from base64 import b64decode
import paramiko
import mimetypes
from io import BytesIO
import os

from managers import log_manager
from .base_publisher import BasePublisher
from shared.schema.parameter import Parameter, ParameterType


class SFTPPublisher(BasePublisher):
"""SFTP Publisher class.
This class represents a publisher that publishes data to an SFTP server.
Attributes:
type (str): The type of the publisher.
name (str): The name of the publisher.
description (str): The description of the publisher.
parameters (list): The list of parameters required for the publisher.
Methods:
publish(publisher_input): Publishes data to the SFTP server.
"""

type = "SFTP_PUBLISHER"
name = "SFTP Publisher"
description = "Publisher for publishing to SFTP server"

parameters = [
Parameter(0, "SFTP_URL", "SFTP URL", "SFTP server URL", ParameterType.STRING),
Parameter(0, "PORT", "SSH port", "Port remote machine is using for SSH (default 22)", ParameterType.STRING),
Parameter(0, "SSH_KEY", "SSH key", "Private key which should be used for SSH connection", ParameterType.STRING),
Parameter(0, "SSH_KEY_PASSWORD", "SSH key password", "Password for the SSH private key", ParameterType.STRING),
Parameter(0, "USERNAME", "Username", "Username for SFTP", ParameterType.STRING),
Parameter(0, "PASSWORD", "Password", "Password for SFTP", ParameterType.STRING),
Parameter(
0,
"PATH",
"Remote path",
"Either absolute or relative path where the file should be saved on the remote machine",
ParameterType.STRING,
),
Parameter(
0,
"FILENAME",
"Filename",
"Custom mame of the transported file without extension (default file_%d-%m-%Y_%H:%M)",
ParameterType.STRING,
),
Parameter(0, "COMMAND", "Command", "Command to be executed on the remote machine", ParameterType.STRING),
]
parameters.extend(BasePublisher.parameters)

def publish(self, publisher_input):
"""Publish to SFTP server.
This method publishes the data to the SFTP server based on the provided input.
Args:
publisher_input (PublisherInput): The input data for the publisher.
Raises:
Exception: If there is an error during the publishing process.
"""
url = publisher_input.parameter_values_map["SFTP_URL"]
port = publisher_input.parameter_values_map["PORT"]
username = publisher_input.parameter_values_map["USERNAME"]
password = publisher_input.parameter_values_map["PASSWORD"]
path = publisher_input.parameter_values_map["PATH"]
filename = publisher_input.parameter_values_map["FILENAME"]
command = publisher_input.parameter_values_map["COMMAND"]
ssh_key = publisher_input.parameter_values_map["SSH_KEY"]
ssh_key_password = publisher_input.parameter_values_map["SSH_KEY_PASSWORD"]

now = datetime.now().strftime("%Y%m%d%H%M%S")

def _get_key(key_path, ssh_key_password=None):
try:
ssh_key = paramiko.RSAKey(filename=key_path, password=ssh_key_password)
return ssh_key
except paramiko.ssh_exception.SSHException:
pass
try:
ssh_key = paramiko.Ed25519Key(filename=key_path, password=ssh_key_password)
return ssh_key
except paramiko.ssh_exception.SSHException:
pass
try:
ssh_key = paramiko.ECDSAKey(filename=key_path, password=ssh_key_password)
return ssh_key
except paramiko.ssh_exception.SSHException:
pass
try:
ssh_key = paramiko.DSSKey(filename=key_path, password=ssh_key_password)
return ssh_key
except paramiko.ssh_exception.SSHException as error:
log_manager.log_critical(f"Issue with SSH key {key_path}")
log_manager.log_debug(f"Error: {error}")
return None

try:
# decide filename and extension
mime_type = publisher_input.mime_type[:]
file_extension = mimetypes.guess_extension(mime_type)
if filename:
filename = f"{filename}{file_extension}"
else:
filename = f"file_{now}{file_extension}"
full_path = os.path.join(path, filename)

# fill file with data
data = publisher_input.data[:]
bytes_data = b64decode(data, validate=True)
file_object = BytesIO(bytes_data)

# decide SFTP port
port = port if port else 22

# determine SSH key type
if ssh_key:
ssh_key = _get_key(ssh_key, ssh_key_password)
else:
ssh_key = None

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if ssh_key:
ssh.connect(hostname=url, port=port, username=username, pkey=ssh_key)
else:
ssh.connect(hostname=url, port=port, username=username, password=password)
sftp = ssh.open_sftp()
log_manager.log_info(f"Successfully connected to {url} on port {port}")
try:
sftp.putfo(file_object, f"{full_path}", confirm=True)
log_manager.log_info(f"Successfully saved data to {full_path} on remote machine")
except Exception as error:
log_manager.log_critical(f"Failed to save data to {full_path} on remote machine")
log_manager.log_debug(f"Error: {error}")
if command:
try:
ssh.exec_command(command)
log_manager.log_info(f"Executed command {command} on remote machine")
except Exception as error:
log_manager.log_critical(f"Failed to execute command {command} on remote machine")
log_manager.log_debug(f"Error: {error}")
sftp.close()
except Exception as error:
BasePublisher.print_exception(self, error)

0 comments on commit 96838df

Please sign in to comment.