Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0][MIG] storage_backend_sftp: Migration to 16.0 #241

Merged
merged 51 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
927848a
[REF] split sftp backend in a separated module
sebastienbeau Apr 11, 2018
447d5bf
[REF] rename method store and retrieve by more explicit method add/ge…
sebastienbeau Apr 11, 2018
4243a43
[REF] refactor test in order to use the same test between the differe…
sebastienbeau Apr 11, 2018
e001407
[IMP] add method for listing directory and deleting file on storage.b…
sebastienbeau Apr 13, 2018
6afbb92
[ADD] allow to connect SFTP using a ssh private key
Jun 21, 2018
c82e25c
[PEP8] fix pep8 issue
sebastienbeau Jul 31, 2018
b2acbb5
[FIX] clean with pre-commit and pep 8
bguillot Apr 10, 2019
b0019a9
[IMP] add tests and support pilbox for thumbnail
bguillot Apr 12, 2019
da43970
[12.0] storage*: Make installable False
rousseldenis Jun 7, 2019
ffb487f
Migrate to storage_backend_sftp to v12
florian-dacosta Jun 27, 2019
2085347
[ADD] icon.png
OCA-git-bot Sep 15, 2019
16fe2a3
pre-commit, black, isort
sbidoul Oct 1, 2019
f5650ee
[UPD] Update storage_backend_sftp.pot
oca-travis Oct 18, 2019
fbb0647
storage_backend_sftp: refactor tests w/ mock
simahawk Oct 16, 2019
d7f108e
13.0: Create branche
lmignon Oct 21, 2019
fc91c9c
[UPD] Update storage_backend_sftp.pot
oca-travis Nov 4, 2019
3624dbd
Fix runbot warning on clashing labels
simahawk Nov 22, 2019
7693140
Add server_env support
simahawk Nov 22, 2019
8b6d99b
[UPD] Update storage_backend_sftp.pot
oca-travis Nov 25, 2019
47e6900
storage_backend_sftp 12.0.2.0.0
OCA-git-bot Nov 25, 2019
483cd61
[REF] storage_backend_sftp: Black python code
simahawk Jan 17, 2020
f14ee06
[MIG] storage_backend_sftp: Migration to 13.0
simahawk Jan 17, 2020
25ba7a3
[UPD] Update storage_backend_sftp.pot
oca-travis Jun 8, 2020
3ce4ce1
storage_backend_sftp: add conn validation
simahawk Jun 19, 2020
7a1324c
storage_backend_sftp: fix SSH key auth
simahawk Jun 19, 2020
151e293
[UPD] Update storage_backend_sftp.pot
oca-travis Jul 2, 2020
a14afd6
storage_backend_sftp 13.0.1.1.0
OCA-git-bot Jul 2, 2020
a5329ee
storage_backend: run permission tests explicitely
simahawk Oct 29, 2020
7ab1adf
storage_backend_sftp: support adapter.move_files
simahawk Oct 29, 2020
65ccbd3
storage_backend|_sftp: add test for find_files
simahawk Oct 29, 2020
f626cb3
storage_backend_sftp: add test for move_files
simahawk Oct 29, 2020
f2e32a3
storage_backend_sftp bump 13.0.1.2.0
simahawk Nov 23, 2020
bd56100
storage_backend_sftp: use public api
simahawk Nov 24, 2020
7dbb092
storage_backend_sftp 13.0.1.3.0
OCA-git-bot Nov 25, 2020
c10571d
[ADD] add new V14 config
sebastienbeau Dec 6, 2020
b677a26
[IMP] all: black, isort, prettier
sebastienbeau Dec 6, 2020
000428f
[MIG] batch migration of modules
sebastienbeau Dec 6, 2020
7fd32f6
storage_backend_sftp 14.0.1.0.1
OCA-git-bot Mar 1, 2021
882cd94
[UPD] Update storage_backend_sftp.pot
oca-travis Jun 9, 2021
9e9c4e0
[CHG] storage: Use more permissive licence: AGPL-> LGPL
etobella Mar 10, 2021
0f4989e
storage_backend_sftp 14.0.2.0.0
OCA-git-bot Aug 2, 2021
626c2ca
[13.0][ADD] storage_backend_ftp
acsonefho Jul 10, 2021
3d1c0ec
[UPD] Update storage_backend_sftp.pot
oca-travis Nov 30, 2021
21e59e2
storage_backend_sftp 14.0.2.0.1
OCA-git-bot Nov 30, 2021
5591ec3
[MIG][15.0] storage_backend_sftp
i-vyshnevska Dec 5, 2021
9c9c7ae
[FIX] storage_backend_sftp: use full path in move_files()
SilvioC2C Apr 6, 2022
9a5f710
[UPD] Update storage_backend_sftp.pot
Apr 11, 2022
2ad314e
storage_backend_sftp 15.0.1.0.1
OCA-git-bot Apr 11, 2022
d87f35b
storage_backend_sftp: Fix paramiko usage
jcoux May 18, 2022
f674b85
storage_backend_sftp 15.0.1.0.2
OCA-git-bot May 25, 2022
10ac638
[MIG] storage_backend_sftp: Migration to 16.0
santostelmo Feb 24, 2023
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
paramiko
python_slugify
6 changes: 6 additions & 0 deletions setup/storage_backend_sftp/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
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 <[email protected]>
* Raphaël Reverdy <[email protected]>
* Cédric Pigeon <[email protected]>
* Simone Orsi <[email protected]>


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 <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

{
"name": "Storage Backend SFTP",
"summary": "Implement SFTP Storage",
"version": "16.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 <[email protected]>
# Copyright 2019 Camptocamp SA (http://www.camptocamp.com).
# Copyright 2020 ACSONE SA/NV (<http://acsone.eu>)
# @author Simone Orsi <[email protected]>
# 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 IOError as e:
if e.errno == errno.ENOENT and path:
sftp_mkdirs(client, os.path.dirname(path), mode=mode)
client.mkdir(path, mode)

Check warning on line 29 in storage_backend_sftp/components/sftp_adapter.py

View check run for this annotation

Codecov / codecov/patch

storage_backend_sftp/components/sftp_adapter.py#L24-L29

Added lines #L24 - L29 were not covered by tests
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")

Check warning on line 45 in storage_backend_sftp/components/sftp_adapter.py

View check run for this annotation

Codecov / codecov/patch

storage_backend_sftp/components/sftp_adapter.py#L41-L45

Added lines #L41 - L45 were not covered by tests


@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)

Check warning on line 52 in storage_backend_sftp/components/sftp_adapter.py

View check run for this annotation

Codecov / codecov/patch

storage_backend_sftp/components/sftp_adapter.py#L52

Added line #L52 was not covered by tests
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)

Check warning on line 56 in storage_backend_sftp/components/sftp_adapter.py

View check run for this annotation

Codecov / codecov/patch

storage_backend_sftp/components/sftp_adapter.py#L54-L56

Added lines #L54 - L56 were not covered by tests
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 IOError 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 IOError 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)

Check warning on line 123 in storage_backend_sftp/components/sftp_adapter.py

View check run for this annotation

Codecov / codecov/patch

storage_backend_sftp/components/sftp_adapter.py#L123

Added line #L123 was not covered by tests
with sftp(self.collection) as client:
return client.remove(full_path)

Check warning on line 125 in storage_backend_sftp/components/sftp_adapter.py

View check run for this annotation

Codecov / codecov/patch

storage_backend_sftp/components/sftp_adapter.py#L125

Added line #L125 was not covered by tests

def validate_config(self):
with sftp(self.collection) as client:
client.listdir()

Check warning on line 129 in storage_backend_sftp/components/sftp_adapter.py

View check run for this annotation

Codecov / codecov/patch

storage_backend_sftp/components/sftp_adapter.py#L129

Added line #L129 was not covered by tests
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 15.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
48 changes: 48 additions & 0 deletions storage_backend_sftp/models/storage_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright 2017 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <[email protected]>
# Copyright 2019 Camptocamp SA (http://www.camptocamp.com).
# @author Simone Orsi <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from odoo import fields, models


class StorageBackend(models.Model):
_inherit = "storage.backend"

backend_type = fields.Selection(
selection_add=[("sftp", "SFTP")], ondelete={"sftp": "set default"}
)
sftp_server = fields.Char(string="SFTP Host")
sftp_port = fields.Integer(string="SFTP Port", default=22)
sftp_auth_method = fields.Selection(
string="SFTP Authentification Method",
selection=[("pwd", "Password"), ("ssh_key", "Private key")],
default="pwd",
required=True,
)
sftp_login = fields.Char(
string="SFTP Login", help="Login to connect to sftp server"
)
sftp_password = fields.Char(string="SFTP Password")
sftp_ssh_private_key = fields.Text(
string="SSH private key",
help="It's recommended to not store the key here "
"but to provide it via secret env variable. "
"See `server_environment` docs.",
)

@property
def _server_env_fields(self):
env_fields = super()._server_env_fields
env_fields.update(
{
"sftp_password": {},
"sftp_login": {},
"sftp_server": {},
"sftp_port": {},
"sftp_auth_method": {},
"sftp_ssh_private_key": {},
}
)
return env_fields
Binary file added storage_backend_sftp/static/description/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions storage_backend_sftp/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_sftp
Loading
Loading