Skip to content
Open
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
3907d0a
[REF] split sftp backend in a separated module
sebastienbeau Apr 11, 2018
31dbb52
[REF] rename method store and retrieve by more explicit method add/ge…
sebastienbeau Apr 11, 2018
9289c35
[REF] refactor test in order to use the same test between the differe…
sebastienbeau Apr 11, 2018
cfa071c
[IMP] add method for listing directory and deleting file on storage.b…
sebastienbeau Apr 13, 2018
2329bf9
[ADD] allow to connect SFTP using a ssh private key
Jun 21, 2018
190e2b7
[PEP8] fix pep8 issue
sebastienbeau Jul 31, 2018
377f582
[FIX] clean with pre-commit and pep 8
bguillot Apr 10, 2019
5d70620
[IMP] add tests and support pilbox for thumbnail
bguillot Apr 12, 2019
e5870b8
[12.0] storage*: Make installable False
rousseldenis Jun 7, 2019
ccdae66
Migrate to storage_backend_sftp to v12
florian-dacosta Jun 27, 2019
0f956f1
[ADD] icon.png
OCA-git-bot Sep 15, 2019
bc4bffe
pre-commit, black, isort
sbidoul Oct 1, 2019
ffc2a69
[UPD] Update storage_backend_sftp.pot
oca-travis Oct 18, 2019
112c9d7
storage_backend_sftp: refactor tests w/ mock
simahawk Oct 16, 2019
45e6108
13.0: Create branche
lmignon Oct 21, 2019
307c556
[UPD] Update storage_backend_sftp.pot
oca-travis Nov 4, 2019
76cf224
Fix runbot warning on clashing labels
simahawk Nov 22, 2019
31e30cc
Add server_env support
simahawk Nov 22, 2019
b2f242b
[UPD] Update storage_backend_sftp.pot
oca-travis Nov 25, 2019
6ce1f22
storage_backend_sftp 12.0.2.0.0
OCA-git-bot Nov 25, 2019
f918242
[REF] storage_backend_sftp: Black python code
simahawk Jan 17, 2020
ae0d243
[MIG] storage_backend_sftp: Migration to 13.0
simahawk Jan 17, 2020
8b9d429
[UPD] Update storage_backend_sftp.pot
oca-travis Jun 8, 2020
87988fe
storage_backend_sftp: add conn validation
simahawk Jun 19, 2020
b9fd9dd
storage_backend_sftp: fix SSH key auth
simahawk Jun 19, 2020
6b91e65
[UPD] Update storage_backend_sftp.pot
oca-travis Jul 2, 2020
c7a2fdc
storage_backend_sftp 13.0.1.1.0
OCA-git-bot Jul 2, 2020
d0854d1
storage_backend: run permission tests explicitely
simahawk Oct 29, 2020
38373e2
storage_backend_sftp: support adapter.move_files
simahawk Oct 29, 2020
a3c9b64
storage_backend|_sftp: add test for find_files
simahawk Oct 29, 2020
7a70291
storage_backend_sftp: add test for move_files
simahawk Oct 29, 2020
1be7e3f
storage_backend_sftp bump 13.0.1.2.0
simahawk Nov 23, 2020
a40b28e
storage_backend_sftp: use public api
simahawk Nov 24, 2020
4208a33
storage_backend_sftp 13.0.1.3.0
OCA-git-bot Nov 25, 2020
1150ba2
[ADD] add new V14 config
sebastienbeau Dec 6, 2020
113ba7a
[IMP] all: black, isort, prettier
sebastienbeau Dec 6, 2020
a40653e
[MIG] batch migration of modules
sebastienbeau Dec 6, 2020
308f8f5
storage_backend_sftp 14.0.1.0.1
OCA-git-bot Mar 1, 2021
504cff7
[UPD] Update storage_backend_sftp.pot
oca-travis Jun 9, 2021
c94f08f
[CHG] storage: Use more permissive licence: AGPL-> LGPL
etobella Mar 10, 2021
4f6a7dc
storage_backend_sftp 14.0.2.0.0
OCA-git-bot Aug 2, 2021
50dd050
[13.0][ADD] storage_backend_ftp
acsonefho Jul 10, 2021
6f8052b
[UPD] Update storage_backend_sftp.pot
oca-travis Nov 30, 2021
cbc1393
storage_backend_sftp 14.0.2.0.1
OCA-git-bot Nov 30, 2021
6e5400f
[MIG][15.0] storage_backend_sftp
i-vyshnevska Dec 5, 2021
d17ae6b
[FIX] storage_backend_sftp: use full path in move_files()
SilvioC2C Apr 6, 2022
b430329
[UPD] Update storage_backend_sftp.pot
Apr 11, 2022
6c8b2a6
storage_backend_sftp 15.0.1.0.1
OCA-git-bot Apr 11, 2022
5b63451
storage_backend_sftp: Fix paramiko usage
jcoux May 18, 2022
dd98b2c
storage_backend_sftp 15.0.1.0.2
OCA-git-bot May 25, 2022
d32f944
[MIG] storage_backend_sftp: Migration to 16.0
santostelmo Feb 24, 2023
4010aff
[UPD] Update storage_backend_sftp.pot
Feb 9, 2024
72b027a
Added translation using Weblate (Italian)
mymage Feb 13, 2024
c7ee144
Translated using Weblate (Italian)
mymage Feb 16, 2024
3cbd074
storage_*: fix test import of mock
simahawk Sep 4, 2024
d9bca49
[BOT] post-merge updates
OCA-git-bot Sep 4, 2024
d548473
[IMP] storage_backend_sftp: pre-commit auto fixes
alan196 Feb 26, 2025
336d209
[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