Compare commits

...

45 Commits
v3.0.0 ... 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
Bai
c908b47ccf fix: 修复组织管理员查看活动日志详情时没有对象的问题 2023-03-08 15:58:05 +08:00
Bai
88af33b7c2 fix: 修复组织管理员查看操作日志可以看到 system 组织下的操作问题, 只有系统管理员可以查看任务监控 2023-03-08 15:22:27 +08:00
老广
136537fcfb Merge pull request #9872 from jumpserver/pr@v3.0@fix_su_from_accounts
fix: 修复su-from-accounts API 500问题,Unsubscribe msg error 转成debug
2023-03-08 14:06:36 +08:00
老广
dfd404c549 Merge pull request #9875 from jumpserver/pr@v3.0@fix_operatelog_hide_sth
fix: 操作日志显示用户加密后的密文,及日期格式调整
2023-03-08 14:05:47 +08:00
老广
53886a5abf Merge pull request #9882 from jumpserver/pr@v3.0@perf_email_test_error
perf: 邮箱测试时,不填写'主题前缀'会报错
2023-03-08 14:02:58 +08:00
jiangweidong
50d3125fec perf: 邮箱测试时,不填写'主题前缀'会报错 2023-03-08 13:51:40 +08:00
jiangweidong
1145305f11 fix: 操作日志显示用户加密后的密文,及日期格式调整 2023-03-08 11:24:56 +08:00
feng
433f063142 fix: 修复su-from-accounts API 500问题,Unsubscribe msg error 转成debug 2023-03-08 11:02:05 +08:00
fit2bot
8c4e496391 fix: 修复i8n 500 (#9848)
Co-authored-by: feng <1304903146@qq.com>
2023-03-02 16:23:02 +08:00
Bai
fad214ccbb fix: 修复 ldap 用户登录时邮箱存在 500 的问题 2023-03-02 16:22:24 +08:00
Bai
3b0f68c4c7 fix: 修复 ldap 用户登录时邮箱存在 500 的问题 2023-03-02 16:11:04 +08:00
Aaron3S
87b4ecfb3a fix: 修复作业执行没有日志权限的问题 2023-03-01 18:34:55 +08:00
Bai
32f2be2793 fix: 资产类型树返回类型节点时, 没有platfrom设置isParent为False, 解决展开节点重复的问题 2023-03-01 17:28:59 +08:00
fit2bot
a098bc6c06 perf: 推送账号 社区版定时任务关闭 (#9805)
Co-authored-by: feng <1304903146@qq.com>
2023-02-28 13:43:38 +08:00
老广
86870ad985 Merge pull request #9798 from jumpserver/pr@v3.0@fix_protocol_init_error
perf: 修改协议创建时一些默认值
2023-02-28 09:45:15 +08:00
ibuler
c602bf7224 perf: 修改协议创建时一些默认值 2023-02-27 11:49:04 +00:00
fit2bot
86dab4fc6e perf: 今日活跃资产 (#9797)
Co-authored-by: feng <1304903146@qq.com>
2023-02-27 18:10:11 +08:00
Aaron3S
a85a80a945 fix: 默认增加普通用户作业中心权限 2023-02-27 17:28:04 +08:00
老广
349edc10aa Merge pull request #9791 from jumpserver/pr@v3.0@add_accounts_suggestions
perf: 添加账号用户名的推荐
2023-02-27 15:19:26 +08:00
ibuler
44918e3cb5 perf: 添加账号用户名的推荐
perf: 修改账号推荐
2023-02-27 07:14:55 +00:00
ibuler
9a2f6c0d70 perf: 修改资产 address 长度,以支持 mb4
perf: 修改长度
2023-02-27 14:08:15 +08:00
ibuler
934969a8f1 perf: 去掉没有 Name 的迁移 2023-02-27 14:02:09 +08:00
老广
57162c1628 Merge pull request #9776 from jumpserver/pr@v3.0@perf_account_migrate2
perf: 优化迁移 accounts
2023-02-27 10:22:59 +08:00
ibuler
32fb36867f perf: 优化迁移 accounts
perf: 优化账号迁移,同名的迁移到历史中
2023-02-26 01:49:25 +00:00
老广
158b589028 Merge pull request #9761 from jumpserver/pr@v3@fix_activity_save_error
fix: 解决Activity保存因为参数出错问题
2023-02-24 18:18:03 +08:00
jiangweidong
d64277353c Merge branch 'v3.0' of http://github.com/jumpserver/jumpserver into pr@v3@fix_activity_save_error 2023-02-24 18:10:47 +08:00
jiangweidong
bff6f397ce fix: 解决Activity保存因为参数出错问题 2023-02-24 18:10:42 +08:00
fit2bot
0ad461a804 perf: 修改host info 接口, 社区开放applet, 修改改密发邮件bug (#9760)
Co-authored-by: feng <1304903146@qq.com>
2023-02-24 18:08:40 +08:00
Bai
a1dcef0ba0 fix: 修复 web gui 支持的数据库 2023-02-24 15:12:08 +08:00
Bai
dbb1ee3a75 fix: 修复认证MFA失败次数清空问题 2023-02-24 14:43:51 +08:00
fit2bot
d6bd207a17 fix: 修复计算今日活跃资产过滤逻辑 (#9744)
Co-authored-by: Bai <baijiangjie@gmail.com>
2023-02-24 12:17:10 +08:00
Bai
e69ba27ff4 fix: 修复获取授权资产详情时返回 spec_info 字段, 解决连接 Magnus 问题 2023-02-24 11:41:47 +08:00
ibuler
adbe7c07c6 perf: 修复社区版可能引起的问题 2023-02-24 00:31:10 +08:00
老广
d1eacf53d4 Merge pull request #9736 from jumpserver/dev
fix: 修复 loong64 grpc 构建失败
2023-02-23 21:50:11 +08:00
老广
472c14fd27 Merge pull request #9734 from jumpserver/pr@dev@fix_build_grpc
fix: 修复 loong64 grpc 构建失败
2023-02-23 21:46:26 +08:00
吴小白
9d4854b8c3 fix: 修复 loong64 grpc 构建失败 2023-02-23 21:34:21 +08:00
60 changed files with 429 additions and 240 deletions

View File

@@ -24,6 +24,7 @@ ARG DEPENDENCIES=" \
libjpeg-dev \
libldap2-dev \
libsasl2-dev \
libssl-dev \
libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl \

View File

@@ -6,7 +6,7 @@ from rest_framework.response import Response
from accounts import serializers
from accounts.filters import AccountFilterSet
from accounts.models import Account
from assets.models import Asset
from assets.models import Asset, Node
from common.permissions import UserConfirmation, ConfirmType
from common.views.mixins import RecordViewLogMixin
from orgs.mixins.api import OrgBulkModelViewSet
@@ -28,6 +28,7 @@ class AccountViewSet(OrgBulkModelViewSet):
rbac_perms = {
'partial_update': ['accounts.change_account'],
'su_from_accounts': 'accounts.view_account',
'username_suggestions': 'accounts.view_account',
}
@action(methods=['get'], detail=False, url_path='su-from-accounts')
@@ -42,11 +43,34 @@ class AccountViewSet(OrgBulkModelViewSet):
asset = get_object_or_404(Asset, pk=asset_id)
accounts = asset.accounts.all()
else:
accounts = []
accounts = Account.objects.none()
accounts = self.filter_queryset(accounts)
serializer = serializers.AccountSerializer(accounts, many=True)
return Response(data=serializer.data)
@action(methods=['get'], detail=False, url_path='username-suggestions')
def username_suggestions(self, request, *args, **kwargs):
asset_ids = request.query_params.get('assets')
node_keys = request.query_params.get('keys')
username = request.query_params.get('username')
assets = Asset.objects.all()
if asset_ids:
assets = assets.filter(id__in=asset_ids.split(','))
if node_keys:
patten = Node.get_node_all_children_key_pattern(node_keys.split(','))
assets = assets.filter(nodes__key__regex=patten)
accounts = Account.objects.filter(asset__in=assets)
if username:
accounts = accounts.filter(username__icontains=username)
usernames = list(accounts.values_list('username', flat=True).distinct()[:10])
usernames.sort()
common = [i for i in usernames if i in usernames if i.lower() in ['root', 'admin', 'administrator']]
others = [i for i in usernames if i not in common]
usernames = common + others
return Response(data=usernames)
class AccountSecretsViewSet(RecordViewLogMixin, AccountViewSet):
"""

View File

@@ -206,7 +206,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
serializer = serializer_cls(recorders, many=True)
header = [str(v.label) for v in serializer.child.fields.values()]
rows = [list(row.values()) for row in serializer.data]
rows = [[str(i) for i in row.values()] for row in serializer.data]
if not rows:
return False

View File

@@ -2,6 +2,7 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _
from accounts.const import AutomationTypes
from jumpserver.utils import has_valid_xpack_license
from .base import AccountBaseAutomation
from .change_secret import ChangeSecretMixin
@@ -27,6 +28,8 @@ class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation):
def save(self, *args, **kwargs):
self.type = AutomationTypes.push_account
if not has_valid_xpack_license():
self.is_periodic = False
super().save(*args, **kwargs)
def to_attr_json(self):

View File

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

View File

@@ -8,7 +8,7 @@ from orgs.utils import tmp_to_org, tmp_to_root_org
logger = get_logger(__file__)
def task_activity_callback(self, pid, trigger, tp):
def task_activity_callback(self, pid, trigger, tp, *args, **kwargs):
model = AutomationTypes.get_type_model(tp)
with tmp_to_root_org():
instance = get_object_or_none(model, pk=pid)

View File

@@ -9,7 +9,7 @@ from orgs.utils import tmp_to_org, tmp_to_root_org
logger = get_logger(__file__)
def task_activity_callback(self, pid, trigger):
def task_activity_callback(self, pid, trigger, *args, **kwargs):
from accounts.models import AccountBackupAutomation
with tmp_to_root_org():
plan = get_object_or_none(AccountBackupAutomation, pk=pid)

View File

@@ -27,7 +27,7 @@ def gather_asset_accounts_util(nodes, task_name):
@shared_task(
queue="ansible", verbose_name=_('Gather asset accounts'),
activity_callback=lambda self, node_ids, task_name=None: (node_ids, None)
activity_callback=lambda self, node_ids, task_name=None, *args, **kwargs: (node_ids, None)
)
def gather_asset_accounts_task(node_ids, task_name=None):
if task_name is None:

View File

@@ -13,7 +13,7 @@ __all__ = [
@shared_task(
queue="ansible", verbose_name=_('Push accounts to assets'),
activity_callback=lambda self, account_ids, asset_ids: (account_ids, None)
activity_callback=lambda self, account_ids, *args, **kwargs: (account_ids, None)
)
def push_accounts_to_assets_task(account_ids):
from accounts.models import PushAccountAutomation

View File

@@ -99,13 +99,14 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet):
("platform", serializers.PlatformSerializer),
("suggestion", serializers.MiniAssetSerializer),
("gateways", serializers.GatewaySerializer),
("spec_info", serializers.SpecSerializer)
("spec_info", serializers.SpecSerializer),
)
rbac_perms = (
("match", "assets.match_asset"),
("platform", "assets.view_platform"),
("gateways", "assets.view_gateway"),
("spec_info", "assets.view_asset"),
("info", "assets.view_asset"),
)
extra_filter_backends = [LabelFilterBackend, IpInFilterBackend, NodeFilterBackend]

View File

@@ -21,4 +21,10 @@ class HostViewSet(AssetViewSet):
@action(methods=["GET"], detail=True, url_path="info")
def info(self, *args, **kwargs):
asset = super().get_object()
return Response(asset.info)
serializer = self.get_serializer(asset.info)
data = serializer.data
data['asset'] = {
'id': asset.id, 'name': asset.name,
'address': asset.address
}
return Response(data)

View File

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

View File

@@ -214,10 +214,13 @@ class AllTypes(ChoicesMixin):
tp_node = cls.choice_to_node(tp, category_node['id'], opened=False, meta=meta)
tp_count = category_type_mapper.get(category + '_' + tp, 0)
tp_node['name'] += f'({tp_count})'
platforms = tp_platforms.get(category + '_' + tp, [])
if not platforms:
tp_node['isParent'] = False
nodes.append(tp_node)
# Platform 格式化
for p in tp_platforms.get(category + '_' + tp, []):
for p in platforms:
platform_node = cls.platform_to_node(p, tp_node['id'], include_asset)
platform_node['name'] += f'({platform_count.get(p.id, 0)})'
nodes.append(platform_node)
@@ -306,10 +309,11 @@ class AllTypes(ChoicesMixin):
protocols_data = deepcopy(default_protocols)
if _protocols:
protocols_data = [p for p in protocols_data if p['name'] in _protocols]
for p in protocols_data:
setting = _protocols_setting.get(p['name'], {})
p['required'] = p.pop('required', False)
p['default'] = p.pop('default', False)
p['required'] = setting.pop('required', False)
p['default'] = setting.pop('default', False)
p['setting'] = {**p.get('setting', {}), **setting}
platform_data = {

View File

@@ -93,7 +93,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='asset',
name='address',
field=models.CharField(db_index=True, max_length=1024, verbose_name='Address'),
field=models.CharField(db_index=True, max_length=767, verbose_name='Address'),
),
migrations.AddField(
model_name='asset',

View File

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

View File

@@ -1,12 +1,15 @@
# Generated by Django 3.2.12 on 2022-07-11 06:13
import time
from django.utils import timezone
from itertools import groupby
from django.db import migrations
def migrate_asset_accounts(apps, schema_editor):
auth_book_model = apps.get_model('assets', 'AuthBook')
account_model = apps.get_model('accounts', 'Account')
account_history_model = apps.get_model('accounts', 'HistoricalAccount')
count = 0
bulk_size = 1000
@@ -20,34 +23,35 @@ def migrate_asset_accounts(apps, schema_editor):
break
count += len(auth_books)
accounts = []
# auth book 和 account 相同的属性
same_attrs = [
'id', 'username', 'comment', 'date_created', 'date_updated',
'created_by', 'asset_id', 'org_id',
]
# 认证的属性,可能是 authbook 的,可能是 systemuser 的
# 认证的属性,可能是 auth_book 的,可能是 system_user 的
auth_attrs = ['password', 'private_key', 'token']
all_attrs = same_attrs + auth_attrs
accounts = []
for auth_book in auth_books:
values = {'version': 1}
account_values = {'version': 1}
system_user = auth_book.systemuser
if system_user:
# 更新一次系统用户的认证属性
values.update({attr: getattr(system_user, attr, '') for attr in all_attrs})
values['created_by'] = str(system_user.id)
values['privileged'] = system_user.type == 'admin'
account_values.update({attr: getattr(system_user, attr, '') for attr in all_attrs})
account_values['created_by'] = str(system_user.id)
account_values['privileged'] = system_user.type == 'admin' \
or system_user.username in ['root', 'Administrator']
auth_book_auth = {attr: getattr(auth_book, attr, '') for attr in all_attrs if getattr(auth_book, attr, '')}
# 最终使用 authbook 的认证属性
values.update(auth_book_auth)
# 最终优先使用 auth_book 的认证属性
account_values.update(auth_book_auth)
auth_infos = []
username = values['username']
username = account_values['username']
for attr in auth_attrs:
secret = values.pop(attr, None)
secret = account_values.pop(attr, None)
if not secret:
continue
@@ -66,13 +70,48 @@ def migrate_asset_accounts(apps, schema_editor):
auth_infos.append((username, 'password', ''))
for name, secret_type, secret in auth_infos:
account = account_model(**values, name=name, secret=secret, secret_type=secret_type)
if not name:
continue
account = account_model(**account_values, name=name, secret=secret, secret_type=secret_type)
accounts.append(account)
account_model.objects.bulk_create(accounts, ignore_conflicts=True)
accounts.sort(key=lambda x: (x.name, x.asset_id, x.date_updated))
grouped_accounts = groupby(accounts, lambda x: (x.name, x.asset_id))
accounts_to_add = []
accounts_to_history = []
for key, _accounts in grouped_accounts:
_accounts = list(_accounts)
if not _accounts:
continue
_account = _accounts[-1]
accounts_to_add.append(_account)
_account_history = []
for ac in _accounts:
if not ac.secret:
continue
if ac.id != _account.id and ac.secret == _account.secret:
continue
history_data = {
'id': _account.id,
'secret': ac.secret,
'secret_type': ac.secret_type,
'history_date': ac.date_updated,
'history_type': '~',
'history_change_reason': 'from account {}'.format(_account.name),
}
_account_history.append(account_history_model(**history_data))
_account.version = len(_account_history)
accounts_to_history.extend(_account_history)
account_model.objects.bulk_create(accounts_to_add, ignore_conflicts=True)
account_history_model.objects.bulk_create(accounts_to_history, ignore_conflicts=True)
print("\t - Create asset accounts: {}-{} using: {:.2f}s".format(
count - len(auth_books), count, time.time() - start
))
print("\t - accounts: {}".format(len(accounts_to_add)))
print("\t - histories: {}".format(len(accounts_to_history)))
def migrate_db_accounts(apps, schema_editor):
@@ -130,6 +169,9 @@ def migrate_db_accounts(apps, schema_editor):
values['secret_type'] = secret_type
values['secret'] = secret
if not name:
continue
for app in apps:
values['asset_id'] = str(app.id)
account = account_model(**values)

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

@@ -100,7 +100,7 @@ class Asset(NodesRelationMixin, AbsConnectivity, JMSOrgBaseModel):
Type = const.AllTypes
name = models.CharField(max_length=128, verbose_name=_('Name'))
address = models.CharField(max_length=1024, verbose_name=_('Address'), db_index=True)
address = models.CharField(max_length=767, verbose_name=_('Address'), db_index=True)
platform = models.ForeignKey(Platform, on_delete=models.PROTECT, verbose_name=_("Platform"), related_name='assets')
domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets',
verbose_name=_("Domain"), on_delete=models.SET_NULL)

View File

@@ -489,7 +489,7 @@ class SomeNodesMixin:
return cls.default_node()
if ori_org and ori_org.is_root():
return None
return cls.default_node()
org_roots = cls.org_root_nodes()
org_roots_length = len(org_roots)

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ from orgs.utils import tmp_to_root_org, tmp_to_org
logger = get_logger(__file__)
def task_activity_callback(self, pid, trigger, tp):
def task_activity_callback(self, pid, trigger, tp, *args, **kwargs):
model = AutomationTypes.get_type_model(tp)
with tmp_to_root_org():
instance = get_object_or_none(model, pk=pid)

View File

@@ -10,6 +10,7 @@ from rest_framework.permissions import IsAuthenticated
from common.drf.filters import DatetimeRangeFilter
from common.plugins.es import QuerySet as ESQuerySet
from common.utils import is_uuid
from common.utils import lazyproperty
from orgs.mixins.api import OrgReadonlyModelViewSet, OrgModelViewSet
from orgs.utils import current_org, tmp_to_root_org
from orgs.models import Organization
@@ -143,13 +144,19 @@ class OperateLogViewSet(OrgReadonlyModelViewSet):
search_fields = ['resource', 'user']
ordering = ['-datetime']
@lazyproperty
def is_action_detail(self):
return self.detail and self.request.query_params.get('type') == 'action_detail'
def get_serializer_class(self):
if self.request.query_params.get('type') == 'action_detail':
if self.is_action_detail:
return OperateLogActionDetailSerializer
return super().get_serializer_class()
def get_queryset(self):
org_q = Q(org_id=Organization.SYSTEM_ID) | Q(org_id=current_org.id)
org_q = Q(org_id=current_org.id)
if self.is_action_detail:
org_q |= Q(org_id=Organization.SYSTEM_ID)
with tmp_to_root_org():
qs = OperateLog.objects.filter(org_q)
es_config = settings.OPERATE_LOG_ELASTICSEARCH_CONFIG

View File

@@ -4,7 +4,6 @@ from django.db import transaction
from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _
from users.models import User
from common.utils import get_request_ip, get_logger
from common.utils.timezone import as_current_tz
from common.utils.encode import Singleton

View File

@@ -2,7 +2,7 @@
#
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from audits.backends.db import OperateLogStore
from common.serializers.fields import LabeledChoiceField
from common.utils import reverse, i18n_trans
@@ -78,7 +78,7 @@ class OperateLogActionDetailSerializer(serializers.ModelSerializer):
return data
class OperateLogSerializer(serializers.ModelSerializer):
class OperateLogSerializer(BulkOrgResourceModelSerializer):
action = LabeledChoiceField(choices=ActionChoices.choices, label=_("Action"))
resource = serializers.SerializerMethodField(label=_("Resource"))
resource_type = serializers.SerializerMethodField(label=_('Resource Type'))

View File

@@ -1,13 +1,15 @@
import codecs
import copy
import csv
from itertools import chain
from datetime import datetime
from django.db import models
from django.http import HttpResponse
from common.utils.timezone import as_current_tz
from common.utils import validate_ip, get_ip_city, get_logger
from settings.serializers import SettingsSerializer
from .const import DEFAULT_CITY
logger = get_logger(__name__)
@@ -70,6 +72,8 @@ def _get_instance_field_value(
f.verbose_name = 'id'
elif isinstance(value, (list, dict)):
value = copy.deepcopy(value)
elif isinstance(value, datetime):
value = as_current_tz(value).strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(f, models.OneToOneField) and isinstance(value, models.Model):
nested_data = _get_instance_field_value(
value, include_model_fields, model_need_continue_fields, ('id',)

View File

@@ -225,6 +225,7 @@ class MFAMixin:
self.request.session['auth_mfa_time'] = time.time()
self.request.session['auth_mfa_required'] = 0
self.request.session['auth_mfa_type'] = mfa_type
MFABlockUtils(self.request.user.username, self.get_request_ip()).clean_failed_count()
def clean_mfa_mark(self):
keys = ['auth_mfa', 'auth_mfa_time', 'auth_mfa_required', 'auth_mfa_type']

View File

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

View File

@@ -6,6 +6,7 @@ import os
import datetime
from typing import Callable
from django.db import IntegrityError
from django.templatetags.static import static
from django.contrib.auth import login as auth_login, logout as auth_logout
from django.http import HttpResponse, HttpRequest
@@ -229,6 +230,23 @@ class UserLoginView(mixins.AuthMixin, UserLoginContextMixin, FormView):
) as e:
form.add_error('code', e.msg)
return super().form_invalid(form)
except (IntegrityError,) as e:
# (1062, "Duplicate entry 'youtester001@example.com' for key 'users_user.email'")
error = str(e)
if len(e.args) < 2:
form.add_error(None, error)
return super().form_invalid(form)
msg_list = e.args[1].split("'")
if len(msg_list) < 4:
form.add_error(None, error)
return super().form_invalid(form)
email, field = msg_list[1], msg_list[3]
if field == 'users_user.email':
error = _('User email already exists ({})').format(email)
form.add_error(None, error)
return super().form_invalid(form)
self.clear_rsa_key()
return self.redirect_to_guard_view()

View File

@@ -32,11 +32,14 @@ class UserLoginMFAView(mixins.AuthMixin, FormView):
return super().get(*args, **kwargs)
def form_valid(self, form):
from users.utils import MFABlockUtils
code = form.cleaned_data.get('code')
mfa_type = form.cleaned_data.get('mfa_type')
try:
self._do_check_user_mfa(code, mfa_type)
user, ip = self.get_user_from_session(), self.get_request_ip()
MFABlockUtils(user.username, ip).clean_failed_count()
return redirect_to_guard_view('mfa_ok')
except (errors.MFAFailedError, errors.BlockMFAError) as e:
form.add_error('code', e.msg)

View File

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

View File

@@ -1,7 +1,10 @@
from werkzeug.local import Local
from django.utils import translation
thread_local = Local()
encrypted_field_set = set()
encrypted_field_set = {'password', 'secret'}
def _find(attr):
@@ -10,4 +13,5 @@ def _find(attr):
def add_encrypted_field_set(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 rest_framework.exceptions import APIException
from django.conf import settings
from common.utils.common import get_logger
from common.sdk.im.utils import digest
from common.sdk.im.mixin import RequestMixin, BaseRequest
@@ -11,14 +12,30 @@ logger = get_logger(__name__)
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
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:
@@ -51,7 +68,7 @@ class FeishuRequests(BaseRequest):
def request_access_token(self):
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)
access_token = response['tenant_access_token']
@@ -86,7 +103,7 @@ class FeiShu(RequestMixin):
'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)
return data['data']['user_id']
@@ -107,7 +124,7 @@ class FeiShu(RequestMixin):
try:
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:
# 只处理可预知的错误
logger.exception(e)

View File

@@ -10,7 +10,7 @@ from .utils import get_logger
logger = get_logger(__file__)
def task_activity_callback(self, subject, message, recipient_list, **kwargs):
def task_activity_callback(self, subject, message, recipient_list, *args, **kwargs):
from users.models import User
email_list = recipient_list
resource_ids = list(User.objects.filter(email__in=email_list).values_list('id', flat=True))

View File

@@ -108,7 +108,7 @@ class Subscription:
try:
self.sub.close()
except Exception as e:
logger.error('Unsubscribe msg error: {}'.format(e))
logger.debug('Unsubscribe msg error: {}'.format(e))
def retry(self, _next, error, complete):
logger.info('Retry subscribe channel: {}'.format(self.ch))

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):
if cls is None:
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

@@ -35,7 +35,10 @@ def i18n_trans(s):
tpl, args = s.split(' % ', 1)
args = args.split(', ')
args = [gettext(arg) for arg in args]
return gettext(tpl) % tuple(args)
try:
return gettext(tpl) % tuple(args)
except TypeError:
return gettext(tpl)
def hello():

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-02-23 19:11+0800\n"
"POT-Creation-Date: 2023-03-02 16:00+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -394,7 +394,7 @@ msgid "Date last login"
msgstr "最終ログイン日"
#: accounts/models/automations/gather_account.py:15
#: accounts/models/automations/push_account.py:13 accounts/models/base.py:34
#: accounts/models/automations/push_account.py:14 accounts/models/base.py:34
#: acls/serializers/base.py:18 acls/serializers/base.py:49
#: assets/models/_user.py:23 audits/models.py:157 authentication/forms.py:25
#: authentication/forms.py:27 authentication/models/temp_token.py:9
@@ -419,11 +419,11 @@ msgstr "自動収集アカウント"
msgid "Gather asset accounts"
msgstr "アカウントのコレクション"
#: accounts/models/automations/push_account.py:12
#: accounts/models/automations/push_account.py:13
msgid "Triggers"
msgstr "トリガー方式"
#: accounts/models/automations/push_account.py:14 acls/models/base.py:81
#: accounts/models/automations/push_account.py:15 acls/models/base.py:81
#: acls/serializers/base.py:81 acls/serializers/login_acl.py:25
#: assets/models/cmd_filter.py:81 audits/models.py:65 audits/serializers.py:82
#: authentication/serializers/connect_token_secret.py:109
@@ -431,7 +431,7 @@ msgstr "トリガー方式"
msgid "Action"
msgstr "アクション"
#: accounts/models/automations/push_account.py:40
#: accounts/models/automations/push_account.py:43
msgid "Push asset account"
msgstr "アカウントプッシュ"
@@ -447,7 +447,7 @@ msgstr "アカウントの確認"
#: assets/models/group.py:20 assets/models/label.py:18
#: assets/models/platform.py:21 assets/models/platform.py:76
#: assets/serializers/asset/common.py:67 assets/serializers/asset/common.py:143
#: assets/serializers/platform.py:91 assets/serializers/platform.py:136
#: assets/serializers/platform.py:133
#: authentication/serializers/connect_token_secret.py:103 ops/mixin.py:21
#: ops/models/adhoc.py:21 ops/models/celery.py:15 ops/models/celery.py:57
#: ops/models/job.py:91 ops/models/playbook.py:23 ops/serializers/job.py:19
@@ -533,7 +533,7 @@ msgstr "エスクローされたパスワード"
#: accounts/serializers/account/account.py:75 applications/models.py:11
#: assets/models/label.py:21 assets/models/platform.py:77
#: assets/serializers/asset/common.py:120 assets/serializers/cagegory.py:8
#: assets/serializers/platform.py:97 assets/serializers/platform.py:137
#: assets/serializers/platform.py:94 assets/serializers/platform.py:134
#: perms/serializers/user_permission.py:26 settings/models.py:35
#: tickets/models/ticket/apply_application.py:13
msgid "Category"
@@ -544,7 +544,7 @@ msgstr "カテゴリ"
#: acls/serializers/command_acl.py:18 applications/models.py:14
#: assets/models/_user.py:50 assets/models/automations/base.py:20
#: assets/models/cmd_filter.py:74 assets/models/platform.py:78
#: assets/serializers/asset/common.py:121 assets/serializers/platform.py:96
#: assets/serializers/asset/common.py:121 assets/serializers/platform.py:93
#: audits/serializers.py:48
#: authentication/serializers/connect_token_secret.py:116 ops/models/job.py:102
#: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:31
@@ -741,7 +741,7 @@ msgstr "アクティブ"
#: authentication/models/sso_token.py:16
#: notifications/models/notification.py:12
#: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:58
#: perms/serializers/permission.py:23 rbac/builtin.py:118
#: perms/serializers/permission.py:23 rbac/builtin.py:122
#: rbac/models/rolebinding.py:49 terminal/backends/command/models.py:19
#: terminal/models/session/session.py:30 terminal/models/session/sharing.py:32
#: terminal/notifications.py:96 terminal/notifications.py:144
@@ -908,7 +908,7 @@ msgstr "アプリケーション"
msgid "Can match application"
msgstr "アプリケーションを一致させることができます"
#: assets/api/asset/asset.py:142
#: assets/api/asset/asset.py:143
msgid "Cannot create asset directly, you should create a host or other"
msgstr ""
"資産を直接作成することはできません。ホストまたはその他を作成する必要がありま"
@@ -944,10 +944,8 @@ msgid "No account"
msgstr "アカウントなし"
#: assets/automations/ping_gateway/manager.py:36
#, fuzzy
#| msgid "Assets amount"
msgid "Asset, {}, using account {}"
msgstr "資産"
msgstr "資産, {}, アカウントを使用 {}"
#: assets/automations/ping_gateway/manager.py:55
#, python-brace-format
@@ -1459,23 +1457,23 @@ msgstr "メタ"
msgid "Internal"
msgstr "ビルトイン"
#: assets/models/platform.py:83 assets/serializers/platform.py:94
#: assets/models/platform.py:83 assets/serializers/platform.py:91
msgid "Charset"
msgstr "シャーセット"
#: assets/models/platform.py:85 assets/serializers/platform.py:122
#: assets/models/platform.py:85 assets/serializers/platform.py:119
msgid "Domain enabled"
msgstr "ドメインを有効にする"
#: assets/models/platform.py:87 assets/serializers/platform.py:121
#: assets/models/platform.py:87 assets/serializers/platform.py:118
msgid "Su enabled"
msgstr "アカウントの切り替えを有効にする"
#: assets/models/platform.py:88 assets/serializers/platform.py:104
#: assets/models/platform.py:88 assets/serializers/platform.py:101
msgid "Su method"
msgstr "アカウントの切り替え方法"
#: assets/models/platform.py:90 assets/serializers/platform.py:101
#: assets/models/platform.py:90 assets/serializers/platform.py:98
msgid "Automation"
msgstr "オートメーション"
@@ -1488,7 +1486,7 @@ msgstr "%(value)s は偶数ではありません"
msgid "Auto fill"
msgstr "自動充填"
#: assets/serializers/asset/common.py:123 assets/serializers/platform.py:99
#: assets/serializers/asset/common.py:123 assets/serializers/platform.py:96
#: authentication/serializers/connect_token_secret.py:28
#: authentication/serializers/connect_token_secret.py:66
#: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:99
@@ -1629,7 +1627,7 @@ msgstr "アカウントの収集方法"
msgid "Primary"
msgstr "主要"
#: assets/serializers/platform.py:123
#: assets/serializers/platform.py:120
msgid "Default Domain"
msgstr "デフォルト ドメイン"
@@ -1940,20 +1938,20 @@ msgid "Auth Token"
msgstr "認証トークン"
#: audits/signal_handlers/login_log.py:31 authentication/notifications.py:73
#: authentication/views/login.py:73 authentication/views/wecom.py:177
#: authentication/views/login.py:74 authentication/views/wecom.py:177
#: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10
#: users/models/user.py:778
msgid "WeCom"
msgstr "企業微信"
#: audits/signal_handlers/login_log.py:32 authentication/views/feishu.py:144
#: authentication/views/login.py:85 notifications/backends/__init__.py:14
#: authentication/views/login.py:86 notifications/backends/__init__.py:14
#: settings/serializers/auth/feishu.py:10 users/models/user.py:780
msgid "FeiShu"
msgstr "本を飛ばす"
#: audits/signal_handlers/login_log.py:33 authentication/views/dingtalk.py:179
#: authentication/views/login.py:79 notifications/backends/__init__.py:12
#: authentication/views/login.py:80 notifications/backends/__init__.py:12
#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:779
msgid "DingTalk"
msgstr "DingTalk"
@@ -2237,15 +2235,15 @@ msgstr "本を飛ばすは拘束されていません"
msgid "Your password is invalid"
msgstr "パスワードが無効です"
#: authentication/errors/redirect.py:85 authentication/mixins.py:306
#: authentication/errors/redirect.py:85 authentication/mixins.py:307
msgid "Your password is too simple, please change it for security"
msgstr "パスワードがシンプルすぎるので、セキュリティのために変更してください"
#: authentication/errors/redirect.py:93 authentication/mixins.py:313
#: authentication/errors/redirect.py:93 authentication/mixins.py:314
msgid "You should to change your password before login"
msgstr "ログインする前にパスワードを変更する必要があります"
#: authentication/errors/redirect.py:101 authentication/mixins.py:320
#: authentication/errors/redirect.py:101 authentication/mixins.py:321
msgid "Your password has expired, please reset before logging in"
msgstr ""
"パスワードの有効期限が切れました。ログインする前にリセットしてください。"
@@ -2348,11 +2346,11 @@ msgstr "無効にする電話番号をクリアする"
msgid "Authentication failed (before login check failed): {}"
msgstr "認証に失敗しました (ログインチェックが失敗する前): {}"
#: authentication/mixins.py:256
#: authentication/mixins.py:257
msgid "The MFA type ({}) is not enabled"
msgstr "MFAタイプ ({}) が有効になっていない"
#: authentication/mixins.py:296
#: authentication/mixins.py:297
msgid "Please change your password"
msgstr "パスワードを変更してください"
@@ -2812,19 +2810,23 @@ msgstr "本を飛ばすからユーザーを取得できませんでした"
msgid "Please login with a password and then bind the FeiShu"
msgstr "パスワードでログインしてから本を飛ばすをバインドしてください"
#: authentication/views/login.py:181
#: authentication/views/login.py:182
msgid "Redirecting"
msgstr "リダイレクト"
#: authentication/views/login.py:182
#: authentication/views/login.py:183
msgid "Redirecting to {} authentication"
msgstr "{} 認証へのリダイレクト"
#: authentication/views/login.py:205
#: authentication/views/login.py:206
msgid "Please enable cookies and try again."
msgstr "クッキーを有効にして、もう一度お試しください。"
#: authentication/views/login.py:307
#: authentication/views/login.py:238
msgid "User email already exists ({})"
msgstr "ユーザー メールボックスは既に存在します ({})"
#: authentication/views/login.py:318
msgid ""
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
" Don't close this page"
@@ -2832,15 +2834,15 @@ msgstr ""
"<b>{}</b> 確認を待ちます。彼女/彼へのリンクをコピーすることもできます <br/>\n"
" このページを閉じないでください"
#: authentication/views/login.py:312
#: authentication/views/login.py:323
msgid "No ticket found"
msgstr "チケットが見つかりません"
#: authentication/views/login.py:348
#: authentication/views/login.py:359
msgid "Logout success"
msgstr "ログアウト成功"
#: authentication/views/login.py:349
#: authentication/views/login.py:360
msgid "Logout success, return login page"
msgstr "ログアウト成功、ログインページを返す"
@@ -3509,15 +3511,15 @@ msgstr "終了しました"
msgid "Time cost"
msgstr "時を過ごす"
#: ops/tasks.py:32
#: ops/tasks.py:34
msgid "Run ansible task"
msgstr "Ansible タスクを実行する"
#: ops/tasks.py:61
#: ops/tasks.py:63
msgid "Run ansible task execution"
msgstr "Ansible タスクの実行を開始する"
#: ops/tasks.py:77
#: ops/tasks.py:79
msgid "Clear celery periodic tasks"
msgstr "タスクログを定期的にクリアする"
@@ -3735,7 +3737,7 @@ msgstr "内部の役割は、破壊することはできません"
msgid "The role has been bound to users, can't be destroy"
msgstr "ロールはユーザーにバインドされており、破壊することはできません"
#: rbac/api/role.py:83
#: rbac/api/role.py:87
msgid "Internal role, can't be update"
msgstr "内部ロール、更新できません"
@@ -3747,27 +3749,27 @@ msgstr "{} 少なくとも1つのシステムロール"
msgid "RBAC"
msgstr "RBAC"
#: rbac/builtin.py:109
#: rbac/builtin.py:113
msgid "SystemAdmin"
msgstr "システム管理者"
#: rbac/builtin.py:112
#: rbac/builtin.py:116
msgid "SystemAuditor"
msgstr "システム監査人"
#: rbac/builtin.py:115
#: rbac/builtin.py:119
msgid "SystemComponent"
msgstr "システムコンポーネント"
#: rbac/builtin.py:121
#: rbac/builtin.py:125
msgid "OrgAdmin"
msgstr "組織管理者"
#: rbac/builtin.py:124
#: rbac/builtin.py:128
msgid "OrgAuditor"
msgstr "監査員を組織する"
#: rbac/builtin.py:127
#: rbac/builtin.py:131
msgid "OrgUser"
msgstr "組織ユーザー"
@@ -5408,9 +5410,9 @@ msgstr "セッション"
msgid "Risk level"
msgstr "リスクレベル"
#: terminal/connect_methods.py:47 terminal/connect_methods.py:48
#: terminal/connect_methods.py:49 terminal/connect_methods.py:50
#: terminal/connect_methods.py:51
#: terminal/connect_methods.py:54 terminal/connect_methods.py:55
#: terminal/connect_methods.py:56 terminal/connect_methods.py:57
#: terminal/connect_methods.py:58
msgid "DB Client"
msgstr "データベース クライアント"
@@ -5899,11 +5901,11 @@ msgstr "孤立したセッションをクリアする"
msgid "Upload session replay to external storage"
msgstr "セッションの記録を外部ストレージにアップロードする"
#: terminal/tasks.py:83
#: terminal/tasks.py:84
msgid "Run applet host deployment"
msgstr "アプリケーション マシンの展開を実行する"
#: terminal/tasks.py:90
#: terminal/tasks.py:94
msgid "Install applet"
msgstr "アプリをインストールする"
@@ -7388,30 +7390,3 @@ msgstr "究極のエディション"
#: xpack/plugins/license/models.py:85
msgid "Community edition"
msgstr "コミュニティ版"
#, fuzzy
#~| msgid "Only admin users"
#~ msgid "Unix admin user"
#~ msgstr "管理者のみ"
#, fuzzy
#~| msgid "Only admin users"
#~ msgid "Windows admin user"
#~ msgstr "管理者のみ"
#, fuzzy
#~| msgid "Only admin users"
#~ msgid "Linux admin user"
#~ msgstr "管理者のみ"
#~ msgid "Can push account to asset"
#~ msgstr "アカウントをアセットにプッシュできます"
#~ msgid "Add asset to node"
#~ msgstr "ノードにアセットを追加する"
#~ msgid "Move asset to node"
#~ msgstr "アセットをノードに移動する"
#~ msgid "Remove asset from node"
#~ msgstr "ノードからアセットを削除"

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-02-23 19:11+0800\n"
"POT-Creation-Date: 2023-03-02 16:00+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n"
@@ -393,7 +393,7 @@ msgid "Date last login"
msgstr "最后登录日期"
#: accounts/models/automations/gather_account.py:15
#: accounts/models/automations/push_account.py:13 accounts/models/base.py:34
#: accounts/models/automations/push_account.py:14 accounts/models/base.py:34
#: acls/serializers/base.py:18 acls/serializers/base.py:49
#: assets/models/_user.py:23 audits/models.py:157 authentication/forms.py:25
#: authentication/forms.py:27 authentication/models/temp_token.py:9
@@ -418,11 +418,11 @@ msgstr "自动化收集账号"
msgid "Gather asset accounts"
msgstr "收集账号"
#: accounts/models/automations/push_account.py:12
#: accounts/models/automations/push_account.py:13
msgid "Triggers"
msgstr "触发方式"
#: accounts/models/automations/push_account.py:14 acls/models/base.py:81
#: accounts/models/automations/push_account.py:15 acls/models/base.py:81
#: acls/serializers/base.py:81 acls/serializers/login_acl.py:25
#: assets/models/cmd_filter.py:81 audits/models.py:65 audits/serializers.py:82
#: authentication/serializers/connect_token_secret.py:109
@@ -430,7 +430,7 @@ msgstr "触发方式"
msgid "Action"
msgstr "动作"
#: accounts/models/automations/push_account.py:40
#: accounts/models/automations/push_account.py:43
msgid "Push asset account"
msgstr "账号推送"
@@ -446,7 +446,7 @@ msgstr "账号验证"
#: assets/models/group.py:20 assets/models/label.py:18
#: assets/models/platform.py:21 assets/models/platform.py:76
#: assets/serializers/asset/common.py:67 assets/serializers/asset/common.py:143
#: assets/serializers/platform.py:91 assets/serializers/platform.py:136
#: assets/serializers/platform.py:133
#: authentication/serializers/connect_token_secret.py:103 ops/mixin.py:21
#: ops/models/adhoc.py:21 ops/models/celery.py:15 ops/models/celery.py:57
#: ops/models/job.py:91 ops/models/playbook.py:23 ops/serializers/job.py:19
@@ -529,7 +529,7 @@ msgstr "已托管密码"
#: accounts/serializers/account/account.py:75 applications/models.py:11
#: assets/models/label.py:21 assets/models/platform.py:77
#: assets/serializers/asset/common.py:120 assets/serializers/cagegory.py:8
#: assets/serializers/platform.py:97 assets/serializers/platform.py:137
#: assets/serializers/platform.py:94 assets/serializers/platform.py:134
#: perms/serializers/user_permission.py:26 settings/models.py:35
#: tickets/models/ticket/apply_application.py:13
msgid "Category"
@@ -540,7 +540,7 @@ msgstr "类别"
#: acls/serializers/command_acl.py:18 applications/models.py:14
#: assets/models/_user.py:50 assets/models/automations/base.py:20
#: assets/models/cmd_filter.py:74 assets/models/platform.py:78
#: assets/serializers/asset/common.py:121 assets/serializers/platform.py:96
#: assets/serializers/asset/common.py:121 assets/serializers/platform.py:93
#: audits/serializers.py:48
#: authentication/serializers/connect_token_secret.py:116 ops/models/job.py:102
#: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:31
@@ -737,7 +737,7 @@ msgstr "激活中"
#: authentication/models/sso_token.py:16
#: notifications/models/notification.py:12
#: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:58
#: perms/serializers/permission.py:23 rbac/builtin.py:118
#: perms/serializers/permission.py:23 rbac/builtin.py:122
#: rbac/models/rolebinding.py:49 terminal/backends/command/models.py:19
#: terminal/models/session/session.py:30 terminal/models/session/sharing.py:32
#: terminal/notifications.py:96 terminal/notifications.py:144
@@ -902,7 +902,7 @@ msgstr "应用程序"
msgid "Can match application"
msgstr "匹配应用"
#: assets/api/asset/asset.py:142
#: assets/api/asset/asset.py:143
msgid "Cannot create asset directly, you should create a host or other"
msgstr "不能直接创建资产, 你应该创建主机或其他资产"
@@ -936,10 +936,8 @@ msgid "No account"
msgstr "没有账号"
#: assets/automations/ping_gateway/manager.py:36
#, fuzzy
#| msgid "Assets amount"
msgid "Asset, {}, using account {}"
msgstr "资产数量"
msgstr "资产, {}, 使用账号 {}"
#: assets/automations/ping_gateway/manager.py:55
#, python-brace-format
@@ -1451,23 +1449,23 @@ msgstr "元数据"
msgid "Internal"
msgstr "内置"
#: assets/models/platform.py:83 assets/serializers/platform.py:94
#: assets/models/platform.py:83 assets/serializers/platform.py:91
msgid "Charset"
msgstr "编码"
#: assets/models/platform.py:85 assets/serializers/platform.py:122
#: assets/models/platform.py:85 assets/serializers/platform.py:119
msgid "Domain enabled"
msgstr "启用网域"
#: assets/models/platform.py:87 assets/serializers/platform.py:121
#: assets/models/platform.py:87 assets/serializers/platform.py:118
msgid "Su enabled"
msgstr "启用账号切换"
#: assets/models/platform.py:88 assets/serializers/platform.py:104
#: assets/models/platform.py:88 assets/serializers/platform.py:101
msgid "Su method"
msgstr "账号切换方式"
#: assets/models/platform.py:90 assets/serializers/platform.py:101
#: assets/models/platform.py:90 assets/serializers/platform.py:98
msgid "Automation"
msgstr "自动化"
@@ -1480,7 +1478,7 @@ msgstr "%(value)s is not an even number"
msgid "Auto fill"
msgstr "自动代填"
#: assets/serializers/asset/common.py:123 assets/serializers/platform.py:99
#: assets/serializers/asset/common.py:123 assets/serializers/platform.py:96
#: authentication/serializers/connect_token_secret.py:28
#: authentication/serializers/connect_token_secret.py:66
#: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:99
@@ -1621,7 +1619,7 @@ msgstr "收集账号方式"
msgid "Primary"
msgstr "主要的"
#: assets/serializers/platform.py:123
#: assets/serializers/platform.py:120
msgid "Default Domain"
msgstr "默认网域"
@@ -1930,20 +1928,20 @@ msgid "Auth Token"
msgstr "认证令牌"
#: audits/signal_handlers/login_log.py:31 authentication/notifications.py:73
#: authentication/views/login.py:73 authentication/views/wecom.py:177
#: authentication/views/login.py:74 authentication/views/wecom.py:177
#: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10
#: users/models/user.py:778
msgid "WeCom"
msgstr "企业微信"
#: audits/signal_handlers/login_log.py:32 authentication/views/feishu.py:144
#: authentication/views/login.py:85 notifications/backends/__init__.py:14
#: authentication/views/login.py:86 notifications/backends/__init__.py:14
#: settings/serializers/auth/feishu.py:10 users/models/user.py:780
msgid "FeiShu"
msgstr "飞书"
#: audits/signal_handlers/login_log.py:33 authentication/views/dingtalk.py:179
#: authentication/views/login.py:79 notifications/backends/__init__.py:12
#: authentication/views/login.py:80 notifications/backends/__init__.py:12
#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:779
msgid "DingTalk"
msgstr "钉钉"
@@ -2217,15 +2215,15 @@ msgstr "没有绑定飞书"
msgid "Your password is invalid"
msgstr "您的密码无效"
#: authentication/errors/redirect.py:85 authentication/mixins.py:306
#: authentication/errors/redirect.py:85 authentication/mixins.py:307
msgid "Your password is too simple, please change it for security"
msgstr "你的密码过于简单,为了安全,请修改"
#: authentication/errors/redirect.py:93 authentication/mixins.py:313
#: authentication/errors/redirect.py:93 authentication/mixins.py:314
msgid "You should to change your password before login"
msgstr "登录完成前,请先修改密码"
#: authentication/errors/redirect.py:101 authentication/mixins.py:320
#: authentication/errors/redirect.py:101 authentication/mixins.py:321
msgid "Your password has expired, please reset before logging in"
msgstr "您的密码已过期,先修改再登录"
@@ -2326,11 +2324,11 @@ msgstr "清空手机号码禁用"
msgid "Authentication failed (before login check failed): {}"
msgstr "认证失败(登录前检查失败): {}"
#: authentication/mixins.py:256
#: authentication/mixins.py:257
msgid "The MFA type ({}) is not enabled"
msgstr "该 MFA ({}) 方式没有启用"
#: authentication/mixins.py:296
#: authentication/mixins.py:297
msgid "Please change your password"
msgstr "请修改密码"
@@ -2782,19 +2780,23 @@ msgstr "从飞书获取用户失败"
msgid "Please login with a password and then bind the FeiShu"
msgstr "请使用密码登录,然后绑定飞书"
#: authentication/views/login.py:181
#: authentication/views/login.py:182
msgid "Redirecting"
msgstr "跳转中"
#: authentication/views/login.py:182
#: authentication/views/login.py:183
msgid "Redirecting to {} authentication"
msgstr "正在跳转到 {} 认证"
#: authentication/views/login.py:205
#: authentication/views/login.py:206
msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie"
#: authentication/views/login.py:307
#: authentication/views/login.py:238
msgid "User email already exists ({})"
msgstr "用户邮箱已存在 ({})"
#: authentication/views/login.py:318
msgid ""
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
" Don't close this page"
@@ -2802,15 +2804,15 @@ msgstr ""
"等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n"
" 不要关闭本页面"
#: authentication/views/login.py:312
#: authentication/views/login.py:323
msgid "No ticket found"
msgstr "没有发现工单"
#: authentication/views/login.py:348
#: authentication/views/login.py:359
msgid "Logout success"
msgstr "退出登录成功"
#: authentication/views/login.py:349
#: authentication/views/login.py:360
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
@@ -3474,15 +3476,15 @@ msgstr "是否完成"
msgid "Time cost"
msgstr "花费时间"
#: ops/tasks.py:32
#: ops/tasks.py:34
msgid "Run ansible task"
msgstr "运行 Ansible 任务"
#: ops/tasks.py:61
#: ops/tasks.py:63
msgid "Run ansible task execution"
msgstr "开始执行 Ansible 任务"
#: ops/tasks.py:77
#: ops/tasks.py:79
msgid "Clear celery periodic tasks"
msgstr "清理周期任务"
@@ -3699,7 +3701,7 @@ msgstr "内部角色,不能删除"
msgid "The role has been bound to users, can't be destroy"
msgstr "角色已绑定用户,不能删除"
#: rbac/api/role.py:83
#: rbac/api/role.py:87
msgid "Internal role, can't be update"
msgstr "内部角色,不能更新"
@@ -3711,27 +3713,27 @@ msgstr "{} 至少有一个系统角色"
msgid "RBAC"
msgstr "RBAC"
#: rbac/builtin.py:109
#: rbac/builtin.py:113
msgid "SystemAdmin"
msgstr "系统管理员"
#: rbac/builtin.py:112
#: rbac/builtin.py:116
msgid "SystemAuditor"
msgstr "系统审计员"
#: rbac/builtin.py:115
#: rbac/builtin.py:119
msgid "SystemComponent"
msgstr "系统组件"
#: rbac/builtin.py:121
#: rbac/builtin.py:125
msgid "OrgAdmin"
msgstr "组织管理员"
#: rbac/builtin.py:124
#: rbac/builtin.py:128
msgid "OrgAuditor"
msgstr "组织审计员"
#: rbac/builtin.py:127
#: rbac/builtin.py:131
msgid "OrgUser"
msgstr "组织用户"
@@ -5336,9 +5338,9 @@ msgstr "会话"
msgid "Risk level"
msgstr "风险等级"
#: terminal/connect_methods.py:47 terminal/connect_methods.py:48
#: terminal/connect_methods.py:49 terminal/connect_methods.py:50
#: terminal/connect_methods.py:51
#: terminal/connect_methods.py:54 terminal/connect_methods.py:55
#: terminal/connect_methods.py:56 terminal/connect_methods.py:57
#: terminal/connect_methods.py:58
msgid "DB Client"
msgstr "数据库客户端"
@@ -5825,11 +5827,11 @@ msgstr "清除孤儿会话"
msgid "Upload session replay to external storage"
msgstr "上传会话录像到外部存储"
#: terminal/tasks.py:83
#: terminal/tasks.py:84
msgid "Run applet host deployment"
msgstr "运行应用机部署"
#: terminal/tasks.py:90
#: terminal/tasks.py:94
msgid "Install applet"
msgstr "安装应用"

View File

@@ -62,10 +62,10 @@ class PlaybookFileBrowserAPIView(APIView):
rbac_perms = ()
permission_classes = (RBACPermission,)
rbac_perms = {
'GET': 'ops.change_playbooks',
'POST': 'ops.change_playbooks',
'DELETE': 'ops.change_playbooks',
'PATCH': 'ops.change_playbooks',
'GET': 'ops.change_playbook',
'POST': 'ops.change_playbook',
'DELETE': 'ops.change_playbook',
'PATCH': 'ops.change_playbook',
}
protected_files = ['root', 'main.yml']

View File

@@ -3,8 +3,10 @@
from celery import shared_task
from celery.exceptions import SoftTimeLimitExceeded
from django.utils.translation import ugettext_lazy as _
from django_celery_beat.models import PeriodicTask
from common.utils import get_logger, get_object_or_none
from ops.celery import app
from orgs.utils import tmp_to_org, tmp_to_root_org
from .celery.decorator import (
register_as_period_task, after_app_ready_start
@@ -19,7 +21,7 @@ from .notifications import ServerPerformanceCheckUtil
logger = get_logger(__file__)
def job_task_activity_callback(self, job_id, trigger):
def job_task_activity_callback(self, job_id, *args, **kwargs):
job = get_object_or_none(Job, id=job_id)
if not job:
return
@@ -48,7 +50,7 @@ def run_ops_job(job_id):
logger.error("Start adhoc execution error: {}".format(e))
def job_execution_task_activity_callback(self, execution_id, trigger):
def job_execution_task_activity_callback(self, execution_id, *args, **kwargs):
execution = get_object_or_none(JobExecution, id=execution_id)
if not execution:
return
@@ -78,16 +80,14 @@ def run_ops_job_execution(execution_id, **kwargs):
@after_app_ready_start
def clean_celery_periodic_tasks():
"""清除celery定时任务"""
need_cleaned_tasks = [
'handle_be_interrupted_change_auth_task_periodic',
]
logger.info('Start clean celery periodic tasks: {}'.format(need_cleaned_tasks))
for task_name in need_cleaned_tasks:
logger.info('Start clean task: {}'.format(task_name))
task = get_celery_periodic_task(task_name)
if task is None:
logger.info('Task does not exist: {}'.format(task_name))
logger.info('Start clean celery periodic tasks.')
register_tasks = PeriodicTask.objects.all()
for task in register_tasks:
if task.task in app.tasks:
continue
task_name = task.name
logger.info('Start clean task: {}'.format(task_name))
disable_celery_periodic_task(task_name)
delete_celery_periodic_task(task_name)
task = get_celery_periodic_task(task_name)

View File

@@ -13,7 +13,7 @@ class CeleryTaskLogView(PermissionsMixin, TemplateView):
template_name = 'ops/celery_task_log.html'
permission_classes = [RBACPermission]
rbac_perms = {
'GET': 'ops.view_celerytask'
'GET': 'ops.view_celerytaskexecution'
}
def get_context_data(self, **kwargs):

View File

@@ -114,9 +114,7 @@ class OrgResourceStatisticsCache(OrgRelatedCache):
@staticmethod
def compute_total_count_today_active_assets():
t = local_zero_hour()
return Session.objects.filter(
date_start__gte=t, is_success=False
).values('asset_id').distinct().count()
return Session.objects.filter(date_start__gte=t).values('asset_id').distinct().count()
@staticmethod
def compute_total_count_today_failed_sessions():

View File

@@ -102,7 +102,10 @@ def on_post_delete_refresh_org_resource_statistics_cache(sender, instance, **kwa
def _refresh_session_org_resource_statistics_cache(instance: Session):
cache_field_name = ['total_count_online_users', 'total_count_online_sessions', 'total_count_today_failed_sessions']
cache_field_name = [
'total_count_online_users', 'total_count_online_sessions',
'total_count_today_active_assets','total_count_today_failed_sessions'
]
org_cache = OrgResourceStatisticsCache(instance.org)
org_cache.expire(*cache_field_name)

View File

@@ -30,6 +30,12 @@ class BaseUserPermedAssetsApi(SelfOrPKUserMixin, ListAPIView):
filterset_class = AssetFilterSet
serializer_class = serializers.AssetPermedSerializer
def get_serializer_class(self):
serializer_class = super().get_serializer_class()
if self.request.query_params.get('id'):
serializer_class = serializers.AssetPermedDetailSerializer
return serializer_class
def get_queryset(self):
if getattr(self, 'swagger_fake_view', False):
return Asset.objects.none()

View File

@@ -23,6 +23,7 @@ def migrate_app_perms_to_assets(apps, schema_editor):
asset_permission = asset_permission_model()
for attr in attrs:
setattr(asset_permission, attr, getattr(app_perm, attr))
asset_permission.name = f"App-{app_perm.name}"
asset_permissions.append(asset_permission)
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
while True:
asset_permissions = asset_permission_model.objects \
.prefetch_related('system_users')[count:bulk_size]
.prefetch_related('system_users')[count:bulk_size]
if not asset_permissions:
break
count += len(asset_permissions)
count += len(asset_permissions)
updated = []
for asset_permission in asset_permissions:
asset_permission.accounts = [s.username for s in asset_permission.system_users.all()]

View File

@@ -15,7 +15,7 @@ from perms.serializers.permission import ActionChoicesField
__all__ = [
'NodePermedSerializer', 'AssetPermedSerializer',
'AccountsPermedSerializer'
'AssetPermedDetailSerializer', 'AccountsPermedSerializer'
]
@@ -46,6 +46,12 @@ class AssetPermedSerializer(OrgResourceModelSerializerMixin):
return queryset
class AssetPermedDetailSerializer(AssetPermedSerializer):
class Meta(AssetPermedSerializer.Meta):
fields = AssetPermedSerializer.Meta.fields + ['spec_info']
read_only_fields = fields
class NodePermedSerializer(serializers.ModelSerializer):
class Meta:
model = Node

View File

@@ -1,5 +1,6 @@
from collections import defaultdict
from orgs.utils import tmp_to_org
from accounts.models import Account
from accounts.const import AliasAccount
from .permission import AssetPermissionUtil
@@ -16,10 +17,11 @@ class PermAccountUtil(AssetPermissionUtil):
:param asset: Asset
:param account_name: 可能是 @USER @INPUT 字符串
"""
permed_accounts = self.get_permed_accounts_for_user(user, asset)
accounts_mapper = {account.alias: account for account in permed_accounts}
account = accounts_mapper.get(account_name)
return account
with tmp_to_org(asset.org):
permed_accounts = self.get_permed_accounts_for_user(user, asset)
accounts_mapper = {account.alias: account for account in permed_accounts}
account = accounts_mapper.get(account_name)
return account
def get_permed_accounts_for_user(self, user, asset):
""" 获取授权给用户某个资产的账号 """

View File

@@ -18,14 +18,19 @@ user_perms = (
('assets', 'asset', 'match', 'asset'),
('assets', 'systemuser', 'match', 'systemuser'),
('assets', 'node', 'match', 'node'),
("ops", "adhoc", "*", "*"),
("ops", "playbook", "*", "*"),
("ops", "job", "*", "*"),
("ops", "jobexecution", "*", "*"),
("ops", "celerytaskexecution", "view", "*"),
)
system_user_perms = (
('authentication', 'connectiontoken', 'add,change,view', 'connectiontoken'),
('authentication', 'temptoken', 'add,change,view', 'temptoken'),
('authentication', 'accesskey', '*', '*'),
('tickets', 'ticket', 'view', 'ticket'),
) + user_perms + _view_all_joined_org_perms
('authentication', 'connectiontoken', 'add,change,view', 'connectiontoken'),
('authentication', 'temptoken', 'add,change,view', 'temptoken'),
('authentication', 'accesskey', '*', '*'),
('tickets', 'ticket', 'view', 'ticket'),
) + user_perms + _view_all_joined_org_perms
_auditor_perms = (
('rbac', 'menupermission', 'view', 'audit'),
@@ -41,7 +46,6 @@ auditor_perms = user_perms + _auditor_perms
system_auditor_perms = system_user_perms + _auditor_perms + _view_root_perms
app_exclude_perms = [
('users', 'user', 'add,delete', 'user'),
('orgs', 'org', 'add,delete,change', 'org'),

View File

@@ -135,7 +135,7 @@ only_system_permissions = (
('xpack', 'license', '*', '*'),
('settings', 'setting', '*', '*'),
('tickets', '*', '*', '*'),
('ops', 'task', 'view', 'taskmonitor'),
('ops', 'celerytask', 'view', 'taskmonitor'),
('terminal', 'terminal', '*', '*'),
('terminal', 'commandstorage', '*', '*'),
('terminal', 'replaystorage', '*', '*'),

View File

@@ -97,13 +97,13 @@ class RBACPermission(permissions.DjangoModelPermissions):
else:
model_cls = queryset.model
except AssertionError as e:
logger.error(f'Error get model cls: {e}')
# logger.error(f'Error get model cls: {e}')
model_cls = None
except AttributeError as e:
logger.error(f'Error get model cls: {e}')
# logger.error(f'Error get model cls: {e}')
model_cls = None
except Exception as e:
logger.error('Error get model class: {} of {}'.format(e, view))
# logger.error('Error get model class: {} of {}'.format(e, view))
raise e
return model_cls

View File

@@ -42,7 +42,7 @@ class MailTestingAPI(APIView):
# if k.startswith('EMAIL'):
# setattr(settings, k, v)
try:
subject = settings.EMAIL_SUBJECT_PREFIX + "Test"
subject = settings.EMAIL_SUBJECT_PREFIX or '' + "Test"
message = "Test smtp setting"
email_from = email_from or email_host_user
email_recipient = email_recipient or email_from

View File

@@ -9,6 +9,13 @@ __all__ = ['FeiShuSettingSerializer']
class FeiShuSettingSerializer(serializers.Serializer):
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_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

@@ -17,17 +17,17 @@ class WebMethod(TextChoices):
@classmethod
def get_methods(cls):
return {
methods = {
Protocol.ssh: [cls.web_cli, cls.web_sftp],
Protocol.telnet: [cls.web_cli],
Protocol.rdp: [cls.web_gui],
Protocol.vnc: [cls.web_gui],
Protocol.mysql: [cls.web_cli, cls.web_gui],
Protocol.mariadb: [cls.web_cli, cls.web_gui],
Protocol.oracle: [cls.web_cli, cls.web_gui],
Protocol.postgresql: [cls.web_cli, cls.web_gui],
Protocol.sqlserver: [cls.web_cli, cls.web_gui],
Protocol.mysql: [cls.web_cli],
Protocol.mariadb: [cls.web_cli],
Protocol.oracle: [cls.web_cli],
Protocol.postgresql: [cls.web_cli],
Protocol.sqlserver: [cls.web_cli],
Protocol.redis: [cls.web_cli],
Protocol.mongodb: [cls.web_cli],
Protocol.clickhouse: [cls.web_cli],
@@ -35,6 +35,13 @@ class WebMethod(TextChoices):
Protocol.k8s: [cls.web_cli],
Protocol.http: []
}
if not settings.XPACK_ENABLED:
return methods
web_gui_dbs = [Protocol.mysql, Protocol.mariadb, Protocol.oracle, Protocol.postgresql]
for db in web_gui_dbs:
methods[db].append(cls.web_gui)
return methods
class NativeClient(TextChoices):
@@ -130,8 +137,6 @@ class AppletMethod:
from .models import Applet, AppletHost
methods = defaultdict(list)
if not settings.XPACK_ENABLED:
return methods
has_applet_hosts = AppletHost.objects.all().exists()
applets = Applet.objects.filter(is_active=True)

View File

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

View File

@@ -80,14 +80,20 @@ def upload_session_replay_to_external_storage(session_id):
return
@shared_task(verbose_name=_('Run applet host deployment'), activity_callback=lambda did: ([did], ))
@shared_task(
verbose_name=_('Run applet host deployment'),
activity_callback=lambda self, did, *args, **kwargs: ([did], )
)
def run_applet_host_deployment(did):
with tmp_to_builtin_org(system=1):
deployment = AppletHostDeployment.objects.get(id=did)
deployment.start()
@shared_task(verbose_name=_('Install applet'), activity_callback=lambda did, applet_id: ([did],))
@shared_task(
verbose_name=_('Install applet'),
activity_callback=lambda self, did, applet_id, *args, **kwargs: ([did],)
)
def run_applet_host_deployment_install_applet(did, applet_id):
with tmp_to_builtin_org(system=1):
deployment = AppletHostDeployment.objects.get(id=did)