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
2444c6fd
Unverified
Commit
2444c6fd
authored
Dec 25, 2018
by
老广
Committed by
GitHub
Dec 25, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #151 from jumpserver/dev
Dev
parents
f98e6815
1eb34b69
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
299 additions
and
279 deletions
+299
-279
config.py
coco/config.py
+2
-2
connection.py
coco/connection.py
+19
-15
app.py
coco/httpd/app.py
+5
-2
__init__.py
coco/httpd/elfinder/__init__.py
+2
-0
base.py
coco/httpd/elfinder/volumes/base.py
+2
-0
sftp.py
coco/httpd/elfinder/volumes/sftp.py
+3
-1
ws.py
coco/httpd/ws.py
+2
-2
interactive.py
coco/interactive.py
+62
-93
interface.py
coco/interface.py
+10
-10
logger.py
coco/logger.py
+2
-1
models.py
coco/models.py
+34
-17
proxy.py
coco/proxy.py
+8
-6
recorder.py
coco/recorder.py
+16
-7
session.py
coco/session.py
+10
-9
sftp.py
coco/sftp.py
+9
-7
sshd.py
coco/sshd.py
+41
-41
struct.py
coco/struct.py
+3
-3
utils.py
coco/utils.py
+10
-19
cocod
cocod
+11
-6
coco.mo
locale/en/LC_MESSAGES/coco.mo
+0
-0
coco.po
locale/en/LC_MESSAGES/coco.po
+24
-19
coco.mo
locale/zh_CN/LC_MESSAGES/coco.mo
+0
-0
coco.po
locale/zh_CN/LC_MESSAGES/coco.po
+24
-19
No files found.
coco/config.py
View file @
2444c6fd
...
@@ -94,7 +94,7 @@ class Config(dict):
...
@@ -94,7 +94,7 @@ class Config(dict):
def
__init__
(
self
,
root_path
,
defaults
=
None
):
def
__init__
(
self
,
root_path
,
defaults
=
None
):
self
.
defaults
=
defaults
or
{}
self
.
defaults
=
defaults
or
{}
self
.
root_path
=
root_path
self
.
root_path
=
root_path
super
()
.
__init__
({})
super
(
Config
,
self
)
.
__init__
({})
def
from_envvar
(
self
,
variable_name
,
silent
=
False
):
def
from_envvar
(
self
,
variable_name
,
silent
=
False
):
"""Loads a configuration from an environment variable pointing to
"""Loads a configuration from an environment variable pointing to
...
@@ -272,7 +272,7 @@ class Config(dict):
...
@@ -272,7 +272,7 @@ class Config(dict):
def
__getitem__
(
self
,
item
):
def
__getitem__
(
self
,
item
):
try
:
try
:
value
=
super
()
.
__getitem__
(
item
)
value
=
super
(
Config
,
self
)
.
__getitem__
(
item
)
except
KeyError
:
except
KeyError
:
value
=
None
value
=
None
if
value
is
not
None
:
if
value
is
not
None
:
...
...
coco/connection.py
View file @
2444c6fd
...
@@ -4,9 +4,13 @@
...
@@ -4,9 +4,13 @@
import
os
import
os
import
re
import
re
import
socket
import
socket
import
selectors
import
telnetlib
import
telnetlib
try
:
import
selectors
except
ImportError
:
import
selectors2
as
selectors
import
paramiko
import
paramiko
from
paramiko.ssh_exception
import
SSHException
from
paramiko.ssh_exception
import
SSHException
...
@@ -54,7 +58,7 @@ class SSHConnection:
...
@@ -54,7 +58,7 @@ class SSHConnection:
asset
.
ip
,
port
=
asset
.
port
,
username
=
system_user
.
username
,
asset
.
ip
,
port
=
asset
.
port
,
username
=
system_user
.
username
,
password
=
system_user
.
password
,
pkey
=
system_user
.
private_key
,
password
=
system_user
.
password
,
pkey
=
system_user
.
private_key
,
timeout
=
config
[
'SSH_TIMEOUT'
],
timeout
=
config
[
'SSH_TIMEOUT'
],
compress
=
Tru
e
,
auth_timeout
=
config
[
'SSH_TIMEOUT'
],
compress
=
Fals
e
,
auth_timeout
=
config
[
'SSH_TIMEOUT'
],
look_for_keys
=
False
,
sock
=
sock
look_for_keys
=
False
,
sock
=
sock
)
)
except
paramiko
.
AuthenticationException
:
except
paramiko
.
AuthenticationException
:
...
@@ -62,7 +66,7 @@ class SSHConnection:
...
@@ -62,7 +66,7 @@ class SSHConnection:
ssh
.
connect
(
ssh
.
connect
(
asset
.
ip
,
port
=
asset
.
port
,
username
=
system_user
.
username
,
asset
.
ip
,
port
=
asset
.
port
,
username
=
system_user
.
username
,
password
=
system_user
.
password
,
timeout
=
config
[
'SSH_TIMEOUT'
],
password
=
system_user
.
password
,
timeout
=
config
[
'SSH_TIMEOUT'
],
compress
=
Tru
e
,
auth_timeout
=
config
[
'SSH_TIMEOUT'
],
compress
=
Fals
e
,
auth_timeout
=
config
[
'SSH_TIMEOUT'
],
look_for_keys
=
False
,
sock
=
sock
,
allow_agent
=
False
,
look_for_keys
=
False
,
sock
=
sock
,
allow_agent
=
False
,
)
)
transport
=
ssh
.
get_transport
()
transport
=
ssh
.
get_transport
()
...
@@ -86,7 +90,7 @@ class SSHConnection:
...
@@ -86,7 +90,7 @@ class SSHConnection:
password_short
,
key_fingerprint
,
password_short
,
key_fingerprint
,
))
))
return
None
,
None
,
error
+
'
\n
'
+
str
(
e
)
return
None
,
None
,
error
+
'
\n
'
+
str
(
e
)
except
(
socket
.
error
,
TimeoutError
)
as
e
:
except
(
socket
.
error
,
socket
.
timeout
)
as
e
:
return
None
,
None
,
error
+
'
\n
'
+
str
(
e
)
return
None
,
None
,
error
+
'
\n
'
+
str
(
e
)
return
ssh
,
sock
,
None
return
ssh
,
sock
,
None
...
@@ -197,7 +201,7 @@ class TelnetConnection:
...
@@ -197,7 +201,7 @@ class TelnetConnection:
)
)
def
get_socket
(
self
):
def
get_socket
(
self
):
logger
.
info
(
'Get telnet server socket. {}'
.
format
(
self
.
client
.
user
))
logger
.
debug
(
'Get telnet server socket. {}'
.
format
(
self
.
client
.
user
))
self
.
sock
=
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_STREAM
)
self
.
sock
=
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_STREAM
)
self
.
sock
.
settimeout
(
10
)
self
.
sock
.
settimeout
(
10
)
self
.
sock
.
connect
((
self
.
asset
.
ip
,
self
.
asset
.
port
))
self
.
sock
.
connect
((
self
.
asset
.
ip
,
self
.
asset
.
port
))
...
@@ -211,7 +215,7 @@ class TelnetConnection:
...
@@ -211,7 +215,7 @@ class TelnetConnection:
for
sock
in
[
key
.
fileobj
for
key
,
_
in
events
]:
for
sock
in
[
key
.
fileobj
for
key
,
_
in
events
]:
data
=
sock
.
recv
(
BUF_SIZE
)
data
=
sock
.
recv
(
BUF_SIZE
)
if
sock
==
self
.
sock
:
if
sock
==
self
.
sock
:
logger
.
info
(
b
'[Telnet server send]: '
+
data
)
logger
.
debug
(
b
'[Telnet server send]: '
+
data
)
if
not
data
:
if
not
data
:
self
.
sock
.
close
()
self
.
sock
.
close
()
...
@@ -247,7 +251,7 @@ class TelnetConnection:
...
@@ -247,7 +251,7 @@ class TelnetConnection:
:param data: option negotiate data
:param data: option negotiate data
:return:
:return:
"""
"""
logger
.
info
(
b
'[Server options negotiate]: '
+
data
)
logger
.
debug
(
b
'[Server options negotiate]: '
+
data
)
data_list
=
data
.
split
(
telnetlib
.
IAC
)
data_list
=
data
.
split
(
telnetlib
.
IAC
)
new_data_list
=
[]
new_data_list
=
[]
for
x
in
data_list
:
for
x
in
data_list
:
...
@@ -272,11 +276,11 @@ class TelnetConnection:
...
@@ -272,11 +276,11 @@ class TelnetConnection:
else
:
else
:
new_data_list
.
append
(
x
)
new_data_list
.
append
(
x
)
new_data
=
telnetlib
.
IAC
.
join
(
new_data_list
)
new_data
=
telnetlib
.
IAC
.
join
(
new_data_list
)
logger
.
info
(
b
'[Client options negotiate]: '
+
new_data
)
logger
.
debug
(
b
'[Client options negotiate]: '
+
new_data
)
self
.
sock
.
send
(
new_data
)
self
.
sock
.
send
(
new_data
)
def
login_auth
(
self
,
raw_data
):
def
login_auth
(
self
,
raw_data
):
logger
.
info
(
'[Telnet login auth]: ({})'
.
format
(
self
.
client
.
user
))
logger
.
debug
(
'[Telnet login auth]: ({})'
.
format
(
self
.
client
.
user
))
try
:
try
:
data
=
raw_data
.
decode
(
'utf-8'
)
data
=
raw_data
.
decode
(
'utf-8'
)
...
@@ -284,23 +288,23 @@ class TelnetConnection:
...
@@ -284,23 +288,23 @@ class TelnetConnection:
try
:
try
:
data
=
raw_data
.
decode
(
'gbk'
)
data
=
raw_data
.
decode
(
'gbk'
)
except
UnicodeDecodeError
:
except
UnicodeDecodeError
:
logger
.
info
(
b
'[Decode error]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
logger
.
debug
(
b
'[Decode error]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
return
None
return
None
if
self
.
incorrect_pattern
.
search
(
data
):
if
self
.
incorrect_pattern
.
search
(
data
):
logger
.
info
(
b
'[Login incorrect prompt]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
logger
.
debug
(
b
'[Login incorrect prompt]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
return
False
return
False
elif
self
.
username_pattern
.
search
(
data
):
elif
self
.
username_pattern
.
search
(
data
):
logger
.
info
(
b
'[Username prompt]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
logger
.
debug
(
b
'[Username prompt]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
self
.
sock
.
send
(
self
.
system_user
.
username
.
encode
(
'utf-8'
)
+
b
'
\r\n
'
)
self
.
sock
.
send
(
self
.
system_user
.
username
.
encode
(
'utf-8'
)
+
b
'
\r\n
'
)
return
None
return
None
elif
self
.
password_pattern
.
search
(
data
):
elif
self
.
password_pattern
.
search
(
data
):
logger
.
info
(
b
'[Password prompt]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
logger
.
debug
(
b
'[Password prompt]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
self
.
sock
.
send
(
self
.
system_user
.
password
.
encode
(
'utf-8'
)
+
b
'
\r\n
'
)
self
.
sock
.
send
(
self
.
system_user
.
password
.
encode
(
'utf-8'
)
+
b
'
\r\n
'
)
return
None
return
None
elif
self
.
success_pattern
.
search
(
data
):
elif
self
.
success_pattern
.
search
(
data
):
logger
.
info
(
b
'[Login Success prompt]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
logger
.
debug
(
b
'[Login Success prompt]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
return
True
return
True
else
:
else
:
logger
.
info
(
b
'[No match]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
logger
.
debug
(
b
'[No match]: '
+
b
'>>'
+
raw_data
+
b
'<<'
)
return
None
return
None
coco/httpd/app.py
View file @
2444c6fd
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
import
os
from
flask_socketio
import
SocketIO
from
flask_socketio
import
SocketIO
from
flask
import
Flask
from
flask
import
Flask
...
@@ -17,8 +18,10 @@ socket_io = SocketIO()
...
@@ -17,8 +18,10 @@ socket_io = SocketIO()
socket_io
.
on_namespace
(
ProxyNamespace
(
'/ssh'
))
socket_io
.
on_namespace
(
ProxyNamespace
(
'/ssh'
))
socket_io
.
on_namespace
(
ElfinderNamespace
(
'/elfinder'
))
socket_io
.
on_namespace
(
ElfinderNamespace
(
'/elfinder'
))
# init_kwargs = {'async_mode': 'threading'}
if
os
.
environ
.
get
(
'USE_EVENTLET'
,
'1'
)
==
'1'
:
init_kwargs
=
{
'async_mode'
:
'eventlet'
}
init_kwargs
=
{
'async_mode'
:
'eventlet'
}
else
:
init_kwargs
=
{
'async_mode'
:
'threading'
}
socket_io
.
init_app
(
app
,
**
init_kwargs
),
socket_io
.
init_app
(
app
,
**
init_kwargs
),
socket_io
.
on_error_default
(
lambda
x
:
logger
.
exception
(
x
))
socket_io
.
on_error_default
(
lambda
x
:
logger
.
exception
(
x
))
...
...
coco/httpd/elfinder/__init__.py
0 → 100644
View file @
2444c6fd
# -*- coding: utf-8 -*-
#
coco/httpd/elfinder/volumes/base.py
View file @
2444c6fd
# -*- coding: utf-8 -*-
#
import
base64
import
base64
import
os
import
os
import
hashlib
import
hashlib
...
...
coco/httpd/elfinder/volumes/sftp.py
View file @
2444c6fd
# -*- coding: utf-8 -*-
#
import
stat
import
stat
import
threading
import
threading
...
@@ -14,9 +16,9 @@ class SFTPVolume(BaseVolume):
...
@@ -14,9 +16,9 @@ class SFTPVolume(BaseVolume):
def
__init__
(
self
,
sftp
):
def
__init__
(
self
,
sftp
):
self
.
sftp
=
sftp
self
.
sftp
=
sftp
self
.
root_name
=
'Home'
self
.
root_name
=
'Home'
super
()
.
__init__
()
self
.
_stat_cache
=
{}
self
.
_stat_cache
=
{}
self
.
lock
=
threading
.
Lock
()
self
.
lock
=
threading
.
Lock
()
super
(
SFTPVolume
,
self
)
.
__init__
()
def
close
(
self
):
def
close
(
self
):
self
.
sftp
.
close
()
self
.
sftp
.
close
()
...
...
coco/httpd/ws.py
View file @
2444c6fd
...
@@ -28,7 +28,7 @@ class ProxyNamespace(BaseNamespace):
...
@@ -28,7 +28,7 @@ class ProxyNamespace(BaseNamespace):
...
...
}
}
"""
"""
super
()
.
__init__
(
*
args
,
**
kwargs
)
super
(
BaseNamespace
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
win_size
=
None
self
.
win_size
=
None
def
new_connection
(
self
):
def
new_connection
(
self
):
...
@@ -46,7 +46,7 @@ class ProxyNamespace(BaseNamespace):
...
@@ -46,7 +46,7 @@ class ProxyNamespace(BaseNamespace):
def
on_connect
(
self
):
def
on_connect
(
self
):
logger
.
debug
(
"On connect event trigger"
)
logger
.
debug
(
"On connect event trigger"
)
self
.
get_current_user
()
self
.
get_current_user
()
super
()
.
on_connect
()
super
(
ProxyNamespace
,
self
)
.
on_connect
()
self
.
new_connection
()
self
.
new_connection
()
def
on_host
(
self
,
message
):
def
on_host
(
self
,
message
):
...
...
coco/interactive.py
View file @
2444c6fd
#!/usr/bin/env python3
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
from
__future__
import
unicode_literals
import
socket
import
socket
import
threading
import
threading
...
@@ -28,20 +29,20 @@ PROXY = 'proxy'
...
@@ -28,20 +29,20 @@ PROXY = 'proxy'
class
InteractiveServer
:
class
InteractiveServer
:
_sentinel
=
object
()
_sentinel
=
object
()
_user_assets_cached
=
{}
def
__init__
(
self
,
client
):
def
__init__
(
self
,
client
):
self
.
client
=
client
self
.
client
=
client
self
.
closed
=
False
self
.
closed
=
False
self
.
_results
=
None
self
.
_results
=
None
self
.
nodes
=
None
self
.
nodes
=
None
self
.
offset
=
0
self
.
assets
=
None
self
.
limit
=
100
self
.
get_user_assets_finished
=
False
self
.
assets
=
[]
self
.
finish
=
False
self
.
page
=
1
self
.
page
=
1
self
.
total_asset
s
=
0
# 用户被授权的所有资产
self
.
total_asset
_count
=
0
# 用户被授权的所有资产数量
self
.
total_count
=
0
# 分页展示中的资产总数量
self
.
total_count
=
0
# 分页展示中的资产总数量
self
.
node_tree
=
None
# 授权节点树
self
.
node_tree
=
None
# 授权节点树
self
.
load_user_assets_from_cache
()
self
.
get_user_assets_async
()
self
.
get_user_assets_async
()
self
.
get_user_nodes_async
()
self
.
get_user_nodes_async
()
...
@@ -82,7 +83,7 @@ class InteractiveServer:
...
@@ -82,7 +83,7 @@ class InteractiveServer:
self
.
client
.
send
(
char
.
CLEAR_CHAR
)
self
.
client
.
send
(
char
.
CLEAR_CHAR
)
self
.
display_logo
()
self
.
display_logo
()
header
=
_
(
"
\n
{T}{T}{title} {user}, Welcome to use Jumpserver open source fortress system {end}{R}{R}"
)
header
=
_
(
"
\n
{T}{T}{title} {user}, Welcome to use Jumpserver open source fortress system {end}{R}{R}"
)
menu
s
=
[
menu
=
[
_
(
"{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, Comment{end} to search login(if unique).{R}"
),
_
(
"{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, Comment{end} to search login(if unique).{R}"
),
_
(
"{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} search, such as: /ip.{R}"
),
_
(
"{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} search, such as: /ip.{R}"
),
_
(
"{T}3) Enter {green}p{end} to display the host you have permission.{R}"
),
_
(
"{T}3) Enter {green}p{end} to display the host you have permission.{R}"
),
...
@@ -97,8 +98,8 @@ class InteractiveServer:
...
@@ -97,8 +98,8 @@ class InteractiveServer:
title
=
"
\033
[1;32m"
,
user
=
self
.
client
.
user
,
end
=
"
\033
[0m"
,
title
=
"
\033
[1;32m"
,
user
=
self
.
client
.
user
,
end
=
"
\033
[0m"
,
T
=
'
\t
'
,
R
=
'
\r\n\r
'
T
=
'
\t
'
,
R
=
'
\r\n\r
'
))
))
for
menu
in
menus
:
for
item
in
menu
:
self
.
client
.
send
(
menu
.
format
(
self
.
client
.
send
(
item
.
format
(
green
=
"
\033
[32m"
,
end
=
"
\033
[0m"
,
green
=
"
\033
[32m"
,
end
=
"
\033
[0m"
,
T
=
'
\t
'
,
R
=
'
\r\n\r
'
T
=
'
\t
'
,
R
=
'
\r\n\r
'
))
))
...
@@ -143,7 +144,7 @@ class InteractiveServer:
...
@@ -143,7 +144,7 @@ class InteractiveServer:
def
search_and_display_assets
(
self
,
q
):
def
search_and_display_assets
(
self
,
q
):
assets
=
self
.
search_assets
(
q
)
assets
=
self
.
search_assets
(
q
)
self
.
display_assets
(
assets
)
self
.
display_assets
_paging
(
assets
)
def
search_and_proxy_assets
(
self
,
opt
):
def
search_and_proxy_assets
(
self
,
opt
):
assets
=
self
.
search_assets
(
opt
)
assets
=
self
.
search_assets
(
opt
)
...
@@ -158,25 +159,13 @@ class InteractiveServer:
...
@@ -158,25 +159,13 @@ class InteractiveServer:
return
return
self
.
proxy
(
asset
)
self
.
proxy
(
asset
)
else
:
else
:
self
.
display_assets
(
assets
)
self
.
display_assets
_paging
(
assets
)
def
refresh_assets_nodes
(
self
):
def
refresh_assets_nodes
(
self
):
self
.
get_user_assets_async
()
self
.
get_user_assets_async
()
self
.
get_user_nodes_async
()
self
.
get_user_nodes_async
()
def
search_assets
(
self
,
q
):
def
search_assets
(
self
,
q
):
if
self
.
finish
:
assets
=
self
.
search_assets_from_local
(
q
)
else
:
assets
=
self
.
search_assets_from_core
(
q
)
return
assets
def
search_assets_from_core
(
self
,
q
):
assets
=
app_service
.
get_search_user_granted_assets
(
self
.
client
.
user
,
q
)
assets
=
self
.
filter_system_users
(
assets
)
return
assets
def
search_assets_from_local
(
self
,
q
):
result
=
[]
result
=
[]
# 所有的
# 所有的
...
@@ -204,20 +193,17 @@ class InteractiveServer:
...
@@ -204,20 +193,17 @@ class InteractiveServer:
# Display assets
# Display assets
#
#
def
display_assets
(
self
,
assets
=
None
):
def
display_assets
(
self
):
if
assets
is
None
:
while
self
.
assets
is
None
and
not
self
.
get_user_assets_finished
:
while
not
self
.
assets
and
not
self
.
finish
:
time
.
sleep
(
0.5
)
time
.
sleep
(
0.2
)
if
self
.
assets
:
assets
=
self
.
assets
self
.
display_assets_paging
(
self
.
assets
)
self
.
display_assets_paging
(
assets
)
def
display_assets_paging
(
self
,
assets
):
def
display_assets_paging
(
self
,
assets
):
if
len
(
assets
)
==
0
:
if
len
(
assets
)
==
0
:
self
.
client
.
send
(
wr
(
_
(
"No Assets"
),
before
=
0
))
self
.
client
.
send
(
wr
(
_
(
"No Assets"
),
before
=
0
))
return
return
self
.
total_count
=
len
(
assets
)
self
.
total_count
=
self
.
total_assets
if
assets
is
self
.
assets
else
len
(
assets
)
action
=
None
action
=
None
gen
=
self
.
_page_generator
(
assets
)
gen
=
self
.
_page_generator
(
assets
)
...
@@ -238,14 +224,8 @@ class InteractiveServer:
...
@@ -238,14 +224,8 @@ class InteractiveServer:
start
,
page
=
0
,
1
start
,
page
=
0
,
1
while
True
:
while
True
:
_assets
=
assets
[
start
:
start
+
self
.
page_size
]
_assets
=
assets
[
start
:
start
+
self
.
page_size
]
# 等待加载
if
(
assets
is
self
.
assets
)
and
(
not
self
.
finish
)
and
(
not
self
.
need_paging
):
time
.
sleep
(
1
)
continue
# 最后一页
# 最后一页
elif
_assets
and
(
page
==
self
.
total_pages
)
and
(
if
page
==
self
.
total_pages
:
assets
is
not
self
.
assets
or
(
assets
is
self
.
assets
and
self
.
finish
)):
return
page
,
_assets
return
page
,
_assets
# 执行动作
# 执行动作
else
:
else
:
...
@@ -273,33 +253,8 @@ class InteractiveServer:
...
@@ -273,33 +253,8 @@ class InteractiveServer:
def
display_a_page_assets
(
self
,
page
,
assets
):
def
display_a_page_assets
(
self
,
page
,
assets
):
self
.
client
.
send
(
char
.
CLEAR_CHAR
)
self
.
client
.
send
(
char
.
CLEAR_CHAR
)
self
.
page
=
page
self
.
page
=
page
self
.
results
=
assets
self
.
display_results
()
def
display_page_bottom_prompt
(
self
):
self
.
client
.
send
(
wr
(
_
(
'Tips: Enter the asset ID and log directly into the asset.'
),
before
=
1
))
prompt_page_up
=
_
(
"Page up: P/p"
)
prompt_page_down
=
_
(
"Page down: Enter|N/n"
)
prompt_back
=
_
(
"BACK: b/q"
)
prompts
=
[
prompt_page_up
,
prompt_page_down
,
prompt_back
]
prompt
=
'
\t
'
.
join
(
prompts
)
self
.
client
.
send
(
wr
(
prompt
,
before
=
1
))
def
get_user_action
(
self
):
opt
=
net_input
(
self
.
client
,
prompt
=
':'
)
if
opt
in
(
'p'
,
'P'
):
return
PAGE_UP
elif
opt
in
(
'b'
,
'q'
):
return
BACK
elif
opt
.
isdigit
()
and
self
.
results
and
0
<
int
(
opt
)
<=
len
(
self
.
results
):
self
.
proxy
(
self
.
results
[
int
(
opt
)
-
1
])
return
BACK
else
:
return
PAGE_DOWN
def
display_results
(
self
):
sort_by
=
config
[
"ASSET_LIST_SORT_BY"
]
sort_by
=
config
[
"ASSET_LIST_SORT_BY"
]
self
.
results
=
sort_assets
(
self
.
resul
ts
,
sort_by
)
self
.
results
=
sort_assets
(
asse
ts
,
sort_by
)
fake_data
=
[
_
(
"ID"
),
_
(
"Hostname"
),
_
(
"IP"
),
_
(
"LoginAs"
)]
fake_data
=
[
_
(
"ID"
),
_
(
"Hostname"
),
_
(
"IP"
),
_
(
"LoginAs"
)]
id_length
=
max
(
len
(
str
(
len
(
self
.
results
))),
4
)
id_length
=
max
(
len
(
str
(
len
(
self
.
results
))),
4
)
hostname_length
=
item_max_length
(
self
.
results
,
15
,
hostname_length
=
item_max_length
(
self
.
results
,
15
,
...
@@ -323,44 +278,59 @@ class InteractiveServer:
...
@@ -323,44 +278,59 @@ class InteractiveServer:
]
]
self
.
client
.
send
(
wr
(
format_with_zh
(
size_list
,
*
data
)))
self
.
client
.
send
(
wr
(
format_with_zh
(
size_list
,
*
data
)))
self
.
client
.
send
(
wr
(
title
(
_
(
"Page: {}, Count: {}, Total Page: {}, Total Count: {}"
)
.
format
(
self
.
client
.
send
(
wr
(
title
(
self
.
page
,
len
(
self
.
results
),
self
.
total_pages
,
self
.
total_count
)),
before
=
1
)
_
(
"Page: {}, Count: {}, Total Page: {}, Total Count: {}"
)
.
format
(
self
.
page
,
len
(
self
.
results
),
self
.
total_pages
,
self
.
total_count
)),
before
=
1
)
)
)
def
display_page_bottom_prompt
(
self
):
msg
=
wr
(
_
(
'Tips: Enter the asset ID and log directly into the asset.'
),
before
=
1
)
self
.
client
.
send
(
msg
)
prompt_page_up
=
_
(
"Page up: P/p"
)
prompt_page_down
=
_
(
"Page down: Enter|N/n"
)
prompt_back
=
_
(
"BACK: b/q"
)
prompts
=
[
prompt_page_up
,
prompt_page_down
,
prompt_back
]
prompt
=
'
\t
'
.
join
(
prompts
)
self
.
client
.
send
(
wr
(
prompt
,
before
=
1
))
def
get_user_action
(
self
):
opt
=
net_input
(
self
.
client
,
prompt
=
':'
)
if
opt
in
(
'p'
,
'P'
):
return
PAGE_UP
elif
opt
in
(
'b'
,
'q'
):
return
BACK
elif
opt
.
isdigit
()
and
self
.
results
and
0
<
int
(
opt
)
<=
len
(
self
.
results
):
self
.
proxy
(
self
.
results
[
int
(
opt
)
-
1
])
return
BACK
else
:
return
PAGE_DOWN
#
#
# Get assets
# Get assets
#
#
def
load_user_assets_from_cache
(
self
):
assets
=
self
.
__class__
.
_user_assets_cached
.
get
(
self
.
client
.
user
.
id
)
self
.
assets
=
assets
if
assets
:
self
.
total_asset_count
=
len
(
assets
)
def
set_user_assets_cache
(
self
,
assets
):
self
.
__class__
.
_user_assets_cached
[
self
.
client
.
user
.
id
]
=
assets
def
get_user_assets_async
(
self
):
def
get_user_assets_async
(
self
):
if
self
.
need_paging
:
thread
=
threading
.
Thread
(
target
=
self
.
get_user_assets
)
thread
=
threading
.
Thread
(
target
=
self
.
get_user_assets_paging
)
else
:
thread
=
threading
.
Thread
(
target
=
self
.
get_user_assets_direct
)
thread
.
start
()
thread
.
start
()
def
get_user_assets
_direct
(
self
):
def
get_user_assets
(
self
):
assets
=
app_service
.
get_user_assets
(
self
.
client
.
user
)
assets
=
app_service
.
get_user_assets
(
self
.
client
.
user
)
assets
=
self
.
filter_system_users
(
assets
)
assets
=
self
.
filter_system_users
(
assets
)
self
.
assets
=
assets
self
.
set_user_assets_cache
(
assets
)
self
.
total_assets
=
len
(
assets
)
self
.
load_user_assets_from_cache
()
self
.
finish
=
True
self
.
get_user_assets_finished
=
True
def
get_user_assets_paging
(
self
):
while
not
self
.
closed
:
assets
,
total
=
app_service
.
get_user_assets_paging
(
self
.
client
.
user
,
offset
=
self
.
offset
,
limit
=
self
.
limit
)
if
not
assets
:
logger
.
info
(
'Get user assets paging async finished.'
)
self
.
finish
=
True
break
logger
.
info
(
'Get user assets paging async: {}'
.
format
(
len
(
assets
)))
assets
=
self
.
filter_system_users
(
assets
)
self
.
total_assets
=
total
self
.
assets
.
extend
(
assets
)
self
.
offset
+=
self
.
limit
#
#
# Nodes
# Nodes
#
#
...
@@ -415,7 +385,7 @@ class InteractiveServer:
...
@@ -415,7 +385,7 @@ class InteractiveServer:
return
return
assets
=
self
.
nodes
[
_id
-
1
]
.
assets_granted
assets
=
self
.
nodes
[
_id
-
1
]
.
assets_granted
self
.
display_assets
(
assets
)
self
.
display_assets
_paging
(
assets
)
#
#
# System users
# System users
...
@@ -487,7 +457,6 @@ class InteractiveServer:
...
@@ -487,7 +457,6 @@ class InteractiveServer:
def
close
(
self
):
def
close
(
self
):
logger
.
debug
(
"Interactive server server close: {}"
.
format
(
self
))
logger
.
debug
(
"Interactive server server close: {}"
.
format
(
self
))
self
.
closed
=
True
self
.
closed
=
True
# current_app.remove_client(self.client)
def
interact_async
(
self
):
def
interact_async
(
self
):
# 目前没用
# 目前没用
...
...
coco/interface.py
View file @
2444c6fd
...
@@ -30,14 +30,14 @@ class SSHInterface(paramiko.ServerInterface):
...
@@ -30,14 +30,14 @@ class SSHInterface(paramiko.ServerInterface):
self
.
user
=
None
self
.
user
=
None
def
check_auth_interactive
(
self
,
username
,
submethods
):
def
check_auth_interactive
(
self
,
username
,
submethods
):
logger
.
info
(
"Check auth interactive:
%
s
%
s"
%
(
username
,
submethods
))
logger
.
debug
(
"Check auth interactive:
%
s
%
s"
%
(
username
,
submethods
))
instructions
=
'Please enter 6 digits.'
instructions
=
'Please enter 6 digits.'
interactive
=
paramiko
.
server
.
InteractiveQuery
(
instructions
=
instructions
)
interactive
=
paramiko
.
server
.
InteractiveQuery
(
instructions
=
instructions
)
interactive
.
add_prompt
(
prompt
=
'[MFA auth]: '
)
interactive
.
add_prompt
(
prompt
=
'[MFA auth]: '
)
return
interactive
return
interactive
def
check_auth_interactive_response
(
self
,
responses
):
def
check_auth_interactive_response
(
self
,
responses
):
logger
.
info
(
"Check auth interactive response:
%
s "
%
responses
)
logger
.
debug
(
"Check auth interactive response:
%
s "
%
responses
)
# TODO:MFA Auth
# TODO:MFA Auth
otp_code
=
responses
[
0
]
otp_code
=
responses
[
0
]
if
not
otp_code
or
not
len
(
otp_code
)
==
6
or
not
otp_code
.
isdigit
():
if
not
otp_code
or
not
len
(
otp_code
)
==
6
or
not
otp_code
.
isdigit
():
...
@@ -90,7 +90,7 @@ class SSHInterface(paramiko.ServerInterface):
...
@@ -90,7 +90,7 @@ class SSHInterface(paramiko.ServerInterface):
logger
.
debug
(
"Public key auth <
%
s> failed, try to password"
%
username
)
logger
.
debug
(
"Public key auth <
%
s> failed, try to password"
%
username
)
return
paramiko
.
AUTH_FAILED
return
paramiko
.
AUTH_FAILED
else
:
else
:
logger
.
debug
(
"Public key auth <
%
s> success"
%
username
)
logger
.
info
(
"Public key auth <
%
s> success"
%
username
)
if
self
.
otp_auth
:
if
self
.
otp_auth
:
return
paramiko
.
AUTH_PARTIALLY_SUCCESSFUL
return
paramiko
.
AUTH_PARTIALLY_SUCCESSFUL
return
paramiko
.
AUTH_SUCCESSFUL
return
paramiko
.
AUTH_SUCCESSFUL
...
@@ -149,14 +149,14 @@ class SSHInterface(paramiko.ServerInterface):
...
@@ -149,14 +149,14 @@ class SSHInterface(paramiko.ServerInterface):
return
0
return
0
def
check_port_forward_request
(
self
,
address
,
port
):
def
check_port_forward_request
(
self
,
address
,
port
):
logger
.
info
(
logger
.
debug
(
"Check channel port forward request:
%
s
%
s"
%
(
address
,
port
)
"Check channel port forward request:
%
s
%
s"
%
(
address
,
port
)
)
)
self
.
event
.
set
()
self
.
event
.
set
()
return
False
return
False
def
check_channel_request
(
self
,
kind
,
chan_id
):
def
check_channel_request
(
self
,
kind
,
chan_id
):
logger
.
info
(
"Check channel request:
%
s
%
d"
%
(
kind
,
chan_id
))
logger
.
debug
(
"Check channel request:
%
s
%
d"
%
(
kind
,
chan_id
))
client
=
self
.
connection
.
new_client
(
chan_id
)
client
=
self
.
connection
.
new_client
(
chan_id
)
client
.
request
.
kind
=
kind
client
.
request
.
kind
=
kind
return
paramiko
.
OPEN_SUCCEEDED
return
paramiko
.
OPEN_SUCCEEDED
...
@@ -188,7 +188,7 @@ class SSHInterface(paramiko.ServerInterface):
...
@@ -188,7 +188,7 @@ class SSHInterface(paramiko.ServerInterface):
def
check_channel_pty_request
(
def
check_channel_pty_request
(
self
,
channel
,
term
,
width
,
height
,
self
,
channel
,
term
,
width
,
height
,
pixelwidth
,
pixelheight
,
modes
):
pixelwidth
,
pixelheight
,
modes
):
logger
.
info
(
"Check channel pty request:
%
s
%
s
%
s
%
s
%
s"
%
logger
.
debug
(
"Check channel pty request:
%
s
%
s
%
s
%
s
%
s"
%
(
term
,
width
,
height
,
pixelwidth
,
pixelheight
))
(
term
,
width
,
height
,
pixelwidth
,
pixelheight
))
client
=
self
.
connection
.
get_client
(
channel
)
client
=
self
.
connection
.
get_client
(
channel
)
client
.
request
.
type
=
'pty'
client
.
request
.
type
=
'pty'
...
@@ -201,18 +201,18 @@ class SSHInterface(paramiko.ServerInterface):
...
@@ -201,18 +201,18 @@ class SSHInterface(paramiko.ServerInterface):
return
True
return
True
def
check_channel_shell_request
(
self
,
channel
):
def
check_channel_shell_request
(
self
,
channel
):
logger
.
info
(
"Check channel shell request:
%
s"
%
channel
.
get_id
())
logger
.
debug
(
"Check channel shell request:
%
s"
%
channel
.
get_id
())
client
=
self
.
connection
.
get_client
(
channel
)
client
=
self
.
connection
.
get_client
(
channel
)
client
.
request
.
meta
[
'shell'
]
=
True
client
.
request
.
meta
[
'shell'
]
=
True
return
True
return
True
def
check_channel_subsystem_request
(
self
,
channel
,
name
):
def
check_channel_subsystem_request
(
self
,
channel
,
name
):
logger
.
info
(
"Check channel subsystem request:
%
s"
%
name
)
logger
.
debug
(
"Check channel subsystem request:
%
s"
%
name
)
client
=
self
.
connection
.
get_client
(
channel
)
client
=
self
.
connection
.
get_client
(
channel
)
client
.
request
.
type
=
'subsystem'
client
.
request
.
type
=
'subsystem'
client
.
request
.
meta
[
'subsystem'
]
=
name
client
.
request
.
meta
[
'subsystem'
]
=
name
self
.
event
.
set
()
self
.
event
.
set
()
return
super
()
.
check_channel_subsystem_request
(
channel
,
name
)
return
super
(
SSHInterface
,
self
)
.
check_channel_subsystem_request
(
channel
,
name
)
def
check_channel_window_change_request
(
self
,
channel
,
width
,
height
,
def
check_channel_window_change_request
(
self
,
channel
,
width
,
height
,
pixelwidth
,
pixelheight
):
pixelwidth
,
pixelheight
):
...
@@ -228,7 +228,7 @@ class SSHInterface(paramiko.ServerInterface):
...
@@ -228,7 +228,7 @@ class SSHInterface(paramiko.ServerInterface):
def
check_channel_x11_request
(
self
,
channel
,
single_connection
,
def
check_channel_x11_request
(
self
,
channel
,
single_connection
,
auth_protocol
,
auth_cookie
,
screen_number
):
auth_protocol
,
auth_cookie
,
screen_number
):
logger
.
info
(
"Check channel x11 request
%
s
%
s
%
s
%
s"
%
logger
.
debug
(
"Check channel x11 request
%
s
%
s
%
s
%
s"
%
(
single_connection
,
auth_protocol
,
(
single_connection
,
auth_protocol
,
auth_cookie
,
screen_number
))
auth_cookie
,
screen_number
))
client
=
self
.
connection
.
get_client
(
channel
)
client
=
self
.
connection
.
get_client
(
channel
)
...
...
coco/logger.py
View file @
2444c6fd
...
@@ -51,12 +51,13 @@ def create_logger():
...
@@ -51,12 +51,13 @@ def create_logger():
},
},
loggers
=
{
loggers
=
{
'coco'
:
main_setting
,
'coco'
:
main_setting
,
'paramiko'
:
main_setting
,
'jms'
:
main_setting
,
'jms'
:
main_setting
,
# 'socket.io': main_setting,
# 'socket.io': main_setting,
# 'engineio': main_setting,
# 'engineio': main_setting,
}
}
)
)
if
level
.
lower
()
==
'debug'
:
config
[
'loggers'
][
'paramiko'
]
=
main_setting
dictConfig
(
config
)
dictConfig
(
config
)
logger
=
logging
.
getLogger
()
logger
=
logging
.
getLogger
()
return
logger
return
logger
...
...
coco/models.py
View file @
2444c6fd
...
@@ -8,14 +8,14 @@ from .service import app_service
...
@@ -8,14 +8,14 @@ from .service import app_service
from
.struct
import
SizedList
,
SelectEvent
from
.struct
import
SizedList
,
SelectEvent
from
.utils
import
wrap_with_line_feed
as
wr
,
wrap_with_warning
as
warning
,
\
from
.utils
import
wrap_with_line_feed
as
wr
,
wrap_with_warning
as
warning
,
\
ugettext
as
_
ugettext
as
_
from
.
import
char
from
.
import
char
,
utils
from
.
import
utils
from
.
compat
import
str
BUF_SIZE
=
4096
BUF_SIZE
=
4096
logger
=
utils
.
get_logger
(
__file__
)
logger
=
utils
.
get_logger
(
__file__
)
class
Connection
:
class
Connection
(
object
)
:
connections
=
{}
connections
=
{}
clients_num
=
0
clients_num
=
0
...
@@ -80,6 +80,8 @@ class Connection:
...
@@ -80,6 +80,8 @@ class Connection:
@classmethod
@classmethod
def
remove_connection
(
cls
,
cid
):
def
remove_connection
(
cls
,
cid
):
connection
=
cls
.
get_connection
(
cid
)
connection
=
cls
.
get_connection
(
cid
)
if
not
connection
:
return
connection
.
close
()
connection
.
close
()
del
cls
.
connections
[
cid
]
del
cls
.
connections
[
cid
]
...
@@ -88,7 +90,7 @@ class Connection:
...
@@ -88,7 +90,7 @@ class Connection:
return
cls
.
connections
.
get
(
cid
)
return
cls
.
connections
.
get
(
cid
)
class
Request
:
class
Request
(
object
)
:
def
__init__
(
self
):
def
__init__
(
self
):
self
.
type
=
None
self
.
type
=
None
self
.
x11
=
None
self
.
x11
=
None
...
@@ -96,7 +98,7 @@ class Request:
...
@@ -96,7 +98,7 @@ class Request:
self
.
meta
=
{
'env'
:
{}}
self
.
meta
=
{
'env'
:
{}}
class
Client
:
class
Client
(
object
)
:
"""
"""
Client is the request client. Nothing more to say
Client is the request client. Nothing more to say
...
@@ -129,12 +131,17 @@ class Client:
...
@@ -129,12 +131,17 @@ class Client:
self
.
close
()
self
.
close
()
return
return
@property
def
closed
(
self
):
return
self
.
chan
.
closed
def
recv
(
self
,
size
):
def
recv
(
self
,
size
):
return
self
.
chan
.
recv
(
size
)
return
self
.
chan
.
recv
(
size
)
def
close
(
self
):
def
close
(
self
):
logger
.
info
(
"Client {} close"
.
format
(
self
))
logger
.
info
(
"Client {} close"
.
format
(
self
))
return
self
.
chan
.
close
()
self
.
chan
.
close
()
return
def
__getattr__
(
self
,
item
):
def
__getattr__
(
self
,
item
):
return
getattr
(
self
.
chan
,
item
)
return
getattr
(
self
.
chan
,
item
)
...
@@ -143,12 +150,12 @@ class Client:
...
@@ -143,12 +150,12 @@ class Client:
return
"<
%
s from
%
s:
%
s>"
%
(
self
.
user
,
self
.
addr
[
0
],
self
.
addr
[
1
])
return
"<
%
s from
%
s:
%
s>"
%
(
self
.
user
,
self
.
addr
[
0
],
self
.
addr
[
1
])
class
ServerFilter
:
class
ServerFilter
(
object
)
:
def
run
(
self
,
data
):
def
run
(
self
,
data
):
pass
pass
class
BaseServer
:
class
BaseServer
(
object
)
:
"""
"""
Base Server
Base Server
Achieve command record
Achieve command record
...
@@ -238,22 +245,31 @@ class BaseServer:
...
@@ -238,22 +245,31 @@ class BaseServer:
return
data
return
data
if
not
self
.
_input
:
if
not
self
.
_input
:
return
data
return
data
if
self
.
_cmd_filter_rules
is
None
:
msg
=
_
(
"Warning: Failed to load filter rule, "
"please press Ctrl + D to exit retry."
)
data
=
self
.
command_forbidden
(
msg
)
return
data
for
rule
in
self
.
_cmd_filter_rules
:
for
rule
in
self
.
_cmd_filter_rules
:
action
,
cmd
=
rule
.
match
(
self
.
_input
)
action
,
cmd
=
rule
.
match
(
self
.
_input
)
if
action
==
rule
.
ALLOW
:
if
action
==
rule
.
ALLOW
:
break
break
elif
action
==
rule
.
DENY
:
elif
action
==
rule
.
DENY
:
data
=
char
.
CLEAR_LINE_CHAR
+
b
'
\r
'
msg
=
_
(
"Command `{}` is forbidden ........"
)
.
format
(
cmd
)
msg
=
_
(
"Command `{}` is forbidden ........"
)
.
format
(
cmd
)
msg
=
wr
(
warning
(
msg
.
encode
()),
before
=
1
,
after
=
1
)
self
.
command_forbidden
(
msg
)
self
.
output_data
.
append
(
msg
)
self
.
session
.
send_to_clients
(
msg
)
self
.
session
.
put_command
(
self
.
_input
,
msg
.
decode
())
self
.
session
.
put_replay
(
msg
)
self
.
input_data
.
clean
()
break
break
return
data
return
data
def
command_forbidden
(
self
,
msg
):
data
=
char
.
CLEAR_LINE_CHAR
+
b
'
\r
'
msg
=
wr
(
warning
(
msg
.
encode
()),
before
=
1
,
after
=
1
)
self
.
output_data
.
append
(
msg
)
self
.
session
.
send_to_clients
(
msg
)
self
.
session
.
put_command
(
self
.
_input
,
msg
.
decode
())
self
.
session
.
put_replay
(
msg
)
self
.
input_data
.
clean
()
return
data
def
r_replay_filter
(
self
,
data
):
def
r_replay_filter
(
self
,
data
):
if
not
self
.
_zmodem_state
:
if
not
self
.
_zmodem_state
:
self
.
session
.
put_replay
(
data
)
self
.
session
.
put_replay
(
data
)
...
@@ -382,13 +398,14 @@ class Server(BaseServer):
...
@@ -382,13 +398,14 @@ class Server(BaseServer):
super
(
Server
,
self
)
.
__init__
(
chan
=
chan
)
super
(
Server
,
self
)
.
__init__
(
chan
=
chan
)
def
close
(
self
):
def
close
(
self
):
super
()
.
close
()
super
(
Server
,
self
)
.
close
()
self
.
chan
.
transport
.
close
()
self
.
chan
.
transport
.
close
()
logger
.
debug
(
"Backend server closed"
)
if
self
.
sock
:
if
self
.
sock
:
self
.
sock
.
transport
.
close
()
self
.
sock
.
transport
.
close
()
class
WSProxy
:
class
WSProxy
(
object
)
:
def
__init__
(
self
,
ws
,
client_id
):
def
__init__
(
self
,
ws
,
client_id
):
self
.
ws
=
ws
self
.
ws
=
ws
self
.
client_id
=
client_id
self
.
client_id
=
client_id
...
...
coco/proxy.py
View file @
2444c6fd
...
@@ -52,7 +52,7 @@ class ProxyServer:
...
@@ -52,7 +52,7 @@ class ProxyServer:
return
False
return
False
return
True
return
True
def
manual_s
et_system_user_username_if_need
(
self
):
def
g
et_system_user_username_if_need
(
self
):
if
self
.
system_user
.
login_mode
==
MANUAL_LOGIN
and
\
if
self
.
system_user
.
login_mode
==
MANUAL_LOGIN
and
\
not
self
.
system_user
.
username
:
not
self
.
system_user
.
username
:
username
=
net_input
(
self
.
client
,
prompt
=
'username: '
,
before
=
1
)
username
=
net_input
(
self
.
client
,
prompt
=
'username: '
,
before
=
1
)
...
@@ -63,15 +63,17 @@ class ProxyServer:
...
@@ -63,15 +63,17 @@ class ProxyServer:
def
proxy
(
self
):
def
proxy
(
self
):
if
not
self
.
check_protocol
():
if
not
self
.
check_protocol
():
return
return
self
.
manual_s
et_system_user_username_if_need
()
self
.
g
et_system_user_username_if_need
()
self
.
get_system_user_auth_or_manual_set
()
self
.
get_system_user_auth_or_manual_set
()
self
.
server
=
self
.
get_server_conn
()
self
.
server
=
self
.
get_server_conn
()
if
self
.
server
is
None
:
if
self
.
server
is
None
:
return
return
session
=
Session
.
new_session
(
self
.
client
,
self
.
server
)
session
=
Session
.
new_session
(
self
.
client
,
self
.
server
)
session
.
bridge
()
try
:
Session
.
remove_session
(
session
.
id
)
session
.
bridge
()
self
.
server
.
close
()
finally
:
Session
.
remove_session
(
session
.
id
)
self
.
server
.
close
()
def
validate_permission
(
self
):
def
validate_permission
(
self
):
"""
"""
...
@@ -83,7 +85,7 @@ class ProxyServer:
...
@@ -83,7 +85,7 @@ class ProxyServer:
)
)
def
get_server_conn
(
self
):
def
get_server_conn
(
self
):
logger
.
info
(
"Connect to {}
"
.
format
(
self
.
asset
.
hostname
))
logger
.
info
(
"Connect to {}
:{} ..."
.
format
(
self
.
asset
.
hostname
,
self
.
asset
.
port
))
self
.
send_connecting_message
()
self
.
send_connecting_message
()
if
not
self
.
validate_permission
():
if
not
self
.
validate_permission
():
self
.
client
.
send
(
warning
(
_
(
'No permission'
)))
self
.
client
.
send
(
warning
(
_
(
'No permission'
)))
...
...
coco/recorder.py
View file @
2444c6fd
...
@@ -21,12 +21,12 @@ logger = get_logger(__file__)
...
@@ -21,12 +21,12 @@ logger = get_logger(__file__)
BUF_SIZE
=
1024
BUF_SIZE
=
1024
class
ReplayRecorder
(
metaclass
=
abc
.
ABCMeta
):
class
ReplayRecorder
(
object
):
time_start
=
None
time_start
=
None
storage
=
None
storage
=
None
def
__init__
(
self
):
def
__init__
(
self
):
super
()
.
__init__
()
super
(
ReplayRecorder
,
self
)
.
__init__
()
self
.
file
=
None
self
.
file
=
None
self
.
file_path
=
None
self
.
file_path
=
None
self
.
get_storage
()
self
.
get_storage
()
...
@@ -82,7 +82,7 @@ class ReplayRecorder(metaclass=abc.ABCMeta):
...
@@ -82,7 +82,7 @@ class ReplayRecorder(metaclass=abc.ABCMeta):
self
.
upload_replay
(
session_id
,
times
-
1
)
self
.
upload_replay
(
session_id
,
times
-
1
)
else
:
else
:
msg
=
'Success push replay file: {}'
.
format
(
session_id
)
msg
=
'Success push replay file: {}'
.
format
(
session_id
)
logger
.
info
(
msg
)
logger
.
debug
(
msg
)
self
.
finish_replay
(
3
,
session_id
)
self
.
finish_replay
(
3
,
session_id
)
os
.
unlink
(
self
.
file_path
)
os
.
unlink
(
self
.
file_path
)
return
True
return
True
...
@@ -101,23 +101,32 @@ class ReplayRecorder(metaclass=abc.ABCMeta):
...
@@ -101,23 +101,32 @@ class ReplayRecorder(metaclass=abc.ABCMeta):
if
app_service
.
finish_replay
(
session_id
):
if
app_service
.
finish_replay
(
session_id
):
logger
.
info
(
logger
.
info
(
"Success finish session {}'s replay "
.
format
(
session_id
)
"Success finish
ed
session {}'s replay "
.
format
(
session_id
)
)
)
return
True
return
True
else
:
else
:
msg
=
"Failed finish session {}'s replay, try {} times"
msg
=
"Failed finish
ed
session {}'s replay, try {} times"
logger
.
error
(
msg
.
format
(
session_id
,
times
))
logger
.
error
(
msg
.
format
(
session_id
,
times
))
return
self
.
finish_replay
(
times
-
1
,
session_id
)
return
self
.
finish_replay
(
times
-
1
,
session_id
)
class
CommandRecorder
(
metaclass
=
Singleton
):
class
CommandRecorder
(
object
):
batch_size
=
10
batch_size
=
10
timeout
=
5
timeout
=
5
no
=
0
no
=
0
storage
=
None
storage
=
None
_cache
=
[]
def
__new__
(
cls
,
*
args
,
**
kwargs
):
if
cls
.
_cache
:
return
cls
.
_cache
[
0
]
else
:
self
=
super
(
CommandRecorder
,
cls
)
.
__new__
(
cls
,
*
args
,
**
kwargs
)
cls
.
_cache
.
append
(
self
)
return
self
def
__init__
(
self
):
def
__init__
(
self
):
super
()
.
__init__
()
super
(
CommandRecorder
,
self
)
.
__init__
()
self
.
queue
=
MemoryQueue
()
self
.
queue
=
MemoryQueue
()
self
.
stop_evt
=
threading
.
Event
()
self
.
stop_evt
=
threading
.
Event
()
self
.
push_to_server_async
()
self
.
push_to_server_async
()
...
...
coco/session.py
View file @
2444c6fd
...
@@ -3,9 +3,13 @@
...
@@ -3,9 +3,13 @@
#
#
import
uuid
import
uuid
import
datetime
import
datetime
import
selectors
import
time
import
time
try
:
import
selectors
except
ImportError
:
import
selectors2
as
selectors
from
.utils
import
get_logger
,
wrap_with_warning
as
warn
,
\
from
.utils
import
get_logger
,
wrap_with_warning
as
warn
,
\
wrap_with_line_feed
as
wr
,
ugettext
as
_
,
ignore_error
wrap_with_line_feed
as
wr
,
ugettext
as
_
,
ignore_error
from
.service
import
app_service
from
.service
import
app_service
...
@@ -68,14 +72,14 @@ class Session:
...
@@ -68,14 +72,14 @@ class Session:
:param silent: If true not send welcome message
:param silent: If true not send welcome message
:return:
:return:
"""
"""
logger
.
info
(
"Session add watcher: {} -> {} "
.
format
(
self
.
id
,
watcher
))
logger
.
debug
(
"Session add watcher: {} -> {} "
.
format
(
self
.
id
,
watcher
))
if
not
silent
:
if
not
silent
:
watcher
.
send
(
"Welcome to watch session {}
\r\n
"
.
format
(
self
.
id
)
.
encode
())
watcher
.
send
(
"Welcome to watch session {}
\r\n
"
.
format
(
self
.
id
)
.
encode
())
self
.
sel
.
register
(
watcher
,
selectors
.
EVENT_READ
)
self
.
sel
.
register
(
watcher
,
selectors
.
EVENT_READ
)
self
.
_watchers
.
append
(
watcher
)
self
.
_watchers
.
append
(
watcher
)
def
remove_watcher
(
self
,
watcher
):
def
remove_watcher
(
self
,
watcher
):
logger
.
info
(
"Session
%
s remove watcher
%
s"
%
(
self
.
id
,
watcher
))
logger
.
debug
(
"Session
%
s remove watcher
%
s"
%
(
self
.
id
,
watcher
))
self
.
sel
.
unregister
(
watcher
)
self
.
sel
.
unregister
(
watcher
)
self
.
_watchers
.
remove
(
watcher
)
self
.
_watchers
.
remove
(
watcher
)
...
@@ -86,7 +90,7 @@ class Session:
...
@@ -86,7 +90,7 @@ class Session:
:param silent: If true not send welcome message
:param silent: If true not send welcome message
:return:
:return:
"""
"""
logger
.
info
(
"Session
%
s add share
%
s"
%
(
self
.
id
,
sharer
))
logger
.
debug
(
"Session
%
s add share
%
s"
%
(
self
.
id
,
sharer
))
if
not
silent
:
if
not
silent
:
sharer
.
send
(
"Welcome to join session: {}
\r\n
"
sharer
.
send
(
"Welcome to join session: {}
\r\n
"
.
format
(
self
.
id
)
.
encode
(
"utf-8"
))
.
format
(
self
.
id
)
.
encode
(
"utf-8"
))
...
@@ -190,7 +194,7 @@ class Session:
...
@@ -190,7 +194,7 @@ class Session:
break
break
elif
sock
==
self
.
client
.
change_size_evt
:
elif
sock
==
self
.
client
.
change_size_evt
:
self
.
resize_win_size
()
self
.
resize_win_size
()
logger
.
info
(
"Session stop event set: {}"
.
format
(
self
.
id
))
logger
.
debug
(
"Session stop event set: {}"
.
format
(
self
.
id
))
def
resize_win_size
(
self
):
def
resize_win_size
(
self
):
width
,
height
=
self
.
client
.
request
.
meta
[
'width'
],
\
width
,
height
=
self
.
client
.
request
.
meta
[
'width'
],
\
...
@@ -201,7 +205,7 @@ class Session:
...
@@ -201,7 +205,7 @@ class Session:
@ignore_error
@ignore_error
def
close
(
self
):
def
close
(
self
):
if
self
.
closed
:
if
self
.
closed
:
logger
.
info
(
"Session has been closed: {} "
.
format
(
self
.
id
))
logger
.
debug
(
"Session has been closed: {} "
.
format
(
self
.
id
))
return
return
logger
.
info
(
"Close the session: {} "
.
format
(
self
.
id
))
logger
.
info
(
"Close the session: {} "
.
format
(
self
.
id
))
self
.
is_finished
=
True
self
.
is_finished
=
True
...
@@ -228,6 +232,3 @@ class Session:
...
@@ -228,6 +232,3 @@ class Session:
def
__repr__
(
self
):
def
__repr__
(
self
):
return
self
.
id
return
self
.
id
# def __del__(self):
# print("GC: Session object has been GC")
coco/sftp.py
View file @
2444c6fd
...
@@ -62,10 +62,11 @@ class SFTPServer(paramiko.SFTPServerInterface):
...
@@ -62,10 +62,11 @@ class SFTPServer(paramiko.SFTPServerInterface):
}
}
"""
"""
super
()
.
__init__
(
server
,
**
kwargs
)
super
(
SFTPServer
,
self
)
.
__init__
(
server
,
**
kwargs
)
self
.
server
=
server
self
.
server
=
server
self
.
_sftp
=
{}
self
.
_sftp
=
{}
self
.
hosts
=
self
.
get_permed_hosts
()
self
.
hosts
=
self
.
get_permed_hosts
()
self
.
is_finished
=
False
def
get_permed_hosts
(
self
):
def
get_permed_hosts
(
self
):
hosts
=
{}
hosts
=
{}
...
@@ -88,7 +89,8 @@ class SFTPServer(paramiko.SFTPServerInterface):
...
@@ -88,7 +89,8 @@ class SFTPServer(paramiko.SFTPServerInterface):
return
hosts
return
hosts
def
session_ended
(
self
):
def
session_ended
(
self
):
super
()
.
session_ended
()
self
.
is_finished
=
True
super
(
SFTPServer
,
self
)
.
session_ended
()
for
_
,
v
in
self
.
_sftp
.
items
():
for
_
,
v
in
self
.
_sftp
.
items
():
sftp
=
v
[
'client'
]
sftp
=
v
[
'client'
]
proxy
=
v
.
get
(
'proxy'
)
proxy
=
v
.
get
(
'proxy'
)
...
@@ -144,7 +146,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
...
@@ -144,7 +146,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
if
len
(
data
)
==
1
and
not
data
[
0
]:
if
len
(
data
)
==
1
and
not
data
[
0
]:
return
request
return
request
host
,
*
path
=
data
host
,
path
=
data
[
0
],
data
[
1
:]
request
[
"host"
]
=
host
request
[
"host"
]
=
host
unique
,
su
=
self
.
host_has_unique_su
(
host
)
unique
,
su
=
self
.
host_has_unique_su
(
host
)
if
unique
:
if
unique
:
...
@@ -388,7 +390,7 @@ class InternalSFTPClient(SFTPServer):
...
@@ -388,7 +390,7 @@ class InternalSFTPClient(SFTPServer):
def
__init__
(
self
,
connection
):
def
__init__
(
self
,
connection
):
fake_server
=
FakeServer
()
fake_server
=
FakeServer
()
fake_server
.
connection
=
connection
fake_server
.
connection
=
connection
super
()
.
__init__
(
fake_server
)
super
(
InternalSFTPClient
,
self
)
.
__init__
(
fake_server
)
def
listdir_attr
(
self
,
path
):
def
listdir_attr
(
self
,
path
):
return
self
.
list_folder
.
__wrapped__
(
self
,
path
)
return
self
.
list_folder
.
__wrapped__
(
self
,
path
)
...
@@ -408,15 +410,15 @@ class InternalSFTPClient(SFTPServer):
...
@@ -408,15 +410,15 @@ class InternalSFTPClient(SFTPServer):
self
.
create_ftp_log
(
path
,
operate
,
success
)
self
.
create_ftp_log
(
path
,
operate
,
success
)
def
stat
(
self
,
path
):
def
stat
(
self
,
path
):
attr
=
super
()
.
stat
.
__wrapped__
(
self
,
path
)
attr
=
super
(
InternalSFTPClient
,
self
)
.
stat
.
__wrapped__
(
self
,
path
)
return
attr
return
attr
def
lstat
(
self
,
path
):
def
lstat
(
self
,
path
):
attr
=
super
()
.
lstat
.
__wrapped__
(
self
,
path
)
attr
=
super
(
InternalSFTPClient
,
self
)
.
lstat
.
__wrapped__
(
self
,
path
)
return
attr
return
attr
def
rmdir
(
self
,
path
):
def
rmdir
(
self
,
path
):
return
super
()
.
rmdir
.
__wrapped__
(
self
,
path
)
return
super
(
InternalSFTPClient
,
self
)
.
rmdir
.
__wrapped__
(
self
,
path
)
def
get_channel
(
self
):
def
get_channel
(
self
):
return
FakeChannel
.
new
()
return
FakeChannel
.
new
()
...
...
coco/sshd.py
View file @
2444c6fd
...
@@ -5,6 +5,7 @@
...
@@ -5,6 +5,7 @@
import
os
import
os
import
socket
import
socket
import
threading
import
threading
import
time
import
paramiko
import
paramiko
...
@@ -25,7 +26,6 @@ class SSHServer:
...
@@ -25,7 +26,6 @@ class SSHServer:
self
.
stop_evt
=
threading
.
Event
()
self
.
stop_evt
=
threading
.
Event
()
self
.
workers
=
[]
self
.
workers
=
[]
self
.
pipe
=
None
self
.
pipe
=
None
self
.
connections
=
[]
@property
@property
def
host_key
(
self
):
def
host_key
(
self
):
...
@@ -57,11 +57,6 @@ class SSHServer:
...
@@ -57,11 +57,6 @@ class SSHServer:
except
IndexError
as
e
:
except
IndexError
as
e
:
logger
.
error
(
"Start SSH server error: {}"
.
format
(
e
))
logger
.
error
(
"Start SSH server error: {}"
.
format
(
e
))
def
new_connection
(
self
,
addr
,
sock
):
connection
=
Connection
.
new_connection
(
addr
=
addr
,
sock
=
sock
)
self
.
connections
.
append
(
connection
)
return
connection
def
handle_connection
(
self
,
sock
,
addr
):
def
handle_connection
(
self
,
sock
,
addr
):
logger
.
debug
(
"Handle new connection from: {}"
.
format
(
addr
))
logger
.
debug
(
"Handle new connection from: {}"
.
format
(
addr
))
transport
=
paramiko
.
Transport
(
sock
,
gss_kex
=
False
)
transport
=
paramiko
.
Transport
(
sock
,
gss_kex
=
False
)
...
@@ -74,55 +69,60 @@ class SSHServer:
...
@@ -74,55 +69,60 @@ class SSHServer:
transport
.
set_subsystem_handler
(
transport
.
set_subsystem_handler
(
'sftp'
,
paramiko
.
SFTPServer
,
SFTPServer
'sftp'
,
paramiko
.
SFTPServer
,
SFTPServer
)
)
connection
=
self
.
new_connection
(
addr
,
sock
=
sock
)
connection
=
Connection
.
new_connection
(
addr
=
addr
,
sock
=
sock
)
server
=
SSHInterface
(
connection
)
server
=
SSHInterface
(
connection
)
try
:
try
:
transport
.
start_server
(
server
=
server
)
transport
.
start_server
(
server
=
server
)
except
(
paramiko
.
SSHException
,
socket
.
timeout
):
while
transport
.
is_active
():
chan
=
transport
.
accept
()
server
.
event
.
wait
(
5
)
if
chan
is
None
:
continue
if
not
server
.
event
.
is_set
():
logger
.
warning
(
"Client not request invalid, exiting"
)
sock
.
close
()
continue
else
:
server
.
event
.
clear
()
client
=
connection
.
clients
.
get
(
chan
.
get_id
())
client
.
chan
=
chan
t
=
threading
.
Thread
(
target
=
self
.
dispatch
,
args
=
(
client
,))
t
.
daemon
=
True
t
.
start
()
transport
.
close
()
except
(
paramiko
.
SSHException
,
sock
.
timeout
):
logger
.
warning
(
"SSH negotiation failed"
)
logger
.
warning
(
"SSH negotiation failed"
)
return
except
EOFError
as
e
:
except
EOFError
as
e
:
logger
.
warning
(
"Handle EOF Error: {}"
.
format
(
e
))
logger
.
warning
(
"Handle EOF Error: {}"
.
format
(
e
))
return
finally
:
while
transport
.
is_active
():
Connection
.
remove_connection
(
connection
.
id
)
chan
=
transport
.
accept
()
sock
.
close
()
server
.
event
.
wait
(
5
)
if
chan
is
None
:
continue
if
not
server
.
event
.
is_set
():
logger
.
warning
(
"Client not request invalid, exiting"
)
sock
.
close
()
return
else
:
server
.
event
.
clear
()
client
=
connection
.
clients
.
get
(
chan
.
get_id
())
client
.
chan
=
chan
t
=
threading
.
Thread
(
target
=
self
.
dispatch
,
args
=
(
client
,))
t
.
daemon
=
True
t
.
start
()
Connection
.
remove_connection
(
connection
.
id
)
@staticmethod
@staticmethod
def
dispatch
(
client
):
def
dispatch
(
client
):
supported
=
{
'pty'
,
'x11'
,
'forward-agent'
}
supported
=
{
'pty'
,
'x11'
,
'forward-agent'
}
chan_type
=
client
.
request
.
type
chan_type
=
client
.
request
.
type
kind
=
client
.
request
.
kind
kind
=
client
.
request
.
kind
if
kind
==
'session'
and
chan_type
in
supported
:
try
:
logger
.
info
(
"Request type `{}:{}`, dispatch to interactive mode"
.
format
(
kind
,
chan_type
))
if
kind
==
'session'
and
chan_type
in
supported
:
try
:
logger
.
info
(
"Dispatch client to interactive mode"
)
InteractiveServer
(
client
)
.
interact
()
try
:
except
Exception
as
e
:
InteractiveServer
(
client
)
.
interact
()
logger
.
error
(
"Unexpected error occur: {}"
.
format
(
e
))
except
IndexError
as
e
:
logger
.
error
(
"Unexpected error occur: {}"
.
format
(
e
))
elif
chan_type
==
'subsystem'
:
while
not
client
.
closed
:
time
.
sleep
(
5
)
logger
.
debug
(
"SFTP session finished"
)
else
:
msg
=
"Request type `{}:{}` not support now"
.
format
(
kind
,
chan_type
)
logger
.
error
(
msg
)
client
.
send
(
msg
)
finally
:
connection
=
Connection
.
get_connection
(
client
.
connection_id
)
connection
=
Connection
.
get_connection
(
client
.
connection_id
)
connection
.
remove_client
(
client
.
id
)
connection
.
remove_client
(
client
.
id
)
elif
chan_type
==
'subsystem'
:
pass
else
:
msg
=
"Request type `{}:{}` not support now"
.
format
(
kind
,
chan_type
)
logger
.
info
(
msg
)
client
.
send
(
msg
)
def
shutdown
(
self
):
def
shutdown
(
self
):
self
.
stop_evt
.
set
()
self
.
stop_evt
.
set
()
...
...
coco/struct.py
View file @
2444c6fd
...
@@ -21,7 +21,7 @@ class MultiQueueMixin:
...
@@ -21,7 +21,7 @@ class MultiQueueMixin:
self
.
put
(
i
)
self
.
put
(
i
)
class
MemoryQueue
(
MultiQueueMixin
,
queue
.
Queue
):
class
MemoryQueue
(
MultiQueueMixin
,
queue
.
Queue
,
object
):
pass
pass
...
@@ -29,11 +29,11 @@ class SizedList(list):
...
@@ -29,11 +29,11 @@ class SizedList(list):
def
__init__
(
self
,
maxsize
=
0
):
def
__init__
(
self
,
maxsize
=
0
):
self
.
maxsize
=
maxsize
self
.
maxsize
=
maxsize
self
.
size
=
0
self
.
size
=
0
super
()
.
__init__
()
super
(
list
,
self
)
.
__init__
()
def
append
(
self
,
b
):
def
append
(
self
,
b
):
if
self
.
maxsize
==
0
or
self
.
size
<
self
.
maxsize
:
if
self
.
maxsize
==
0
or
self
.
size
<
self
.
maxsize
:
super
()
.
append
(
b
)
super
(
SizedList
,
self
)
.
append
(
b
)
self
.
size
+=
len
(
b
)
self
.
size
+=
len
(
b
)
def
clean
(
self
):
def
clean
(
self
):
...
...
coco/utils.py
View file @
2444c6fd
...
@@ -12,7 +12,6 @@ from io import StringIO
...
@@ -12,7 +12,6 @@ from io import StringIO
from
binascii
import
hexlify
from
binascii
import
hexlify
from
werkzeug.local
import
Local
,
LocalProxy
from
werkzeug.local
import
Local
,
LocalProxy
from
functools
import
partial
,
wraps
from
functools
import
partial
,
wraps
import
builtins
import
paramiko
import
paramiko
import
pyte
import
pyte
...
@@ -294,7 +293,7 @@ def get_logger(file_name):
...
@@ -294,7 +293,7 @@ def get_logger(file_name):
return
logging
.
getLogger
(
'coco.'
+
file_name
)
return
logging
.
getLogger
(
'coco.'
+
file_name
)
def
net_input
(
client
,
prompt
=
'Opt> '
,
sensitive
=
False
,
before
=
0
,
after
=
0
,
only_one_char
=
False
):
def
net_input
(
client
,
prompt
=
'Opt> '
,
sensitive
=
False
,
before
=
0
,
after
=
0
):
"""实现了一个ssh input, 提示用户输入, 获取并返回
"""实现了一个ssh input, 提示用户输入, 获取并返回
:return user input string
:return user input string
...
@@ -303,12 +302,8 @@ def net_input(client, prompt='Opt> ', sensitive=False, before=0, after=0, only_o
...
@@ -303,12 +302,8 @@ def net_input(client, prompt='Opt> ', sensitive=False, before=0, after=0, only_o
parser
=
TtyIOParser
()
parser
=
TtyIOParser
()
client
.
send
(
wrap_with_line_feed
(
prompt
,
before
=
before
,
after
=
after
))
client
.
send
(
wrap_with_line_feed
(
prompt
,
before
=
before
,
after
=
after
))
if
only_one_char
:
data
=
client
.
recv
(
1
)
return
data
.
decode
()
while
True
:
while
True
:
data
=
client
.
recv
(
1
0
)
data
=
client
.
recv
(
1
)
if
len
(
data
)
==
0
:
if
len
(
data
)
==
0
:
break
break
# Client input backspace
# Client input backspace
...
@@ -336,18 +331,8 @@ def net_input(client, prompt='Opt> ', sensitive=False, before=0, after=0, only_o
...
@@ -336,18 +331,8 @@ def net_input(client, prompt='Opt> ', sensitive=False, before=0, after=0, only_o
client
.
send
(
b
''
)
client
.
send
(
b
''
)
continue
continue
# handle shell expect
multi_char_with_enter
=
False
if
len
(
data
)
>
1
and
data
[
-
1
]
in
char
.
ENTER_CHAR_ORDER
:
if
sensitive
:
client
.
send
(
len
(
data
)
*
'*'
)
else
:
client
.
send
(
data
)
input_data
.
append
(
data
[:
-
1
])
multi_char_with_enter
=
True
# If user types ENTER we should get user input
# If user types ENTER we should get user input
if
data
in
char
.
ENTER_CHAR
or
multi_char_with_enter
:
if
data
in
char
.
ENTER_CHAR
:
client
.
send
(
wrap_with_line_feed
(
b
''
,
after
=
2
))
client
.
send
(
wrap_with_line_feed
(
b
''
,
after
=
2
))
option
=
parser
.
parse_input
(
input_data
)
option
=
parser
.
parse_input
(
input_data
)
del
input_data
[:]
del
input_data
[:]
...
@@ -435,11 +420,17 @@ def get_current_lang(attr):
...
@@ -435,11 +420,17 @@ def get_current_lang(attr):
def
_gettext
(
lang
):
def
_gettext
(
lang
):
import
builtins
if
lang
==
'en'
:
if
lang
==
'en'
:
trans_en
.
install
()
trans_en
.
install
()
else
:
else
:
trans_zh
.
install
()
trans_zh
.
install
()
return
builtins
.
__dict__
[
'_'
]
try
:
return
builtins
.
__dict__
[
'_'
]
except
KeyError
:
def
_f
(
x
):
return
x
return
_f
def
_find
(
attr
):
def
_find
(
attr
):
...
...
cocod
View file @
2444c6fd
#!/usr/bin/env python3
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
import
os
import
eventlet
if
os
.
environ
.
get
(
'USE_EVENTLET'
,
'1'
)
==
'1'
:
from
eventlet.debug
import
hub_prevent_multiple_readers
import
eventlet
eventlet
.
monkey_patch
()
from
eventlet.debug
import
hub_prevent_multiple_readers
hub_prevent_multiple_readers
(
False
)
eventlet
.
monkey_patch
()
hub_prevent_multiple_readers
(
False
)
print
(
"Use eventlet dispatch"
)
else
:
print
(
"Use local threading"
)
import
os
import
sys
import
sys
import
argparse
import
argparse
import
time
import
time
...
@@ -15,7 +19,8 @@ import signal
...
@@ -15,7 +19,8 @@ import signal
dirs
=
(
'logs'
,
'keys'
)
dirs
=
(
'logs'
,
'keys'
)
for
d
in
dirs
:
for
d
in
dirs
:
os
.
makedirs
(
d
,
exist_ok
=
True
)
if
not
os
.
path
.
isdir
(
d
):
os
.
makedirs
(
d
)
from
coco
import
Coco
from
coco
import
Coco
...
...
locale/en/LC_MESSAGES/coco.mo
View file @
2444c6fd
No preview for this file type
locale/en/LC_MESSAGES/coco.po
View file @
2444c6fd
...
@@ -7,7 +7,7 @@ msgid ""
...
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-12-
18 20:03
+0800\n"
"POT-Creation-Date: 2018-12-
21 16:48
+0800\n"
"PO-Revision-Date: 2018-08-10 10:42+0800\n"
"PO-Revision-Date: 2018-08-10 10:42+0800\n"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Language-Team: Language locale/en/LC\n"
"Language-Team: Language locale/en/LC\n"
...
@@ -84,75 +84,80 @@ msgstr ""
...
@@ -84,75 +84,80 @@ msgstr ""
msgid "Terminal does not support login rdp, please use web terminal to access"
msgid "Terminal does not support login rdp, please use web terminal to access"
msgstr ""
msgstr ""
#: coco/interactive.py:21
2
#: coco/interactive.py:21
7
msgid "No Assets"
msgid "No Assets"
msgstr ""
msgstr ""
#: coco/interactive.py:2
75
#: coco/interactive.py:2
80
msgid "Tips: Enter the asset ID and log directly into the asset."
msgid "Tips: Enter the asset ID and log directly into the asset."
msgstr ""
msgstr ""
#: coco/interactive.py:2
76
#: coco/interactive.py:2
81
msgid "Page up: P/p"
msgid "Page up: P/p"
msgstr ""
msgstr ""
#: coco/interactive.py:2
77
#: coco/interactive.py:2
82
msgid "Page down: Enter|N/n"
msgid "Page down: Enter|N/n"
msgstr ""
msgstr ""
#: coco/interactive.py:2
78
#: coco/interactive.py:2
83
msgid "BACK: b/q"
msgid "BACK: b/q"
msgstr ""
msgstr ""
#: coco/interactive.py:
299
#: coco/interactive.py:
303
msgid "ID"
msgid "ID"
msgstr ""
msgstr ""
#: coco/interactive.py:
299
#: coco/interactive.py:
303
msgid "Hostname"
msgid "Hostname"
msgstr ""
msgstr ""
#: coco/interactive.py:
299
#: coco/interactive.py:
303
msgid "IP"
msgid "IP"
msgstr ""
msgstr ""
#: coco/interactive.py:
299
#: coco/interactive.py:
303
msgid "LoginAs"
msgid "LoginAs"
msgstr ""
msgstr ""
#: coco/interactive.py:31
3
#: coco/interactive.py:31
7
msgid "Comment"
msgid "Comment"
msgstr ""
msgstr ""
#: coco/interactive.py:32
2
#: coco/interactive.py:32
6
msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgstr ""
msgstr ""
#: coco/interactive.py:39
4
#: coco/interactive.py:39
8
msgid "No Nodes"
msgid "No Nodes"
msgstr ""
msgstr ""
#: coco/interactive.py:
398
#: coco/interactive.py:
402
msgid "Node: [ ID.Name(Asset amount) ]"
msgid "Node: [ ID.Name(Asset amount) ]"
msgstr ""
msgstr ""
#: coco/interactive.py:40
0
#: coco/interactive.py:40
4
msgid "Tips: Enter g+NodeID to display the host under the node, such as g1"
msgid "Tips: Enter g+NodeID to display the host under the node, such as g1"
msgstr ""
msgstr ""
#: coco/interactive.py:4
08
#: coco/interactive.py:4
12
msgid "There is no matched node, please re-enter"
msgid "There is no matched node, please re-enter"
msgstr ""
msgstr ""
#: coco/interactive.py:4
38
#: coco/interactive.py:4
42
msgid "Select a login:: "
msgid "Select a login:: "
msgstr ""
msgstr ""
#: coco/interactive.py:46
1
#: coco/interactive.py:46
5
msgid "No system user"
msgid "No system user"
msgstr ""
msgstr ""
#: coco/models.py:247
#: coco/models.py:242
msgid ""
"Warning: Failed to load filter rule, please press Ctrl + D to exit retry."
msgstr ""
#: coco/models.py:251
msgid "Command `{}` is forbidden ........"
msgid "Command `{}` is forbidden ........"
msgstr ""
msgstr ""
...
...
locale/zh_CN/LC_MESSAGES/coco.mo
View file @
2444c6fd
No preview for this file type
locale/zh_CN/LC_MESSAGES/coco.po
View file @
2444c6fd
...
@@ -7,7 +7,7 @@ msgid ""
...
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-12-
18 20:04
+0800\n"
"POT-Creation-Date: 2018-12-
21 16:48
+0800\n"
"PO-Revision-Date: 2018-08-10 10:42+0800\n"
"PO-Revision-Date: 2018-08-10 10:42+0800\n"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Language-Team: Language locale/zh\n"
"Language-Team: Language locale/zh\n"
...
@@ -91,75 +91,80 @@ msgstr "{T}0) 输入 {green}q{end} 退出.{R}"
...
@@ -91,75 +91,80 @@ msgstr "{T}0) 输入 {green}q{end} 退出.{R}"
msgid "Terminal does not support login rdp, please use web terminal to access"
msgid "Terminal does not support login rdp, please use web terminal to access"
msgstr "终端不支持登录windows, 请使用web terminal访问"
msgstr "终端不支持登录windows, 请使用web terminal访问"
#: coco/interactive.py:21
2
#: coco/interactive.py:21
7
msgid "No Assets"
msgid "No Assets"
msgstr "没有资产"
msgstr "没有资产"
#: coco/interactive.py:2
75
#: coco/interactive.py:2
80
msgid "Tips: Enter the asset ID and log directly into the asset."
msgid "Tips: Enter the asset ID and log directly into the asset."
msgstr "提示: 输入资产ID,直接登录资产."
msgstr "提示: 输入资产ID,直接登录资产."
#: coco/interactive.py:2
76
#: coco/interactive.py:2
81
msgid "Page up: P/p"
msgid "Page up: P/p"
msgstr "上一页: P/p"
msgstr "上一页: P/p"
#: coco/interactive.py:2
77
#: coco/interactive.py:2
82
msgid "Page down: Enter|N/n"
msgid "Page down: Enter|N/n"
msgstr "下一页: Enter|N/n"
msgstr "下一页: Enter|N/n"
#: coco/interactive.py:2
78
#: coco/interactive.py:2
83
msgid "BACK: b/q"
msgid "BACK: b/q"
msgstr "返回: B/b"
msgstr "返回: B/b"
#: coco/interactive.py:
299
#: coco/interactive.py:
303
msgid "ID"
msgid "ID"
msgstr ""
msgstr ""
#: coco/interactive.py:
299
#: coco/interactive.py:
303
msgid "Hostname"
msgid "Hostname"
msgstr "主机名"
msgstr "主机名"
#: coco/interactive.py:
299
#: coco/interactive.py:
303
msgid "IP"
msgid "IP"
msgstr ""
msgstr ""
#: coco/interactive.py:
299
#: coco/interactive.py:
303
msgid "LoginAs"
msgid "LoginAs"
msgstr "登录用户"
msgstr "登录用户"
#: coco/interactive.py:31
3
#: coco/interactive.py:31
7
msgid "Comment"
msgid "Comment"
msgstr "备注"
msgstr "备注"
#: coco/interactive.py:32
2
#: coco/interactive.py:32
6
msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgstr "页码: {}, 数量: {}, 总页数: {}, 总数量: {}"
msgstr "页码: {}, 数量: {}, 总页数: {}, 总数量: {}"
#: coco/interactive.py:39
4
#: coco/interactive.py:39
8
msgid "No Nodes"
msgid "No Nodes"
msgstr "没有节点"
msgstr "没有节点"
#: coco/interactive.py:
398
#: coco/interactive.py:
402
msgid "Node: [ ID.Name(Asset amount) ]"
msgid "Node: [ ID.Name(Asset amount) ]"
msgstr "节点: [ ID.名称(资产数量) ]"
msgstr "节点: [ ID.名称(资产数量) ]"
#: coco/interactive.py:40
0
#: coco/interactive.py:40
4
msgid "Tips: Enter g+NodeID to display the host under the node, such as g1"
msgid "Tips: Enter g+NodeID to display the host under the node, such as g1"
msgstr "提示: 输入 g+节点ID 显示节点下主机. 如: g1"
msgstr "提示: 输入 g+节点ID 显示节点下主机. 如: g1"
#: coco/interactive.py:4
08
#: coco/interactive.py:4
12
msgid "There is no matched node, please re-enter"
msgid "There is no matched node, please re-enter"
msgstr "没有匹配分组,请重新输入"
msgstr "没有匹配分组,请重新输入"
#: coco/interactive.py:4
38
#: coco/interactive.py:4
42
msgid "Select a login:: "
msgid "Select a login:: "
msgstr "选择一个登录:"
msgstr "选择一个登录:"
#: coco/interactive.py:46
1
#: coco/interactive.py:46
5
msgid "No system user"
msgid "No system user"
msgstr "没有系统用户"
msgstr "没有系统用户"
#: coco/models.py:247
#: coco/models.py:242
msgid ""
"Warning: Failed to load filter rule, please press Ctrl + D to exit retry."
msgstr "警告: 加载过滤规则失败,请按 Ctrl + D 退出重试."
#: coco/models.py:251
msgid "Command `{}` is forbidden ........"
msgid "Command `{}` is forbidden ........"
msgstr "命令 `{}` 是被禁止的 ..."
msgstr "命令 `{}` 是被禁止的 ..."
...
...
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