Commit 151537dc authored by BaiJiangJie's avatar BaiJiangJie Committed by 老广

[Feature] ssh、sftp 添加action权限校验(控制用户连接、上传、下载等操作) (#207)

* [Feature] 1. check actions - 用户对资产进行操作(连接、上传、下载)之前,校验actions权限

* [Feature] 2. check actions - 优化actions校验逻辑,按照download和upload分类,限制相关一系列操作

* [Bugfix] 3. check actions - 修复luna上传目录时path join失败的bug, 优化actions校验逻辑

* [Bugfix] 4. check actions - 修改小细节

* [Feature] 5. check actions - 修改校验逻辑(从请求API校验修改为本地校验)
parent 23a5574a
# -*- coding: utf-8 -*-
#
#
# Permission actions choices
#
PERMS_ACTION_NAME_ALL = 'all'
PERMS_ACTION_NAME_CONNECT = 'connect'
PERMS_ACTION_NAME_UPLOAD_FILE = 'upload_file'
PERMS_ACTION_NAME_DOWNLOAD_FILE = 'download_file'
...@@ -109,8 +109,8 @@ class ElFinderConnector: ...@@ -109,8 +109,8 @@ class ElFinderConnector:
try: try:
func() func()
except Exception as e: except Exception as e:
logger.debug(e, exc_info=True)
self.response['error'] = '%s' % e self.response['error'] = '%s' % e
logger.error(e, exc_info=True)
def get_request_data(self): def get_request_data(self):
data_source = {} data_source = {}
......
...@@ -140,6 +140,7 @@ class SFTPVolume(BaseVolume): ...@@ -140,6 +140,7 @@ class SFTPVolume(BaseVolume):
if not many: if not many:
names = [names] names = [names]
for name in names: for name in names:
name = name.lstrip(self.path_sep)
path = self._join(parent_path, name) path = self._join(parent_path, name)
remote_path = self._remote_path(path) remote_path = self._remote_path(path)
self.sftp.mkdir(remote_path) self.sftp.mkdir(remote_path)
...@@ -214,14 +215,11 @@ class SFTPVolume(BaseVolume): ...@@ -214,14 +215,11 @@ class SFTPVolume(BaseVolume):
""" Delete a File or Directory object. """ """ Delete a File or Directory object. """
path = self._path(target) path = self._path(target)
remote_path = self._remote_path(path) remote_path = self._remote_path(path)
try: info = self.info(target)
info = self.info(target) if info['mime'] == 'directory':
if info['mime'] == 'directory': self.sftp.rmdir(remote_path)
self.sftp.rmdir(remote_path) else:
else: self.sftp.unlink(remote_path)
self.sftp.unlink(remote_path)
except OSError:
raise OSError("Delete {} failed".format(self._base_name(path)))
return target return target
def upload_as_url(self, url, parent): def upload_as_url(self, url, parent):
......
...@@ -7,6 +7,7 @@ import time ...@@ -7,6 +7,7 @@ import time
from .session import Session from .session import Session
from .models import Server, TelnetServer from .models import Server, TelnetServer
from .const import PERMS_ACTION_NAME_CONNECT
from .connection import SSHConnection, TelnetConnection from .connection import SSHConnection, TelnetConnection
from .service import app_service from .service import app_service
from .conf import config from .conf import config
...@@ -93,15 +94,20 @@ class ProxyServer: ...@@ -93,15 +94,20 @@ class ProxyServer:
验证用户是否有连接改资产的权限 验证用户是否有连接改资产的权限
:return: True or False :return: True or False
""" """
return app_service.validate_user_asset_permission( kwargs = {
self.client.user.id, self.asset.id, self.system_user.id 'user_id': self.client.user.id,
) 'asset_id': self.asset.id,
'system_user_id': self.system_user.id,
'action_name': PERMS_ACTION_NAME_CONNECT
}
return app_service.validate_user_asset_permission(**kwargs)
def get_server_conn(self): def get_server_conn(self):
logger.info("Connect to {}:{} ...".format(self.asset.hostname, self.asset.port)) 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_unicode(warning(_('No permission'))) msg = _('No permission')
self.client.send_unicode(warning(wr(msg, before=2, after=0)))
server = None server = None
elif self.system_user.protocol == self.asset.protocol == 'telnet': elif self.system_user.protocol == self.asset.protocol == 'telnet':
server = self.get_telnet_server_conn() server = self.get_telnet_server_conn()
......
...@@ -11,6 +11,10 @@ from coco.utils import get_logger ...@@ -11,6 +11,10 @@ from coco.utils import get_logger
from .conf import config from .conf import config
from .service import app_service from .service import app_service
from .connection import SSHConnection from .connection import SSHConnection
from .const import (
PERMS_ACTION_NAME_DOWNLOAD_FILE, PERMS_ACTION_NAME_UPLOAD_FILE,
PERMS_ACTION_NAME_ALL,
)
CURRENT_DIR = os.path.dirname(__file__) CURRENT_DIR = os.path.dirname(__file__)
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -267,11 +271,27 @@ class SFTPServer(paramiko.SFTPServerInterface): ...@@ -267,11 +271,27 @@ class SFTPServer(paramiko.SFTPServerInterface):
def lstat(self, path): def lstat(self, path):
return self.stat(path) return self.stat(path)
@staticmethod
def validate_permission(system_user, action):
check_actions = [PERMS_ACTION_NAME_ALL, action]
granted_actions = getattr(system_user, 'actions', [])
actions = list(set(granted_actions).intersection(set(check_actions)))
return bool(actions)
def check_action(self, path, action):
request = self.parse_path(path)
host, su = request['host'], request['su']
system_user = self.hosts.get(host, {}).get('system_users', {}).get(su)
if not system_user:
raise PermissionError("No system user explicit")
if not self.validate_permission(system_user, action):
raise PermissionError("Permission deny")
@convert_error @convert_error
def open(self, path, flags, attr=None): def open(self, path, flags, attr=None):
binary_flag = getattr(os, 'O_BINARY', 0) binary_flag = getattr(os, 'O_BINARY', 0)
flags |= binary_flag flags |= binary_flag
success = False
if flags & os.O_WRONLY: if flags & os.O_WRONLY:
if flags & os.O_APPEND: if flags & os.O_APPEND:
...@@ -288,12 +308,17 @@ class SFTPServer(paramiko.SFTPServerInterface): ...@@ -288,12 +308,17 @@ class SFTPServer(paramiko.SFTPServerInterface):
if 'r' in mode: if 'r' in mode:
operate = "Download" operate = "Download"
action = PERMS_ACTION_NAME_DOWNLOAD_FILE
elif 'a' in mode: elif 'a' in mode:
operate = "Append" operate = "Append"
action = PERMS_ACTION_NAME_UPLOAD_FILE
else: else:
operate = "Upload" operate = "Upload"
action = PERMS_ACTION_NAME_UPLOAD_FILE
success = False
try: try:
self.check_action(path, action)
client, rpath = self.get_sftp_client_rpath(path) client, rpath = self.get_sftp_client_rpath(path)
f = client.open(rpath, mode, bufsize=4096) f = client.open(rpath, mode, bufsize=4096)
f.prefetch() f.prefetch()
...@@ -309,6 +334,7 @@ class SFTPServer(paramiko.SFTPServerInterface): ...@@ -309,6 +334,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
@convert_error @convert_error
def remove(self, path): def remove(self, path):
self.check_action(path, action=PERMS_ACTION_NAME_UPLOAD_FILE)
client, rpath = self.get_sftp_client_rpath(path) client, rpath = self.get_sftp_client_rpath(path)
success = False success = False
...@@ -321,6 +347,7 @@ class SFTPServer(paramiko.SFTPServerInterface): ...@@ -321,6 +347,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
@convert_error @convert_error
def rename(self, src, dest): def rename(self, src, dest):
self.check_action(src, action=PERMS_ACTION_NAME_UPLOAD_FILE)
client, rsrc = self.get_sftp_client_rpath(src) client, rsrc = self.get_sftp_client_rpath(src)
client2, rdest = self.get_sftp_client_rpath(dest) client2, rdest = self.get_sftp_client_rpath(dest)
success = False success = False
...@@ -338,6 +365,7 @@ class SFTPServer(paramiko.SFTPServerInterface): ...@@ -338,6 +365,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
@convert_error @convert_error
def mkdir(self, path, attr=0o755): def mkdir(self, path, attr=0o755):
self.check_action(path, action=PERMS_ACTION_NAME_UPLOAD_FILE)
client, rpath = self.get_sftp_client_rpath(path) client, rpath = self.get_sftp_client_rpath(path)
success = False success = False
...@@ -352,6 +380,7 @@ class SFTPServer(paramiko.SFTPServerInterface): ...@@ -352,6 +380,7 @@ class SFTPServer(paramiko.SFTPServerInterface):
@convert_error @convert_error
def rmdir(self, path): def rmdir(self, path):
self.check_action(path, action=PERMS_ACTION_NAME_UPLOAD_FILE)
client, rpath = self.get_sftp_client_rpath(path) client, rpath = self.get_sftp_client_rpath(path)
success = False success = False
...@@ -405,10 +434,14 @@ class InternalSFTPClient(SFTPServer): ...@@ -405,10 +434,14 @@ class InternalSFTPClient(SFTPServer):
client, rpath = self.get_sftp_client_rpath(path) client, rpath = self.get_sftp_client_rpath(path)
if 'r' in mode: if 'r' in mode:
operate = "Download" operate = "Download"
action = PERMS_ACTION_NAME_DOWNLOAD_FILE
else: else:
operate = "Upload" operate = "Upload"
action = PERMS_ACTION_NAME_UPLOAD_FILE
success = False success = False
try: try:
self.check_action(path, action=action)
f = client.open(rpath, mode, bufsize=4096) f = client.open(rpath, mode, bufsize=4096)
success = True success = True
return f return f
...@@ -423,6 +456,12 @@ class InternalSFTPClient(SFTPServer): ...@@ -423,6 +456,12 @@ class InternalSFTPClient(SFTPServer):
attr = super(InternalSFTPClient, self).lstat.__wrapped__(self, path) attr = super(InternalSFTPClient, self).lstat.__wrapped__(self, path)
return attr return attr
def rename(self, src, dest):
return super(InternalSFTPClient, self).rename.__wrapped__(self, src, dest)
def mkdir(self, path, attr=0o755):
return super(InternalSFTPClient, self).mkdir.__wrapped__(self, path, attr)
def rmdir(self, path): def rmdir(self, path):
return super(InternalSFTPClient, self).rmdir.__wrapped__(self, path) return super(InternalSFTPClient, self).rmdir.__wrapped__(self, path)
...@@ -430,9 +469,10 @@ class InternalSFTPClient(SFTPServer): ...@@ -430,9 +469,10 @@ class InternalSFTPClient(SFTPServer):
return FakeChannel.new() return FakeChannel.new()
def unlink(self, path): def unlink(self, path):
return self.remove(path) return super(InternalSFTPClient, self).remove.__wrapped__(self, path)
def putfo(self, f, path, callback=None, confirm=True): def putfo(self, f, path, callback=None, confirm=True):
self.check_action(path, action=PERMS_ACTION_NAME_UPLOAD_FILE)
client, rpath = self.get_sftp_client_rpath(path) client, rpath = self.get_sftp_client_rpath(path)
success = False success = False
try: try:
......
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