Compare commits

...

9 Commits
v3.0.4 ... v3.0

Author SHA1 Message Date
Bai
98a5e97518 fix: 修复日志记录到syslog时中文编码问题 2023-03-15 19:45:16 +08:00
ibuler
17be132497 fix: windows 平台默认不开启 console 2023-03-15 19:00:52 +08:00
feng
0cbce5f07a fix: 账号模版创建带密码的密钥之后无法添加到主机 2023-03-14 11:24:44 +08:00
ibuler
26dfb729bc perf: 优化资产迁移,避免冲突 2023-03-14 11:20:29 +08:00
fit2bot
bbe03ddb73 perf: 修改 ssh key with pass 报错 (#9918)
Co-authored-by: ibuler <ibuler@qq.com>
2023-03-10 16:05:11 +08:00
Bai
c88645fa57 feat: 支持飞书国际版(lark) 2023-03-10 15:48:08 +08:00
Bai
7271feb598 fix: ignore 2023-03-09 18:12:22 +08:00
jiangweidong
950183ca85 fix: 操作日志可能保存明文密码 2023-03-09 12:38:40 +08:00
Eric
6b1aa752d6 fix: 修复存储故障造成的录像获取失败问题 2023-03-09 11:51:37 +08:00
18 changed files with 96 additions and 30 deletions

View File

@@ -33,7 +33,8 @@ class AuthValidateMixin(serializers.Serializer):
return secret return secret
elif secret_type == SecretType.SSH_KEY: elif secret_type == SecretType.SSH_KEY:
passphrase = passphrase if passphrase else None passphrase = passphrase if passphrase else None
return validate_ssh_key(secret, passphrase) secret = validate_ssh_key(secret, passphrase)
return secret
else: else:
return secret return secret
@@ -41,8 +42,9 @@ class AuthValidateMixin(serializers.Serializer):
secret_type = validated_data.get('secret_type') secret_type = validated_data.get('secret_type')
passphrase = validated_data.get('passphrase') passphrase = validated_data.get('passphrase')
secret = validated_data.pop('secret', None) secret = validated_data.pop('secret', None)
self.handle_secret(secret, secret_type, passphrase) validated_data['secret'] = self.handle_secret(
validated_data['secret'] = secret secret, secret_type, passphrase
)
for field in ('secret',): for field in ('secret',):
value = validated_data.get(field) value = validated_data.get(field)
if not value: if not value:

View File

@@ -39,7 +39,7 @@ class Protocol(ChoicesMixin, models.TextChoices):
'port': 3389, 'port': 3389,
'secret_types': ['password'], 'secret_types': ['password'],
'setting': { 'setting': {
'console': True, 'console': False,
'security': 'any', 'security': 'any',
} }
}, },

View File

@@ -34,8 +34,9 @@ def migrate_database_to_asset(apps, *args):
_attrs = app.attrs or {} _attrs = app.attrs or {}
attrs.update(_attrs) attrs.update(_attrs)
name = 'DB-{}'.format(app.name)
db = db_model( db = db_model(
id=app.id, name=app.name, address=attrs['host'], id=app.id, name=name, address=attrs['host'],
protocols='{}/{}'.format(app.type, attrs['port']), protocols='{}/{}'.format(app.type, attrs['port']),
db_name=attrs['database'] or '', db_name=attrs['database'] or '',
platform=platforms_map[app.type], platform=platforms_map[app.type],
@@ -61,8 +62,9 @@ def migrate_cloud_to_asset(apps, *args):
for app in applications: for app in applications:
attrs = app.attrs attrs = app.attrs
print("\t- Create cloud: {}".format(app.name)) print("\t- Create cloud: {}".format(app.name))
name = 'Cloud-{}'.format(app.name)
cloud = cloud_model( cloud = cloud_model(
id=app.id, name=app.name, id=app.id, name=name,
address=attrs.get('cluster', ''), address=attrs.get('cluster', ''),
protocols='k8s/443', platform=platform, protocols='k8s/443', platform=platform,
org_id=app.org_id, org_id=app.org_id,

View File

@@ -0,0 +1,29 @@
# Generated by Django 3.2.17 on 2023-03-15 09:41
from django.db import migrations
def set_windows_platform_non_console(apps, schema_editor):
Platform = apps.get_model('assets', 'Platform')
names = ['Windows', 'Windows-RDP', 'Windows-TLS', 'RemoteAppHost']
windows = Platform.objects.filter(name__in=names)
if not windows:
return
for p in windows:
rdp = p.protocols.filter(name='rdp').first()
if not rdp:
continue
rdp.setting['console'] = False
rdp.save()
class Migration(migrations.Migration):
dependencies = [
('assets', '0109_alter_asset_options'),
]
operations = [
migrations.RunPython(set_windows_platform_non_console)
]

View File

@@ -11,7 +11,7 @@ __all__ = ['Platform', 'PlatformProtocol', 'PlatformAutomation']
class PlatformProtocol(models.Model): class PlatformProtocol(models.Model):
SETTING_ATTRS = { SETTING_ATTRS = {
'console': True, 'console': False,
'security': 'any,tls,rdp', 'security': 'any,tls,rdp',
'sftp_enabled': True, 'sftp_enabled': True,
'sftp_home': '/tmp' 'sftp_home': '/tmp'

View File

@@ -73,7 +73,7 @@ class AssetAccountSerializer(
'is_active', 'version', 'secret_type', 'is_active', 'version', 'secret_type',
] ]
fields_write_only = [ fields_write_only = [
'secret', 'push_now', 'template' 'secret', 'passphrase', 'push_now', 'template'
] ]
fields = fields_mini + fields_write_only fields = fields_mini + fields_write_only
extra_kwargs = { extra_kwargs = {

View File

@@ -1,6 +1,5 @@
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from django.core import validators
from assets.const.web import FillType from assets.const.web import FillType
from common.serializers import WritableNestedModelSerializer from common.serializers import WritableNestedModelSerializer
@@ -19,7 +18,7 @@ class ProtocolSettingSerializer(serializers.Serializer):
("nla", "NLA"), ("nla", "NLA"),
] ]
# RDP # RDP
console = serializers.BooleanField(required=False) console = serializers.BooleanField(required=False, default=False)
security = serializers.ChoiceField(choices=SECURITY_CHOICES, default="any") security = serializers.ChoiceField(choices=SECURITY_CHOICES, default="any")
# SFTP # SFTP

View File

@@ -60,7 +60,7 @@ class FeiShuQRMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, View):
'state': state, 'state': state,
'redirect_uri': redirect_uri, 'redirect_uri': redirect_uri,
} }
url = URL.AUTHEN + '?' + urlencode(params) url = URL().authen + '?' + urlencode(params)
return url return url
@staticmethod @staticmethod

View File

@@ -2,4 +2,3 @@ from __future__ import absolute_import
# This will make sure the app is always imported when # This will make sure the app is always imported when
# Django starts so that shared_task will use this app. # Django starts so that shared_task will use this app.

View File

@@ -1,7 +1,10 @@
from werkzeug.local import Local from werkzeug.local import Local
from django.utils import translation
thread_local = Local() thread_local = Local()
encrypted_field_set = {'password'} encrypted_field_set = {'password', 'secret'}
def _find(attr): def _find(attr):
@@ -10,4 +13,5 @@ def _find(attr):
def add_encrypted_field_set(label): def add_encrypted_field_set(label):
if label: if label:
encrypted_field_set.add(str(label)) with translation.override('en'):
encrypted_field_set.add(str(label))

View File

@@ -3,6 +3,7 @@ import json
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from django.conf import settings
from common.utils.common import get_logger from common.utils.common import get_logger
from common.sdk.im.utils import digest from common.sdk.im.utils import digest
from common.sdk.im.mixin import RequestMixin, BaseRequest from common.sdk.im.mixin import RequestMixin, BaseRequest
@@ -11,14 +12,30 @@ logger = get_logger(__name__)
class URL: class URL:
AUTHEN = 'https://open.feishu.cn/open-apis/authen/v1/index'
GET_TOKEN = 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/'
# https://open.feishu.cn/document/ukTMukTMukTM/uEDO4UjLxgDO14SM4gTN # https://open.feishu.cn/document/ukTMukTMukTM/uEDO4UjLxgDO14SM4gTN
GET_USER_INFO_BY_CODE = 'https://open.feishu.cn/open-apis/authen/v1/access_token' @property
def host(self):
if settings.FEISHU_VERSION == 'feishu':
h = 'https://open.feishu.cn'
else:
h = 'https://open.larksuite.com'
return h
SEND_MESSAGE = 'https://open.feishu.cn/open-apis/im/v1/messages' @property
def authen(self):
return f'{self.host}/open-apis/authen/v1/index'
@property
def get_token(self):
return f'{self.host}/open-apis/auth/v3/tenant_access_token/internal/'
@property
def get_user_info_by_code(self):
return f'{self.host}/open-apis/authen/v1/access_token'
@property
def send_message(self):
return f'{self.host}/open-apis/im/v1/messages'
class ErrorCode: class ErrorCode:
@@ -51,7 +68,7 @@ class FeishuRequests(BaseRequest):
def request_access_token(self): def request_access_token(self):
data = {'app_id': self._app_id, 'app_secret': self._app_secret} data = {'app_id': self._app_id, 'app_secret': self._app_secret}
response = self.raw_request('post', url=URL.GET_TOKEN, data=data) response = self.raw_request('post', url=URL().get_token, data=data)
self.check_errcode_is_0(response) self.check_errcode_is_0(response)
access_token = response['tenant_access_token'] access_token = response['tenant_access_token']
@@ -86,7 +103,7 @@ class FeiShu(RequestMixin):
'code': code 'code': code
} }
data = self._requests.post(URL.GET_USER_INFO_BY_CODE, json=body, check_errcode_is_0=False) data = self._requests.post(URL().get_user_info_by_code, json=body, check_errcode_is_0=False)
self._requests.check_errcode_is_0(data) self._requests.check_errcode_is_0(data)
return data['data']['user_id'] return data['data']['user_id']
@@ -107,7 +124,7 @@ class FeiShu(RequestMixin):
try: try:
logger.info(f'Feishu send text: user_ids={user_ids} msg={msg}') logger.info(f'Feishu send text: user_ids={user_ids} msg={msg}')
self._requests.post(URL.SEND_MESSAGE, params=params, json=body) self._requests.post(URL().send_message, params=params, json=body)
except APIException as e: except APIException as e:
# 只处理可预知的错误 # 只处理可预知的错误
logger.exception(e) logger.exception(e)

View File

@@ -274,4 +274,4 @@ def ensure_last_char_is_ascii(data):
def data_to_json(data, sort_keys=True, indent=2, cls=None): def data_to_json(data, sort_keys=True, indent=2, cls=None):
if cls is None: if cls is None:
cls = DjangoJSONEncoder cls = DjangoJSONEncoder
return json.dumps(data, sort_keys=sort_keys, indent=indent, cls=cls) return json.dumps(data, ensure_ascii=False, sort_keys=sort_keys, indent=indent, cls=cls)

View File

@@ -376,6 +376,7 @@ class Config(dict):
'AUTH_FEISHU': False, 'AUTH_FEISHU': False,
'FEISHU_APP_ID': '', 'FEISHU_APP_ID': '',
'FEISHU_APP_SECRET': '', 'FEISHU_APP_SECRET': '',
'FEISHU_VERSION': 'feishu',
'LOGIN_REDIRECT_TO_BACKEND': '', # 'OPENID / CAS / SAML2 'LOGIN_REDIRECT_TO_BACKEND': '', # 'OPENID / CAS / SAML2
'LOGIN_REDIRECT_MSG_ENABLED': True, 'LOGIN_REDIRECT_MSG_ENABLED': True,

View File

@@ -137,6 +137,7 @@ DINGTALK_APPSECRET = CONFIG.DINGTALK_APPSECRET
AUTH_FEISHU = CONFIG.AUTH_FEISHU AUTH_FEISHU = CONFIG.AUTH_FEISHU
FEISHU_APP_ID = CONFIG.FEISHU_APP_ID FEISHU_APP_ID = CONFIG.FEISHU_APP_ID
FEISHU_APP_SECRET = CONFIG.FEISHU_APP_SECRET FEISHU_APP_SECRET = CONFIG.FEISHU_APP_SECRET
FEISHU_VERSION = CONFIG.FEISHU_VERSION
# Saml2 auth # Saml2 auth
AUTH_SAML2 = CONFIG.AUTH_SAML2 AUTH_SAML2 = CONFIG.AUTH_SAML2

View File

@@ -23,6 +23,7 @@ def migrate_app_perms_to_assets(apps, schema_editor):
asset_permission = asset_permission_model() asset_permission = asset_permission_model()
for attr in attrs: for attr in attrs:
setattr(asset_permission, attr, getattr(app_perm, attr)) setattr(asset_permission, attr, getattr(app_perm, attr))
asset_permission.name = f"App-{app_perm.name}"
asset_permissions.append(asset_permission) asset_permissions.append(asset_permission)
asset_permission_model.objects.bulk_create(asset_permissions, ignore_conflicts=True) asset_permission_model.objects.bulk_create(asset_permissions, ignore_conflicts=True)

View File

@@ -9,11 +9,11 @@ def migrate_system_user_to_accounts(apps, schema_editor):
bulk_size = 10000 bulk_size = 10000
while True: while True:
asset_permissions = asset_permission_model.objects \ asset_permissions = asset_permission_model.objects \
.prefetch_related('system_users')[count:bulk_size] .prefetch_related('system_users')[count:bulk_size]
if not asset_permissions: if not asset_permissions:
break break
count += len(asset_permissions)
count += len(asset_permissions)
updated = [] updated = []
for asset_permission in asset_permissions: for asset_permission in asset_permissions:
asset_permission.accounts = [s.username for s in asset_permission.system_users.all()] asset_permission.accounts = [s.username for s in asset_permission.system_users.all()]

View File

@@ -9,6 +9,13 @@ __all__ = ['FeiShuSettingSerializer']
class FeiShuSettingSerializer(serializers.Serializer): class FeiShuSettingSerializer(serializers.Serializer):
PREFIX_TITLE = _('FeiShu') PREFIX_TITLE = _('FeiShu')
VERSION_CHOICES = (
('feishu', _('FeiShu')),
('lark', 'Lark')
)
AUTH_FEISHU = serializers.BooleanField(default=False, label=_('Enable FeiShu Auth'))
FEISHU_APP_ID = serializers.CharField(max_length=256, required=True, label='App ID') FEISHU_APP_ID = serializers.CharField(max_length=256, required=True, label='App ID')
FEISHU_APP_SECRET = EncryptedField(max_length=256, required=False, label='App Secret') FEISHU_APP_SECRET = EncryptedField(max_length=256, required=False, label='App Secret')
AUTH_FEISHU = serializers.BooleanField(default=False, label=_('Enable FeiShu Auth')) FEISHU_VERSION = serializers.ChoiceField(
choices=VERSION_CHOICES, default='feishu', label=_('Version')
)

View File

@@ -105,9 +105,13 @@ class Session(OrgModelMixin):
def find_ok_relative_path_in_storage(self, storage): def find_ok_relative_path_in_storage(self, storage):
session_paths = self.get_all_possible_relative_path() session_paths = self.get_all_possible_relative_path()
for rel_path in session_paths: for rel_path in session_paths:
if storage.exists(rel_path): # storage 为多个外部存储时, 可能会因部分不可用,
return rel_path # 抛出异常, 影响录像的获取
try:
if storage.exists(rel_path):
return rel_path
except:
pass
@property @property
def asset_obj(self): def asset_obj(self):
return Asset.objects.get(id=self.asset_id) return Asset.objects.get(id=self.asset_id)