Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
fd0d64b
[REF] split sftp backend in a separated module
sebastienbeau Apr 11, 2018
82afb0a
[REF] rename method store and retrieve by more explicit method add/ge…
sebastienbeau Apr 11, 2018
483974b
[REF] refactor test in order to use the same test between the differe…
sebastienbeau Apr 11, 2018
7f88c0f
[IMP] add method for listing directory and deleting file on storage.b…
sebastienbeau Apr 13, 2018
d1acc76
[ADD] allow to connect SFTP using a ssh private key
Jun 21, 2018
4a4cb72
[PEP8] fix pep8 issue
sebastienbeau Jul 31, 2018
2504ddc
[FIX] clean with pre-commit and pep 8
bguillot Apr 10, 2019
8a22537
[IMP] add tests and support pilbox for thumbnail
bguillot Apr 12, 2019
8b378bf
[12.0] storage*: Make installable False
rousseldenis Jun 7, 2019
9fc369c
Migrate to storage_backend_sftp to v12
florian-dacosta Jun 27, 2019
152bcf0
[ADD] icon.png
OCA-git-bot Sep 15, 2019
3347c18
pre-commit, black, isort
sbidoul Oct 1, 2019
7ad17c6
[UPD] Update storage_backend_sftp.pot
oca-travis Oct 18, 2019
d873691
storage_backend_sftp: refactor tests w/ mock
simahawk Oct 16, 2019
2856828
13.0: Create branche
lmignon Oct 21, 2019
2eb12cf
[UPD] Update storage_backend_sftp.pot
oca-travis Nov 4, 2019
3cca51c
Fix runbot warning on clashing labels
simahawk Nov 22, 2019
2b4846e
Add server_env support
simahawk Nov 22, 2019
19da5bf
[UPD] Update storage_backend_sftp.pot
oca-travis Nov 25, 2019
6d2f240
storage_backend_sftp 12.0.2.0.0
OCA-git-bot Nov 25, 2019
ee5854a
[REF] storage_backend_sftp: Black python code
simahawk Jan 17, 2020
6f8822c
[MIG] storage_backend_sftp: Migration to 13.0
simahawk Jan 17, 2020
7f9ff14
[UPD] Update storage_backend_sftp.pot
oca-travis Jun 8, 2020
cd7e466
storage_backend_sftp: add conn validation
simahawk Jun 19, 2020
3f219a4
storage_backend_sftp: fix SSH key auth
simahawk Jun 19, 2020
3bb3905
[UPD] Update storage_backend_sftp.pot
oca-travis Jul 2, 2020
da5ddd9
storage_backend_sftp 13.0.1.1.0
OCA-git-bot Jul 2, 2020
99b8f62
storage_backend: run permission tests explicitely
simahawk Oct 29, 2020
48d8fc8
storage_backend_sftp: support adapter.move_files
simahawk Oct 29, 2020
2640318
storage_backend|_sftp: add test for find_files
simahawk Oct 29, 2020
d0d369f
storage_backend_sftp: add test for move_files
simahawk Oct 29, 2020
5288862
storage_backend_sftp bump 13.0.1.2.0
simahawk Nov 23, 2020
821961a
storage_backend_sftp: use public api
simahawk Nov 24, 2020
a0fd76e
storage_backend_sftp 13.0.1.3.0
OCA-git-bot Nov 25, 2020
276b93b
[ADD] add new V14 config
sebastienbeau Dec 6, 2020
8d8cf1e
[IMP] all: black, isort, prettier
sebastienbeau Dec 6, 2020
65214d7
[MIG] batch migration of modules
sebastienbeau Dec 6, 2020
67ba28b
storage_backend_sftp 14.0.1.0.1
OCA-git-bot Mar 1, 2021
03510fe
[UPD] Update storage_backend_sftp.pot
oca-travis Jun 9, 2021
3b53ab5
[CHG] storage: Use more permissive licence: AGPL-> LGPL
etobella Mar 10, 2021
bedffb3
storage_backend_sftp 14.0.2.0.0
OCA-git-bot Aug 2, 2021
27953e9
[13.0][ADD] storage_backend_ftp
acsonefho Jul 10, 2021
52d2b60
[UPD] Update storage_backend_sftp.pot
oca-travis Nov 30, 2021
64df671
storage_backend_sftp 14.0.2.0.1
OCA-git-bot Nov 30, 2021
ae848e1
[MIG][15.0] storage_backend_sftp
i-vyshnevska Dec 5, 2021
b394518
[FIX] storage_backend_sftp: use full path in move_files()
SilvioC2C Apr 6, 2022
6253368
[UPD] Update storage_backend_sftp.pot
Apr 11, 2022
cc3b086
storage_backend_sftp 15.0.1.0.1
OCA-git-bot Apr 11, 2022
483b2b0
storage_backend_sftp: Fix paramiko usage
jcoux May 18, 2022
1aead6d
storage_backend_sftp 15.0.1.0.2
OCA-git-bot May 25, 2022
698ccb2
[MIG] storage_backend_sftp: Migration to 16.0
santostelmo Feb 24, 2023
b525b2d
[UPD] Update storage_backend_sftp.pot
Feb 9, 2024
13cd2a5
Added translation using Weblate (Italian)
mymage Feb 13, 2024
d0116c9
Translated using Weblate (Italian)
mymage Feb 16, 2024
36554b3
storage_*: fix test import of mock
simahawk Sep 4, 2024
040636b
[BOT] post-merge updates
OCA-git-bot Sep 4, 2024
5e30757
[IMP] storage_backend_sftp: pre-commit auto fixes
alan196 Feb 26, 2025
38f5677
[MIG] storage_backend_sftp: Migration to 17.0
alan196 Feb 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# generated from manifests external_dependencies
fsspec>=2024.5.0
paramiko
python_slugify
44 changes: 44 additions & 0 deletions storage_backend_sftp/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3

=====================
Storage backend SFTP
=====================

Add the possibility to store and get data from an SFTP for your storage backend



Installation
============

To install this module, you need to:

#. (root) pip install paramiko


Known issues / Roadmap
======================

Update README with the last model of README when migration to v11 in OCA


Credits
=======


Contributors
------------

* Sebastien Beau <sebastien.beau@akretion.com>
* Raphaël Reverdy <raphael.reverdy@akretion.com>
* Cédric Pigeon <cedric.pigeon@acsone.eu>
* Simone Orsi <simone.orsi@camptocamp.com>


Maintainer
----------

* Akretion
2 changes: 2 additions & 0 deletions storage_backend_sftp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import components
17 changes: 17 additions & 0 deletions storage_backend_sftp/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2017 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

{
"name": "Storage Backend SFTP",
"summary": "Implement SFTP Storage",
"version": "17.0.1.0.0",
"category": "Storage",
"website": "https://github.com/OCA/storage",
"author": " Akretion,Odoo Community Association (OCA)",
"license": "LGPL-3",
"installable": True,
"external_dependencies": {"python": ["paramiko"]},
"depends": ["storage_backend"],
"data": ["views/backend_storage_view.xml"],
}
1 change: 1 addition & 0 deletions storage_backend_sftp/components/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import sftp_adapter
129 changes: 129 additions & 0 deletions storage_backend_sftp/components/sftp_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Copyright 2017 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# Copyright 2019 Camptocamp SA (http://www.camptocamp.com).
# Copyright 2020 ACSONE SA/NV (<http://acsone.eu>)
# @author Simone Orsi <simahawk@gmail.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import errno
import logging
import os
from contextlib import contextmanager
from io import StringIO

from odoo.addons.component.core import Component

_logger = logging.getLogger(__name__)

try:
import paramiko
except ImportError as err: # pragma: no cover
_logger.debug(err)


def sftp_mkdirs(client, path, mode=511):
try:
client.mkdir(path, mode)
except OSError as e:
if e.errno == errno.ENOENT and path:
sftp_mkdirs(client, os.path.dirname(path), mode=mode)
client.mkdir(path, mode)
else:
raise # pragma: no cover


def load_ssh_key(ssh_key_buffer):
for pkey_class in (
paramiko.RSAKey,
paramiko.DSSKey,
paramiko.ECDSAKey,
paramiko.Ed25519Key,
):
try:
return pkey_class.from_private_key(ssh_key_buffer)
except paramiko.SSHException:
ssh_key_buffer.seek(0) # reset the buffer "file"
raise Exception("Invalid ssh private key")


@contextmanager
def sftp(backend):
transport = paramiko.Transport((backend.sftp_server, backend.sftp_port))
if backend.sftp_auth_method == "pwd":
transport.connect(username=backend.sftp_login, password=backend.sftp_password)
elif backend.sftp_auth_method == "ssh_key":
ssh_key_buffer = StringIO(backend.sftp_ssh_private_key)
private_key = load_ssh_key(ssh_key_buffer)
transport.connect(username=backend.sftp_login, pkey=private_key)
client = paramiko.SFTPClient.from_transport(transport)
yield client
transport.close()


class SFTPStorageBackendAdapter(Component):
_name = "sftp.adapter"
_inherit = "base.storage.adapter"
_usage = "sftp"

def add(self, relative_path, data, **kwargs):
with sftp(self.collection) as client:
full_path = self._fullpath(relative_path)
dirname = os.path.dirname(full_path)
if dirname:
try:
client.stat(dirname)
except OSError as e:
if e.errno == errno.ENOENT:
sftp_mkdirs(client, dirname)
else:
raise # pragma: no cover
remote_file = client.open(full_path, "w")
remote_file.write(data)
remote_file.close()

def get(self, relative_path, **kwargs):
full_path = self._fullpath(relative_path)
with sftp(self.collection) as client:
file_data = client.open(full_path, "r")
data = file_data.read()
# TODO: shouldn't we close the file?
return data

def list(self, relative_path):
full_path = self._fullpath(relative_path)
with sftp(self.collection) as client:
try:
return client.listdir(full_path)
except OSError as e:
if e.errno == errno.ENOENT:
# The path do not exist return an empty list
return []
else:
raise # pragma: no cover

def move_files(self, files, destination_path):
_logger.debug("mv %s %s", files, destination_path)
fp = self._fullpath
with sftp(self.collection) as client:
for sftp_file in files:
dest_file_path = os.path.join(
destination_path, os.path.basename(sftp_file)
)
# Remove existing file at the destination path (an error is raised
# otherwise)
try:
client.lstat(dest_file_path)
except FileNotFoundError:
_logger.debug("destination %s is free", dest_file_path)
else:
client.unlink(dest_file_path)
# Move the file using absolute filepaths
client.rename(fp(sftp_file), fp(dest_file_path))

def delete(self, relative_path):
full_path = self._fullpath(relative_path)
with sftp(self.collection) as client:
return client.remove(full_path)

def validate_config(self):
with sftp(self.collection) as client:
client.listdir()
87 changes: 87 additions & 0 deletions storage_backend_sftp/i18n/it.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * storage_backend_sftp
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-02-16 16:36+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__backend_type
msgid "Backend Type"
msgstr "Tipo backend"

#. module: storage_backend_sftp
#: model:ir.model.fields,help:storage_backend_sftp.field_storage_backend__sftp_ssh_private_key
msgid ""
"It's recommended to not store the key here but to provide it via secret env "
"variable. See `server_environment` docs."
msgstr ""
"Si raccomanda si non salvare qui la chiave ma di fornirla attraverso una "
"variabile di ambiente segreta. Vedere documentazione 'server_enviroment'."

#. module: storage_backend_sftp
#: model:ir.model.fields,help:storage_backend_sftp.field_storage_backend__sftp_login
msgid "Login to connect to sftp server"
msgstr "Accedere per collegarsi al server SFTP"

#. module: storage_backend_sftp
#: model:ir.model.fields.selection,name:storage_backend_sftp.selection__storage_backend__sftp_auth_method__pwd
msgid "Password"
msgstr "Password"

#. module: storage_backend_sftp
#: model:ir.model.fields.selection,name:storage_backend_sftp.selection__storage_backend__sftp_auth_method__ssh_key
msgid "Private key"
msgstr "Chiave privata"

#. module: storage_backend_sftp
#: model:ir.model.fields.selection,name:storage_backend_sftp.selection__storage_backend__backend_type__sftp
#: model_terms:ir.ui.view,arch_db:storage_backend_sftp.storage_backend_view_form
msgid "SFTP"
msgstr "SFTP"

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_auth_method
msgid "SFTP Authentification Method"
msgstr "Metodo autenticazione SFTP"

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_server
msgid "SFTP Host"
msgstr "Host SFTP"

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_login
msgid "SFTP Login"
msgstr "Accesso SFTP"

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_password
msgid "SFTP Password"
msgstr "Password SFTP"

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_port
msgid "SFTP Port"
msgstr "Porta SFTP"

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_ssh_private_key
msgid "SSH private key"
msgstr "Chiave privata SSH"

#. module: storage_backend_sftp
#: model:ir.model,name:storage_backend_sftp.model_storage_backend
msgid "Storage Backend"
msgstr "Backend deposito"
82 changes: 82 additions & 0 deletions storage_backend_sftp/i18n/storage_backend_sftp.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * storage_backend_sftp
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__backend_type
msgid "Backend Type"
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model.fields,help:storage_backend_sftp.field_storage_backend__sftp_ssh_private_key
msgid ""
"It's recommended to not store the key here but to provide it via secret env "
"variable. See `server_environment` docs."
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model.fields,help:storage_backend_sftp.field_storage_backend__sftp_login
msgid "Login to connect to sftp server"
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model.fields.selection,name:storage_backend_sftp.selection__storage_backend__sftp_auth_method__pwd
msgid "Password"
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model.fields.selection,name:storage_backend_sftp.selection__storage_backend__sftp_auth_method__ssh_key
msgid "Private key"
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model.fields.selection,name:storage_backend_sftp.selection__storage_backend__backend_type__sftp
#: model_terms:ir.ui.view,arch_db:storage_backend_sftp.storage_backend_view_form
msgid "SFTP"
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_auth_method
msgid "SFTP Authentification Method"
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_server
msgid "SFTP Host"
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_login
msgid "SFTP Login"
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_password
msgid "SFTP Password"
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_port
msgid "SFTP Port"
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model.fields,field_description:storage_backend_sftp.field_storage_backend__sftp_ssh_private_key
msgid "SSH private key"
msgstr ""

#. module: storage_backend_sftp
#: model:ir.model,name:storage_backend_sftp.model_storage_backend
msgid "Storage Backend"
msgstr ""
1 change: 1 addition & 0 deletions storage_backend_sftp/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import storage_backend
Loading
Loading