Commit 510513c1 authored by ibuler's avatar ibuler

Merge branch 'dev' of github.com:jumpserver/coco into dev

parents a2592688 61bae649
......@@ -376,6 +376,8 @@ defaults = {
'SFTP_SHOW_HIDDEN_FILE': False,
'UPLOAD_FAILED_REPLAY_ON_START': True,
'REUSE_CONNECTION': True,
'FORCE_REMOVE_FOLDER': False,
'TELNET_TTYPE': 'XTERM-256COLOR',
}
......
......@@ -300,25 +300,31 @@ class TelnetConnection:
logger.info(msg)
return None, msg
if data.startswith(telnetlib.IAC):
self.option_negotiate(data)
else:
result = self.login_auth(data)
if result:
msg = 'Successful asset connection.<{}>/<{}>/<{}>.'.format(
self.client.user, self.system_user.username,
self.asset.hostname
)
logger.info(msg)
return self.sock, None
elif result is False:
self.sock.close()
msg = 'Authentication failed.\r\n'
logger.info(msg)
return None, msg
elif result is None:
# 将数据以 \r\n 进行分割
_data_list = data.split(b'\r\n')
for _data in _data_list:
if not _data:
continue
if _data.startswith(telnetlib.IAC):
self.option_negotiate(_data)
else:
result = self.login_auth(_data)
if result:
msg = 'Successful asset connection.<{}>/<{}>/<{}>.'.format(
self.client.user, self.system_user.username,
self.asset.hostname
)
logger.info(msg)
return self.sock, None
elif result is False:
self.sock.close()
msg = 'Authentication failed.\r\n'
logger.info(msg)
return None, msg
elif result is None:
continue
def option_negotiate(self, data):
"""
Telnet server option negotiate before connection
......@@ -338,7 +344,8 @@ class TelnetConnection:
elif x == telnetlib.DO + telnetlib.TTYPE:
new_data_list.append(telnetlib.WILL + telnetlib.TTYPE)
elif x == telnetlib.SB + telnetlib.TTYPE + b'\x01':
new_data_list.append(telnetlib.SB + telnetlib.TTYPE + b'\x00' + b'XTERM-256COLOR')
terminal_type = bytes(config.TELNET_TTYPE, encoding='utf-8')
new_data_list.append(telnetlib.SB + telnetlib.TTYPE + b'\x00' + terminal_type)
elif telnetlib.DO in x:
new_data_list.append(x.replace(telnetlib.DO, telnetlib.WONT))
elif telnetlib.WILL in x:
......
......@@ -31,7 +31,7 @@ class ElFinderConnector:
_allowed_args = [
'cmd', 'target', 'targets[]', 'current', 'tree',
'name', 'content', 'src', 'dst', 'cut', 'init',
'type', 'width', 'height', 'upload[]', 'dirs[]',
'type', 'width', 'height', 'upload[]', 'upload_path[]', 'dirs[]',
'targets', "chunk", "range", "cid", 'reload',
]
......@@ -125,7 +125,7 @@ class ElFinderConnector:
# Copy allowed parameters from the given request's GET to self.data
for field in self._allowed_args:
if field in request_data:
if field in ["targets[]", "targets", "dirs[]"]:
if field in ["targets[]", "targets", "dirs[]", 'upload_path[]']:
self.data[field] = request_data.getlist(field)
else:
self.data[field] = request_data[field]
......@@ -270,20 +270,21 @@ class ElFinderConnector:
parent = self.data['target']
volume = self.get_volume(parent)
upload = self.data.get('upload[]')
upload_paths = self.data.get('upload_path[]')
if self.data.get('chunk') and self.data.get('cid'):
self.response.update(
volume.upload_as_chunk(
self.request.files, self.data.get('chunk'), parent
self.request.files, self.data.get('chunk'), parent, upload_paths
)
)
elif self.data.get('chunk'):
self.response.update(
volume.upload_chunk_merge(parent, self.data.get('chunk'))
volume.upload_chunk_merge(parent, self.data.get('chunk'), upload_paths),
)
elif isinstance(upload, str):
self.response.update(volume.upload_as_url(upload, parent))
self.response.update(volume.upload_as_url(upload, parent, upload))
else:
self.response.update(volume.upload(self.request.files, parent))
self.response.update(volume.upload(self.request.files, parent, upload_paths))
def __size(self):
target = self.data['targets[]']
......
......@@ -214,23 +214,25 @@ class BaseVolume:
"""
raise NotImplementedError
def upload(self, files, parent):
def upload(self, files, parent, upload_path):
""" Uploads one or more files in to the parent directory.
:param files: A list of uploaded file objects, as described here:
https://docs.djangoproject.com/en/dev/topics/http/file-uploads/
:param parent: The hash of the directory in which to create the
new files.
:param upload_path:
:returns: TODO
"""
raise NotImplementedError
def upload_as_chunk(self, files, chunk_name, parent):
def upload_as_chunk(self, files, chunk_name, parent, upload_path):
"""
Upload a large file as chunk
:param files:
:param chunk_name:
:param cid:
:param parent:
:param upload_path:
:return:
"""
......@@ -4,7 +4,7 @@ import stat
import threading
from flask import send_file
import requests
import os
from coco.utils import get_logger
from .base import BaseVolume
......@@ -222,10 +222,10 @@ class SFTPVolume(BaseVolume):
self.sftp.unlink(remote_path)
return target
def upload_as_url(self, url, parent):
def upload_as_url(self, url, parent, upload_path):
raise PermissionError("Not support upload from url")
def upload(self, files, parent):
def upload(self, files, parent, upload_path):
""" For now, this uses a very naive way of storing files - the entire
file is read in to the File model's content field in one go.
......@@ -234,20 +234,23 @@ class SFTPVolume(BaseVolume):
"""
added = []
parent_path = self._path(parent)
item = files.get('upload[]')
path = self._join(parent_path, item.filename)
remote_path = self._remote_path(path)
infos = self._list(parent_path)
files_exist = [d['name'] for d in infos]
if item.filename in files_exist:
raise OSError("File {} exits".format(remote_path))
with self.sftp.open(remote_path, 'w') as rf:
for data in item:
rf.write(data)
added.append(self._info(path))
for i, item in enumerate(files.getlist("upload[]")):
if upload_path and (parent != upload_path[i]) and (item.filename in upload_path[i]):
path = self._join(parent_path, upload_path[i].lstrip(self.path_sep))
else:
path = self._join(parent_path, item.filename)
remote_path = self._remote_path(path)
infos = self._list(os.path.dirname(path))
files_exist = [d['name'] for d in infos]
if item.filename in files_exist:
raise OSError("File {} exits".format(remote_path))
with self.sftp.open(remote_path, 'w') as rf:
for data in item:
rf.write(data)
added.append(self._info(path))
return {'added': added}
def upload_as_chunk(self, files, chunk_name, parent):
def upload_as_chunk(self, files, chunk_name, parent, upload_path):
added = []
parent_path = self._path(parent)
item = files.get('upload[]')
......@@ -255,11 +258,13 @@ class SFTPVolume(BaseVolume):
filename = '.'.join(__tmp[:-2])
num, total = __tmp[-2].split('_')
num, total = int(num), int(total)
path = self._join(parent_path, filename)
if len(upload_path) == 1 and (parent != upload_path[0]) and (filename in upload_path[0]):
path = self._join(parent_path, upload_path[0].lstrip(self.path_sep))
else:
path = self._join(parent_path, upload_path[0].lstrip(self.path_sep), filename)
remote_path = self._remote_path(path)
if num == 0:
infos = self._list(parent_path)
infos = self._list(os.path.dirname(path))
files_exist = [d['name'] for d in infos]
if item.filename in files_exist:
raise OSError("File {} exits".format(remote_path))
......@@ -271,9 +276,12 @@ class SFTPVolume(BaseVolume):
else:
return {'added': added, '_chunkmerged': filename, '_name': filename}
def upload_chunk_merge(self, parent, chunk):
def upload_chunk_merge(self, parent, chunk, upload_path):
parent_path = self._path(parent)
path = self._join(parent_path, chunk)
if len(upload_path) == 1 and (parent != upload_path[0]):
path = self._join(parent_path, upload_path[0].lstrip(self.path_sep))
else:
path = self._join(parent_path, chunk)
return {"added": [self._info(path)]}
def size(self, target):
......
......@@ -422,7 +422,7 @@
'minsLeft' : '剩余 $1 分钟', // from v2.1.17 added 13.11.2016
'openAsEncoding' : '使用所选编码重新打开', // from v2.1.19 added 2.12.2016
'saveAsEncoding' : '使用所选编码保存', // from v2.1.19 added 2.12.2016
'selectFolder' : '选择目录(暂不支持)', // from v2.1.20 added 13.12.2016
'selectFolder' : '选择目录', // from v2.1.20 added 13.12.2016
'firstLetterSearch': '首字母搜索', // from v2.1.23 added 24.3.2017
'presets' : '预置', // from v2.1.25 added 26.5.2017
'tooManyToTrash' : '项目太多,不能移动到回收站.', // from v2.1.25 added 9.6.2017
......
import os
import stat
import paramiko
import time
from datetime import datetime
......@@ -47,6 +48,7 @@ def convert_error(func):
if isinstance(error, Exception):
logger.error(error)
return response
return wrapper
......@@ -74,6 +76,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
self._sftp = {}
self.hosts = self.get_permed_hosts()
self.is_finished = False
self.force_rm_folder = config.FORCE_REMOVE_FOLDER
def get_user_assets(self):
user_id = self.server.connection.user.id
......@@ -256,7 +259,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
def stat(self, path):
request = self.parse_path(path)
host, su, dpath, unique = request['host'], request['su'], \
request['dpath'], request['su_unique']
request['dpath'], request['su_unique']
logger.debug("Stat path: {} => {}".format(path, request))
if not host or not su:
......@@ -392,12 +395,24 @@ class SFTPServer(paramiko.SFTPServerInterface):
success = False
try:
client.rmdir(rpath)
if self.force_rm_folder:
self._rmdir(client, rpath)
else:
client.rmdir(rpath)
success = True
return paramiko.SFTP_OK
finally:
self.create_ftp_log(path, "Rmdir", success)
def _rmdir(self, sftp_client, path):
for item in list(sftp_client.listdir_iter(path)):
filepath = "/".join([path, item.filename])
if stat.S_IFMT(item.st_mode) == stat.S_IFDIR:
self._rmdir(sftp_client, filepath)
continue
sftp_client.remove(filepath)
sftp_client.rmdir(path)
class FakeServer:
pass
......
......@@ -60,3 +60,9 @@ BOOTSTRAP_TOKEN: <PleasgeChangeSameWithJumpserver>
# 是否复用和用户后端资产已建立的连接(用户不会复用其他用户的连接)
# REUSE_CONNECTION: true
# 是否强制删除文件夹:(default false)
# FORCE_REMOVE_FOLDER: false
# Telnet连接协商使用的终端类型
# TELNET_TTYPE: XTERM-256COLOR
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment