Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
C
coco
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
coco
Commits
510513c1
Commit
510513c1
authored
Aug 08, 2019
by
ibuler
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev' of github.com:jumpserver/coco into dev
parents
a2592688
61bae649
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
90 additions
and
49 deletions
+90
-49
conf.py
coco/conf.py
+2
-0
connection.py
coco/connection.py
+25
-18
connector.py
coco/httpd/elfinder/connector.py
+7
-6
base.py
coco/httpd/elfinder/volumes/base.py
+4
-2
sftp.py
coco/httpd/elfinder/volumes/sftp.py
+28
-20
elfinder.zh_CN.js
coco/httpd/static/plugins/elfinder/i18n/elfinder.zh_CN.js
+1
-1
sftp.py
coco/sftp.py
+17
-2
config_example.yml
config_example.yml
+6
-0
No files found.
coco/conf.py
View file @
510513c1
...
@@ -376,6 +376,8 @@ defaults = {
...
@@ -376,6 +376,8 @@ defaults = {
'SFTP_SHOW_HIDDEN_FILE'
:
False
,
'SFTP_SHOW_HIDDEN_FILE'
:
False
,
'UPLOAD_FAILED_REPLAY_ON_START'
:
True
,
'UPLOAD_FAILED_REPLAY_ON_START'
:
True
,
'REUSE_CONNECTION'
:
True
,
'REUSE_CONNECTION'
:
True
,
'FORCE_REMOVE_FOLDER'
:
False
,
'TELNET_TTYPE'
:
'XTERM-256COLOR'
,
}
}
...
...
coco/connection.py
View file @
510513c1
...
@@ -300,25 +300,31 @@ class TelnetConnection:
...
@@ -300,25 +300,31 @@ class TelnetConnection:
logger
.
info
(
msg
)
logger
.
info
(
msg
)
return
None
,
msg
return
None
,
msg
if
data
.
startswith
(
telnetlib
.
IAC
):
# 将数据以 \r\n 进行分割
self
.
option_negotiate
(
data
)
_data_list
=
data
.
split
(
b
'
\r\n
'
)
else
:
for
_data
in
_data_list
:
result
=
self
.
login_auth
(
data
)
if
not
_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
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
):
def
option_negotiate
(
self
,
data
):
"""
"""
Telnet server option negotiate before connection
Telnet server option negotiate before connection
...
@@ -338,7 +344,8 @@ class TelnetConnection:
...
@@ -338,7 +344,8 @@ class TelnetConnection:
elif
x
==
telnetlib
.
DO
+
telnetlib
.
TTYPE
:
elif
x
==
telnetlib
.
DO
+
telnetlib
.
TTYPE
:
new_data_list
.
append
(
telnetlib
.
WILL
+
telnetlib
.
TTYPE
)
new_data_list
.
append
(
telnetlib
.
WILL
+
telnetlib
.
TTYPE
)
elif
x
==
telnetlib
.
SB
+
telnetlib
.
TTYPE
+
b
'
\x01
'
:
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
:
elif
telnetlib
.
DO
in
x
:
new_data_list
.
append
(
x
.
replace
(
telnetlib
.
DO
,
telnetlib
.
WONT
))
new_data_list
.
append
(
x
.
replace
(
telnetlib
.
DO
,
telnetlib
.
WONT
))
elif
telnetlib
.
WILL
in
x
:
elif
telnetlib
.
WILL
in
x
:
...
...
coco/httpd/elfinder/connector.py
View file @
510513c1
...
@@ -31,7 +31,7 @@ class ElFinderConnector:
...
@@ -31,7 +31,7 @@ class ElFinderConnector:
_allowed_args
=
[
_allowed_args
=
[
'cmd'
,
'target'
,
'targets[]'
,
'current'
,
'tree'
,
'cmd'
,
'target'
,
'targets[]'
,
'current'
,
'tree'
,
'name'
,
'content'
,
'src'
,
'dst'
,
'cut'
,
'init'
,
'name'
,
'content'
,
'src'
,
'dst'
,
'cut'
,
'init'
,
'type'
,
'width'
,
'height'
,
'upload[]'
,
'dirs[]'
,
'type'
,
'width'
,
'height'
,
'upload[]'
,
'
upload_path[]'
,
'
dirs[]'
,
'targets'
,
"chunk"
,
"range"
,
"cid"
,
'reload'
,
'targets'
,
"chunk"
,
"range"
,
"cid"
,
'reload'
,
]
]
...
@@ -125,7 +125,7 @@ class ElFinderConnector:
...
@@ -125,7 +125,7 @@ class ElFinderConnector:
# Copy allowed parameters from the given request's GET to self.data
# Copy allowed parameters from the given request's GET to self.data
for
field
in
self
.
_allowed_args
:
for
field
in
self
.
_allowed_args
:
if
field
in
request_data
:
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
)
self
.
data
[
field
]
=
request_data
.
getlist
(
field
)
else
:
else
:
self
.
data
[
field
]
=
request_data
[
field
]
self
.
data
[
field
]
=
request_data
[
field
]
...
@@ -270,20 +270,21 @@ class ElFinderConnector:
...
@@ -270,20 +270,21 @@ class ElFinderConnector:
parent
=
self
.
data
[
'target'
]
parent
=
self
.
data
[
'target'
]
volume
=
self
.
get_volume
(
parent
)
volume
=
self
.
get_volume
(
parent
)
upload
=
self
.
data
.
get
(
'upload[]'
)
upload
=
self
.
data
.
get
(
'upload[]'
)
upload_paths
=
self
.
data
.
get
(
'upload_path[]'
)
if
self
.
data
.
get
(
'chunk'
)
and
self
.
data
.
get
(
'cid'
):
if
self
.
data
.
get
(
'chunk'
)
and
self
.
data
.
get
(
'cid'
):
self
.
response
.
update
(
self
.
response
.
update
(
volume
.
upload_as_chunk
(
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'
):
elif
self
.
data
.
get
(
'chunk'
):
self
.
response
.
update
(
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
):
elif
isinstance
(
upload
,
str
):
self
.
response
.
update
(
volume
.
upload_as_url
(
upload
,
parent
))
self
.
response
.
update
(
volume
.
upload_as_url
(
upload
,
parent
,
upload
))
else
:
else
:
self
.
response
.
update
(
volume
.
upload
(
self
.
request
.
files
,
parent
))
self
.
response
.
update
(
volume
.
upload
(
self
.
request
.
files
,
parent
,
upload_paths
))
def
__size
(
self
):
def
__size
(
self
):
target
=
self
.
data
[
'targets[]'
]
target
=
self
.
data
[
'targets[]'
]
...
...
coco/httpd/elfinder/volumes/base.py
View file @
510513c1
...
@@ -214,23 +214,25 @@ class BaseVolume:
...
@@ -214,23 +214,25 @@ class BaseVolume:
"""
"""
raise
NotImplementedError
raise
NotImplementedError
def
upload
(
self
,
files
,
parent
):
def
upload
(
self
,
files
,
parent
,
upload_path
):
""" Uploads one or more files in to the parent directory.
""" Uploads one or more files in to the parent directory.
:param files: A list of uploaded file objects, as described here:
:param files: A list of uploaded file objects, as described here:
https://docs.djangoproject.com/en/dev/topics/http/file-uploads/
https://docs.djangoproject.com/en/dev/topics/http/file-uploads/
:param parent: The hash of the directory in which to create the
:param parent: The hash of the directory in which to create the
new files.
new files.
:param upload_path:
:returns: TODO
:returns: TODO
"""
"""
raise
NotImplementedError
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
Upload a large file as chunk
:param files:
:param files:
:param chunk_name:
:param chunk_name:
:param cid:
:param cid:
:param parent:
:param parent:
:param upload_path:
:return:
:return:
"""
"""
coco/httpd/elfinder/volumes/sftp.py
View file @
510513c1
...
@@ -4,7 +4,7 @@ import stat
...
@@ -4,7 +4,7 @@ import stat
import
threading
import
threading
from
flask
import
send_file
from
flask
import
send_file
import
request
s
import
o
s
from
coco.utils
import
get_logger
from
coco.utils
import
get_logger
from
.base
import
BaseVolume
from
.base
import
BaseVolume
...
@@ -222,10 +222,10 @@ class SFTPVolume(BaseVolume):
...
@@ -222,10 +222,10 @@ class SFTPVolume(BaseVolume):
self
.
sftp
.
unlink
(
remote_path
)
self
.
sftp
.
unlink
(
remote_path
)
return
target
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"
)
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
""" 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.
file is read in to the File model's content field in one go.
...
@@ -234,20 +234,23 @@ class SFTPVolume(BaseVolume):
...
@@ -234,20 +234,23 @@ class SFTPVolume(BaseVolume):
"""
"""
added
=
[]
added
=
[]
parent_path
=
self
.
_path
(
parent
)
parent_path
=
self
.
_path
(
parent
)
item
=
files
.
get
(
'upload[]'
)
for
i
,
item
in
enumerate
(
files
.
getlist
(
"upload[]"
)):
path
=
self
.
_join
(
parent_path
,
item
.
filename
)
if
upload_path
and
(
parent
!=
upload_path
[
i
])
and
(
item
.
filename
in
upload_path
[
i
]):
remote_path
=
self
.
_remote_path
(
path
)
path
=
self
.
_join
(
parent_path
,
upload_path
[
i
]
.
lstrip
(
self
.
path_sep
))
infos
=
self
.
_list
(
parent_path
)
else
:
files_exist
=
[
d
[
'name'
]
for
d
in
infos
]
path
=
self
.
_join
(
parent_path
,
item
.
filename
)
if
item
.
filename
in
files_exist
:
remote_path
=
self
.
_remote_path
(
path
)
raise
OSError
(
"File {} exits"
.
format
(
remote_path
))
infos
=
self
.
_list
(
os
.
path
.
dirname
(
path
))
with
self
.
sftp
.
open
(
remote_path
,
'w'
)
as
rf
:
files_exist
=
[
d
[
'name'
]
for
d
in
infos
]
for
data
in
item
:
if
item
.
filename
in
files_exist
:
rf
.
write
(
data
)
raise
OSError
(
"File {} exits"
.
format
(
remote_path
))
added
.
append
(
self
.
_info
(
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
}
return
{
'added'
:
added
}
def
upload_as_chunk
(
self
,
files
,
chunk_name
,
parent
):
def
upload_as_chunk
(
self
,
files
,
chunk_name
,
parent
,
upload_path
):
added
=
[]
added
=
[]
parent_path
=
self
.
_path
(
parent
)
parent_path
=
self
.
_path
(
parent
)
item
=
files
.
get
(
'upload[]'
)
item
=
files
.
get
(
'upload[]'
)
...
@@ -255,11 +258,13 @@ class SFTPVolume(BaseVolume):
...
@@ -255,11 +258,13 @@ class SFTPVolume(BaseVolume):
filename
=
'.'
.
join
(
__tmp
[:
-
2
])
filename
=
'.'
.
join
(
__tmp
[:
-
2
])
num
,
total
=
__tmp
[
-
2
]
.
split
(
'_'
)
num
,
total
=
__tmp
[
-
2
]
.
split
(
'_'
)
num
,
total
=
int
(
num
),
int
(
total
)
num
,
total
=
int
(
num
),
int
(
total
)
if
len
(
upload_path
)
==
1
and
(
parent
!=
upload_path
[
0
])
and
(
filename
in
upload_path
[
0
]):
path
=
self
.
_join
(
parent_path
,
filename
)
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
)
remote_path
=
self
.
_remote_path
(
path
)
if
num
==
0
:
if
num
==
0
:
infos
=
self
.
_list
(
parent_path
)
infos
=
self
.
_list
(
os
.
path
.
dirname
(
path
)
)
files_exist
=
[
d
[
'name'
]
for
d
in
infos
]
files_exist
=
[
d
[
'name'
]
for
d
in
infos
]
if
item
.
filename
in
files_exist
:
if
item
.
filename
in
files_exist
:
raise
OSError
(
"File {} exits"
.
format
(
remote_path
))
raise
OSError
(
"File {} exits"
.
format
(
remote_path
))
...
@@ -271,9 +276,12 @@ class SFTPVolume(BaseVolume):
...
@@ -271,9 +276,12 @@ class SFTPVolume(BaseVolume):
else
:
else
:
return
{
'added'
:
added
,
'_chunkmerged'
:
filename
,
'_name'
:
filename
}
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
)
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
)]}
return
{
"added"
:
[
self
.
_info
(
path
)]}
def
size
(
self
,
target
):
def
size
(
self
,
target
):
...
...
coco/httpd/static/plugins/elfinder/i18n/elfinder.zh_CN.js
View file @
510513c1
...
@@ -422,7 +422,7 @@
...
@@ -422,7 +422,7 @@
'minsLeft'
:
'剩余 $1 分钟'
,
// from v2.1.17 added 13.11.2016
'minsLeft'
:
'剩余 $1 分钟'
,
// from v2.1.17 added 13.11.2016
'openAsEncoding'
:
'使用所选编码重新打开'
,
// from v2.1.19 added 2.12.2016
'openAsEncoding'
:
'使用所选编码重新打开'
,
// from v2.1.19 added 2.12.2016
'saveAsEncoding'
:
'使用所选编码保存'
,
// 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
'firstLetterSearch'
:
'首字母搜索'
,
// from v2.1.23 added 24.3.2017
'presets'
:
'预置'
,
// from v2.1.25 added 26.5.2017
'presets'
:
'预置'
,
// from v2.1.25 added 26.5.2017
'tooManyToTrash'
:
'项目太多,不能移动到回收站.'
,
// from v2.1.25 added 9.6.2017
'tooManyToTrash'
:
'项目太多,不能移动到回收站.'
,
// from v2.1.25 added 9.6.2017
...
...
coco/sftp.py
View file @
510513c1
import
os
import
os
import
stat
import
paramiko
import
paramiko
import
time
import
time
from
datetime
import
datetime
from
datetime
import
datetime
...
@@ -47,6 +48,7 @@ def convert_error(func):
...
@@ -47,6 +48,7 @@ def convert_error(func):
if
isinstance
(
error
,
Exception
):
if
isinstance
(
error
,
Exception
):
logger
.
error
(
error
)
logger
.
error
(
error
)
return
response
return
response
return
wrapper
return
wrapper
...
@@ -74,6 +76,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
...
@@ -74,6 +76,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
self
.
_sftp
=
{}
self
.
_sftp
=
{}
self
.
hosts
=
self
.
get_permed_hosts
()
self
.
hosts
=
self
.
get_permed_hosts
()
self
.
is_finished
=
False
self
.
is_finished
=
False
self
.
force_rm_folder
=
config
.
FORCE_REMOVE_FOLDER
def
get_user_assets
(
self
):
def
get_user_assets
(
self
):
user_id
=
self
.
server
.
connection
.
user
.
id
user_id
=
self
.
server
.
connection
.
user
.
id
...
@@ -256,7 +259,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
...
@@ -256,7 +259,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
def
stat
(
self
,
path
):
def
stat
(
self
,
path
):
request
=
self
.
parse_path
(
path
)
request
=
self
.
parse_path
(
path
)
host
,
su
,
dpath
,
unique
=
request
[
'host'
],
request
[
'su'
],
\
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
))
logger
.
debug
(
"Stat path: {} => {}"
.
format
(
path
,
request
))
if
not
host
or
not
su
:
if
not
host
or
not
su
:
...
@@ -392,12 +395,24 @@ class SFTPServer(paramiko.SFTPServerInterface):
...
@@ -392,12 +395,24 @@ class SFTPServer(paramiko.SFTPServerInterface):
success
=
False
success
=
False
try
:
try
:
client
.
rmdir
(
rpath
)
if
self
.
force_rm_folder
:
self
.
_rmdir
(
client
,
rpath
)
else
:
client
.
rmdir
(
rpath
)
success
=
True
success
=
True
return
paramiko
.
SFTP_OK
return
paramiko
.
SFTP_OK
finally
:
finally
:
self
.
create_ftp_log
(
path
,
"Rmdir"
,
success
)
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
:
class
FakeServer
:
pass
pass
...
...
config_example.yml
View file @
510513c1
...
@@ -60,3 +60,9 @@ BOOTSTRAP_TOKEN: <PleasgeChangeSameWithJumpserver>
...
@@ -60,3 +60,9 @@ BOOTSTRAP_TOKEN: <PleasgeChangeSameWithJumpserver>
# 是否复用和用户后端资产已建立的连接(用户不会复用其他用户的连接)
# 是否复用和用户后端资产已建立的连接(用户不会复用其他用户的连接)
# REUSE_CONNECTION: true
# REUSE_CONNECTION: true
# 是否强制删除文件夹:(default false)
# FORCE_REMOVE_FOLDER: false
# Telnet连接协商使用的终端类型
# TELNET_TTYPE: XTERM-256COLOR
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment