mirror of
https://github.com/OCA/knowledge.git
synced 2025-07-26 10:28:40 -06:00
[ADD] OCA codesprint fixes
- a few bugfixes, including the grave one that everyone read with admin permissions - write/create attachment support
This commit is contained in:
parent
a57aadd3aa
commit
7ccb6914b2
@ -1,20 +1,44 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# © 2016 Therp BV <http://therp.nl>
|
# © 2016 Therp BV <http://therp.nl>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
from paramiko import SFTP_EOF, SFTPHandle
|
from paramiko import SFTP_EOF, SFTP_OK, SFTP_PERMISSION_DENIED, SFTPHandle
|
||||||
from base64 import b64decode
|
from base64 import b64decode, b64encode
|
||||||
|
from openerp.models import NewId
|
||||||
|
from openerp.exceptions import AccessError
|
||||||
|
|
||||||
|
|
||||||
class DocumentSFTPHandle(SFTPHandle):
|
class DocumentSFTPHandle(SFTPHandle):
|
||||||
def __init__(self, attachment, flags=0):
|
def __init__(self, record, flags=0):
|
||||||
self.attachment = attachment
|
self.record = record
|
||||||
super(DocumentSFTPHandle, self).__init__(flags)
|
super(DocumentSFTPHandle, self).__init__(flags)
|
||||||
|
|
||||||
def stat(self):
|
def stat(self):
|
||||||
return self.attachment.env['document.sftp.root']._file(self.attachment)
|
return self.record.env['document.sftp.root']._file(self.record)
|
||||||
|
|
||||||
def read(self, offset, length):
|
def read(self, offset, length):
|
||||||
data = b64decode(self.attachment.datas)
|
data = b64decode(self.record.datas)
|
||||||
if offset > len(data):
|
if offset > len(data):
|
||||||
return SFTP_EOF
|
return SFTP_EOF
|
||||||
return data[offset:offset + length]
|
return data[offset:offset + length]
|
||||||
|
|
||||||
|
def write(self, offset, write_data):
|
||||||
|
data = b64decode(self.record.datas) if self.record.datas else ''
|
||||||
|
if offset > len(data):
|
||||||
|
return SFTP_EOF
|
||||||
|
try:
|
||||||
|
self.record.update({
|
||||||
|
'datas': b64encode(
|
||||||
|
data[0:offset] + write_data +
|
||||||
|
data[offset + len(write_data):]
|
||||||
|
),
|
||||||
|
})
|
||||||
|
if isinstance(self.record.id, NewId):
|
||||||
|
self.record.create(self.record._cache)
|
||||||
|
except AccessError:
|
||||||
|
return SFTP_PERMISSION_DENIED
|
||||||
|
|
||||||
|
# we need this commit, because this runs in its own thread with its own
|
||||||
|
# cursor
|
||||||
|
self.record.env.cr.commit()
|
||||||
|
# TODO: do we want to clear caches here?
|
||||||
|
return SFTP_OK
|
||||||
|
@ -19,6 +19,7 @@ class DocumentSFTPServer(ServerInterface):
|
|||||||
if not user:
|
if not user:
|
||||||
return AUTH_FAILED
|
return AUTH_FAILED
|
||||||
user.sudo(user.id).check_credentials(password)
|
user.sudo(user.id).check_credentials(password)
|
||||||
|
self.env = self.env(user=user.id)
|
||||||
return AUTH_SUCCESSFUL
|
return AUTH_SUCCESSFUL
|
||||||
except AccessDenied:
|
except AccessDenied:
|
||||||
pass
|
pass
|
||||||
@ -37,6 +38,7 @@ class DocumentSFTPServer(ServerInterface):
|
|||||||
'Ignoring key of unknown type for line %s', line)
|
'Ignoring key of unknown type for line %s', line)
|
||||||
continue
|
continue
|
||||||
if RSAKey(data=decodebytes(key_data)) == key:
|
if RSAKey(data=decodebytes(key_data)) == key:
|
||||||
|
self.env = self.env(user=user.id)
|
||||||
return AUTH_SUCCESSFUL
|
return AUTH_SUCCESSFUL
|
||||||
return AUTH_FAILED
|
return AUTH_FAILED
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ from openerp import api
|
|||||||
|
|
||||||
class DocumentSFTPSftpServerInterface(SFTPServerInterface):
|
class DocumentSFTPSftpServerInterface(SFTPServerInterface):
|
||||||
def __init__(self, server, env):
|
def __init__(self, server, env):
|
||||||
self.env = env
|
self.env = server.env
|
||||||
|
|
||||||
def list_folder(self, path):
|
def list_folder(self, path):
|
||||||
if not path or path in ('/', '.'):
|
if not path or path in ('/', '.'):
|
||||||
@ -38,6 +38,7 @@ class DocumentSFTPSftpServerInterface(SFTPServerInterface):
|
|||||||
return handler._open(path, flags, attr)
|
return handler._open(path, flags, attr)
|
||||||
|
|
||||||
def session_ended(self):
|
def session_ended(self):
|
||||||
|
self.env.cr.commit()
|
||||||
self.env.cr.close()
|
self.env.cr.close()
|
||||||
return super(DocumentSFTPSftpServerInterface, self).session_ended()
|
return super(DocumentSFTPSftpServerInterface, self).session_ended()
|
||||||
|
|
||||||
|
@ -2,12 +2,13 @@
|
|||||||
# © 2016 Therp BV <http://therp.nl>
|
# © 2016 Therp BV <http://therp.nl>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
import stat
|
import stat
|
||||||
|
import time
|
||||||
try:
|
try:
|
||||||
from paramiko import SFTPAttributes
|
from paramiko import SFTPAttributes
|
||||||
from ..document_sftp_handle import DocumentSFTPHandle
|
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
from openerp import api, models
|
from ..document_sftp_handle import DocumentSFTPHandle
|
||||||
|
from openerp import api, models, fields
|
||||||
|
|
||||||
|
|
||||||
class DocumentSFTPRoot(models.AbstractModel):
|
class DocumentSFTPRoot(models.AbstractModel):
|
||||||
@ -36,6 +37,9 @@ class DocumentSFTPRoot(models.AbstractModel):
|
|||||||
result.st_group = 0
|
result.st_group = 0
|
||||||
result.st_size = attachment.file_size
|
result.st_size = attachment.file_size
|
||||||
result.st_mode = stat.S_IFREG | stat.S_IRUSR
|
result.st_mode = stat.S_IFREG | stat.S_IRUSR
|
||||||
|
result.st_mtime = time.mktime(fields.Datetime.from_string(
|
||||||
|
attachment.create_date
|
||||||
|
).timetuple())
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
@ -62,3 +66,10 @@ class DocumentSFTPRoot(models.AbstractModel):
|
|||||||
def _lstat(self, path):
|
def _lstat(self, path):
|
||||||
"""Return attributes about a link"""
|
"""Return attributes about a link"""
|
||||||
return self._stat(path)
|
return self._stat(path)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _split_path(self, path):
|
||||||
|
"""Return a list of normalized and stripped path components"""
|
||||||
|
# TODO: normalization
|
||||||
|
path = path.strip('/')
|
||||||
|
return path.split('/')
|
||||||
|
@ -41,8 +41,7 @@ class DocumentSFTPRootByModel(models.Model):
|
|||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _list_folder(self, path):
|
def _list_folder(self, path):
|
||||||
path = path.strip('/')
|
components = self._split_path(path)
|
||||||
components = path.split('/')
|
|
||||||
result = []
|
result = []
|
||||||
if len(components) == 1:
|
if len(components) == 1:
|
||||||
for model in self.env['ir.model'].search([
|
for model in self.env['ir.model'].search([
|
||||||
@ -55,17 +54,11 @@ class DocumentSFTPRootByModel(models.Model):
|
|||||||
result.append(self._directory(model.model))
|
result.append(self._directory(model.model))
|
||||||
elif len(components) == 2:
|
elif len(components) == 2:
|
||||||
model = components[-1]
|
model = components[-1]
|
||||||
seen = set([])
|
|
||||||
if model not in self.env.registry:
|
if model not in self.env.registry:
|
||||||
return SFTP_NO_SUCH_FILE
|
return SFTP_NO_SUCH_FILE
|
||||||
for attachment in self.env['ir.attachment'].search([
|
for record in self.env[model].search([], order='id asc'):
|
||||||
('res_model', '=', model),
|
|
||||||
('res_id', '!=', False),
|
|
||||||
], order='res_id asc'):
|
|
||||||
# TODO: better lump ids together in steps of 100 or something?
|
# TODO: better lump ids together in steps of 100 or something?
|
||||||
if attachment.res_id not in seen:
|
result.append(self._directory(str(record.id)))
|
||||||
seen.add(attachment.res_id)
|
|
||||||
result.append(self._directory(str(attachment.res_id)))
|
|
||||||
elif len(components) == 3:
|
elif len(components) == 3:
|
||||||
model = components[-2]
|
model = components[-2]
|
||||||
res_id = int(components[-1])
|
res_id = int(components[-1])
|
||||||
@ -81,10 +74,32 @@ class DocumentSFTPRootByModel(models.Model):
|
|||||||
@api.model
|
@api.model
|
||||||
def _open(self, path, flags, attr):
|
def _open(self, path, flags, attr):
|
||||||
if flags & os.O_WRONLY or flags & os.O_RDWR:
|
if flags & os.O_WRONLY or flags & os.O_RDWR:
|
||||||
# TODO: do something more sensible here
|
return self._open_write(path, flags, attr)
|
||||||
return SFTP_PERMISSION_DENIED
|
return self._open_read(path, flags, attr)
|
||||||
path = path.strip('/')
|
|
||||||
components = path.split('/')
|
@api.model
|
||||||
|
def _open_write(self, path, flags, attr):
|
||||||
|
components = self._split_path(path)
|
||||||
|
if len(components) == 4:
|
||||||
|
# TODO: locking!
|
||||||
|
existing = self.env['ir.attachment'].search([
|
||||||
|
('res_model', '=', components[-3]),
|
||||||
|
('res_id', '=', int(components[-2])),
|
||||||
|
('datas_fname', '=', components[-1]),
|
||||||
|
])
|
||||||
|
return self._file_handle(
|
||||||
|
existing or self.env['ir.attachment'].new({
|
||||||
|
'res_model': components[-3],
|
||||||
|
'res_id': int(components[-2]),
|
||||||
|
'datas_fname': components[-1],
|
||||||
|
'name': components[-1],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return SFTP_PERMISSION_DENIED
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _open_read(self, path, flags, attr):
|
||||||
|
components = self._split_path(path)
|
||||||
if len(components) == 4:
|
if len(components) == 4:
|
||||||
return self._file_handle(self.env['ir.attachment'].search([
|
return self._file_handle(self.env['ir.attachment'].search([
|
||||||
('res_model', '=', components[-3]),
|
('res_model', '=', components[-3]),
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//footer" position="before">
|
<xpath expr="//footer" position="before">
|
||||||
<group name="document_sftp" string="SSH">
|
<group name="document_sftp" string="SSH">
|
||||||
<field name="authorized_keys" />
|
<field name="authorized_keys" readonly="False" />
|
||||||
</group>
|
</group>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
|
Loading…
Reference in New Issue
Block a user