diff --git a/apps/assets/const/protocol.py b/apps/assets/const/protocol.py index 7875e5ef4..a5f1d413a 100644 --- a/apps/assets/const/protocol.py +++ b/apps/assets/const/protocol.py @@ -58,36 +58,43 @@ class Protocol(ChoicesMixin, models.TextChoices): return { cls.mysql: { 'port': 3306, + 'setting': {}, + 'required': True, 'secret_types': ['password'], - 'setting': { - } }, cls.mariadb: { 'port': 3306, + 'required': True, 'secret_types': ['password'], }, cls.postgresql: { 'port': 5432, + 'required': True, 'secret_types': ['password'], }, cls.oracle: { 'port': 1521, + 'required': True, 'secret_types': ['password'], }, cls.sqlserver: { 'port': 1433, + 'required': True, 'secret_types': ['password'], }, cls.clickhouse: { 'port': 9000, + 'required': True, 'secret_types': ['password'], }, cls.mongodb: { 'port': 27017, + 'required': True, 'secret_types': ['password'], }, cls.redis: { 'port': 6379, + 'required': True, 'secret_types': ['password'], }, } @@ -97,6 +104,7 @@ class Protocol(ChoicesMixin, models.TextChoices): return { cls.k8s: { 'port': 443, + 'required': True, 'secret_types': ['token'], }, cls.http: { diff --git a/apps/assets/const/types.py b/apps/assets/const/types.py index dcdb03ed3..379a0d188 100644 --- a/apps/assets/const/types.py +++ b/apps/assets/const/types.py @@ -302,8 +302,8 @@ class AllTypes(ChoicesMixin): 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'] = setting.pop('required', False) - p['default'] = setting.pop('default', False) + p['required'] = p.pop('required', False) + p['default'] = p.pop('default', False) p['setting'] = {**setting, **p.get('setting', {})} platform_data = { diff --git a/apps/assets/serializers/domain.py b/apps/assets/serializers/domain.py index 6692ea03b..33dcffbe1 100644 --- a/apps/assets/serializers/domain.py +++ b/apps/assets/serializers/domain.py @@ -13,7 +13,7 @@ __all__ = ['DomainSerializer', 'DomainWithGatewaySerializer'] class DomainSerializer(BulkOrgResourceModelSerializer): gateways = ObjectRelatedField( - many=True, required=False, queryset=Asset.objects, label=_('Gateway') + many=True, required=False, label=_('Gateway'), read_only=True, ) assets = ObjectRelatedField( many=True, required=False, queryset=Asset.objects, label=_('Asset') diff --git a/apps/rbac/builtin.py b/apps/rbac/builtin.py index c3f748a27..ef8de353d 100644 --- a/apps/rbac/builtin.py +++ b/apps/rbac/builtin.py @@ -31,6 +31,7 @@ system_user_perms = ( _auditor_perms = ( ('rbac', 'menupermission', 'view', 'audit'), ('audits', '*', '*', '*'), + ('ops', 'jobauditlog', '*', '*'), ('terminal', 'commandstorage', 'view', 'commandstorage'), ('terminal', 'sessionreplay', 'view,download', 'sessionreplay'), ('terminal', 'session', '*', '*'), diff --git a/apps/static/img/avatar/admin.png b/apps/static/img/avatar/admin.png index 1948d1661..8462a8a18 100644 Binary files a/apps/static/img/avatar/admin.png and b/apps/static/img/avatar/admin.png differ diff --git a/apps/static/img/avatar/user.png b/apps/static/img/avatar/user.png index 1948d1661..8462a8a18 100644 Binary files a/apps/static/img/avatar/user.png and b/apps/static/img/avatar/user.png differ diff --git a/apps/terminal/api/applet/applet.py b/apps/terminal/api/applet/applet.py index c2b361ca6..5bd003a9c 100644 --- a/apps/terminal/api/applet/applet.py +++ b/apps/terminal/api/applet/applet.py @@ -87,6 +87,7 @@ class DownloadUploadMixin: class AppletViewSet(DownloadUploadMixin, JMSBulkModelViewSet): queryset = Applet.objects.all() serializer_class = serializers.AppletSerializer + filterset_fields = ['name', 'version', 'builtin', 'is_active'] search_fields = ['name', 'display_name', 'author'] rbac_perms = { 'upload': 'terminal.add_applet', diff --git a/apps/terminal/applets/navicat/README.md b/apps/terminal/applets/navicat/README.md index b46ffb9e5..ee18cd491 100644 --- a/apps/terminal/applets/navicat/README.md +++ b/apps/terminal/applets/navicat/README.md @@ -1,4 +1,3 @@ - ## Navicat Premium - 需要先手动导入License激活 diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index 6d0d0d3bf..1bad257f0 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -11,6 +11,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework.serializers import ValidationError from common.db.models import JMSBaseModel +from common.utils import lazyproperty __all__ = ['Applet', 'AppletPublication'] @@ -48,6 +49,14 @@ class Applet(JMSBaseModel): else: return default_storage.path('applets/{}'.format(self.name)) + @lazyproperty + def readme(self): + readme_file = os.path.join(self.path, 'README.md') + if os.path.isfile(readme_file): + with open(readme_file, 'r') as f: + return f.read() + return '' + @property def manifest(self): path = os.path.join(self.path, 'manifest.yml') diff --git a/apps/terminal/models/applet/host.py b/apps/terminal/models/applet/host.py index 37d15e64d..e179a8380 100644 --- a/apps/terminal/models/applet/host.py +++ b/apps/terminal/models/applet/host.py @@ -114,6 +114,13 @@ class AppletHostDeployment(JMSBaseModel): ordering = ('-date_start',) def start(self, **kwargs): + # 重新初始化部署,applet host 关联的终端需要删除 + # 否则 tinker 会因终端注册名称相同,造成冲突,执行任务失败 + if self.host.terminal: + terminal = self.host.terminal + self.host.terminal = None + self.host.save() + terminal.delete() from ...automations.deploy_applet_host import DeployAppletHostManager manager = DeployAppletHostManager(self) manager.run(**kwargs) diff --git a/apps/terminal/serializers/applet.py b/apps/terminal/serializers/applet.py index 64ae6eda2..f05f424c2 100644 --- a/apps/terminal/serializers/applet.py +++ b/apps/terminal/serializers/applet.py @@ -31,7 +31,7 @@ class AppletSerializer(serializers.ModelSerializer): model = Applet fields_mini = ['id', 'name', 'display_name', 'is_active'] read_only_fields = [ - 'icon', 'date_created', 'date_updated', + 'icon', 'readme', 'date_created', 'date_updated', ] fields = fields_mini + [ 'version', 'author', 'type', 'protocols', diff --git a/apps/users/serializers/profile.py b/apps/users/serializers/profile.py index 287454146..304aac7b8 100644 --- a/apps/users/serializers/profile.py +++ b/apps/users/serializers/profile.py @@ -3,7 +3,7 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from common.utils import validate_ssh_public_key -from common.serializers.fields import EncryptedField +from common.serializers.fields import EncryptedField, LabeledChoiceField from ..models import User from .user import UserSerializer @@ -123,7 +123,7 @@ class UserProfileSerializer(UserSerializer): public_key_hash_md5 = serializers.CharField( source='get_public_key_hash_md5', required=False, read_only=True, max_length=128 ) - mfa_level = serializers.ChoiceField(choices=MFA_LEVEL_CHOICES, label=_('MFA'), required=False) + mfa_level = LabeledChoiceField(choices=MFA_LEVEL_CHOICES, label=_("MFA"), required=False) guide_url = serializers.SerializerMethodField() receive_backends = serializers.ListField(child=serializers.CharField(), read_only=True) console_orgs = UserOrgSerializer(many=True, read_only=True)