From 16400082e7445bcafc1fb48abe09d1319bc5bf5b Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 26 Feb 2025 15:34:04 +0800 Subject: [PATCH 1/6] perf: update i18n --- apps/i18n/lina/en.json | 3 +++ apps/i18n/lina/zh.json | 3 +++ 2 files changed, 6 insertions(+) diff --git a/apps/i18n/lina/en.json b/apps/i18n/lina/en.json index e39ef8f80..37f8d37e1 100644 --- a/apps/i18n/lina/en.json +++ b/apps/i18n/lina/en.json @@ -1069,6 +1069,7 @@ "RunningPath": "Running path", "RunningPathHelpText": "Enter the run path of the script, this setting only applies to shell scripts", "RunningTimes": "Last 5 run times", + "RunningSummary": "Running summary", "SCP": "Sangfor cloud platform", "SMS": "Message", "SMSProvider": "SMS service provider", @@ -1258,6 +1259,7 @@ "TaskList": "Tasks", "TaskMonitor": "Monitoring", "TaskPath": "Task path", + "TaskSummary": "Task summary", "TechnologyConsult": "Technical consultation", "TempPasswordTip": "The temporary password is valid for 300 seconds and becomes invalid immediately after use", "TempToken": "Temporary tokens", @@ -1490,6 +1492,7 @@ "ResetSecret": "Secret can be changed", "Connectable": "Connectable", "ExecutionRecord": "Execution records", + "ExecutionSummary": "Execution summary", "Documentation": "Documentation", "setVariable": "Set variable", "VerifyFace": "Verify face", diff --git a/apps/i18n/lina/zh.json b/apps/i18n/lina/zh.json index 0c581ecb7..3986a318a 100644 --- a/apps/i18n/lina/zh.json +++ b/apps/i18n/lina/zh.json @@ -1048,6 +1048,7 @@ "RunningPath": "运行路径", "RunningPathHelpText": "填写脚本的运行路径,此设置仅 shell 脚本生效", "RunningTimes": "最近5次运行时间", + "RunningSummary": "运行中", "SCP": "深信服云平台", "SMS": "短信", "SMSProvider": "短信服务商", @@ -1237,6 +1238,7 @@ "TaskList": "任务列表", "TaskMonitor": "任务监控", "TaskPath": "任务路径", + "TaskSummary": "任务汇总", "TechnologyConsult": "技术咨询", "TempPasswordTip": "临时密码有效期为 300 秒,使用后立刻失效", "TempToken": "临时密码", @@ -1474,6 +1476,7 @@ "DiscoveredAccountList": "发现账号", "AccountDiscoverTask": "账号发现", "ExecutionRecord": "执行记录", + "ExecutionSummary": "执行状态", "FoundAccountInAssetDeleteMsg": "删除从系统资产中发现的找好", "RemoteAssetFoundAccountDeleteMsg": "删除从远端资产中发现的找好", "PushAccountHelpText": "若账号已存在,则使用账号密文进行推送;若账号不存在,则根据填写的密文创建账号,然后进行推送。" From 385bf47b11eec95503054b21781277f17a1e4187 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Wed, 26 Feb 2025 15:16:16 +0800 Subject: [PATCH 2/6] perf: Add last login date update for account on session save --- apps/accounts/filters.py | 2 +- apps/accounts/models/account.py | 5 +++++ apps/terminal/models/session/session.py | 5 +++++ apps/terminal/signal_handlers/session.py | 5 +++-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/accounts/filters.py b/apps/accounts/filters.py index 32968ca71..406f5b832 100644 --- a/apps/accounts/filters.py +++ b/apps/accounts/filters.py @@ -108,7 +108,7 @@ class AccountFilterSet(BaseFilterSet): ) if kwargs: - queryset = queryset.filter(date_last_login__gte=date) + queryset = queryset.filter(**kwargs) return queryset @staticmethod diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index 1f363b678..5e90aee98 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -1,4 +1,5 @@ from django.db import models +from django.utils import timezone from django.utils.translation import gettext_lazy as _ from simple_history.models import HistoricalRecords @@ -167,6 +168,10 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount): return escape(value) + def update_last_login_date(self): + self.date_last_login = timezone.now() + self.save(update_fields=['date_last_login']) + def replace_history_model_with_mixin(): """ diff --git a/apps/terminal/models/session/session.py b/apps/terminal/models/session/session.py index 85a8e8273..b037e83bc 100644 --- a/apps/terminal/models/session/session.py +++ b/apps/terminal/models/session/session.py @@ -11,6 +11,7 @@ from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from accounts.models import Account from assets.models import Asset from common.const.signals import OP_LOG_SKIP_SIGNAL from common.utils import get_object_or_none, lazyproperty @@ -125,6 +126,10 @@ class Session(OrgModelMixin): def user_obj(self): return User.objects.get(id=self.user_id) + @property + def account_obj(self): + return get_object_or_none(Account, pk=self.account_id) + def can_replay(self): return self.has_replay diff --git a/apps/terminal/signal_handlers/session.py b/apps/terminal/signal_handlers/session.py index 4c79de9ed..6a75c42e8 100644 --- a/apps/terminal/signal_handlers/session.py +++ b/apps/terminal/signal_handlers/session.py @@ -5,9 +5,11 @@ from terminal.models import Session @receiver(pre_save, sender=Session) -def on_session_pre_save(sender, instance,**kwargs): +def on_session_pre_save(sender, instance, **kwargs): if instance.need_update_cmd_amount: instance.cmd_amount = instance.compute_command_amount() + if instance.account_obj: + instance.account_obj.update_last_login_date() @receiver(post_save, sender=Session) @@ -16,4 +18,3 @@ def on_session_finished(sender, instance: Session, created, **kwargs): return # 清理一次可能因 task 未执行的缓存数据 Session.unlock_session(instance.id) - From a498b22e807d947e2b00a4d8638fb0525d675721 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Wed, 26 Feb 2025 15:38:46 +0800 Subject: [PATCH 3/6] perf: Update account change secret status and date on successful secret change --- apps/accounts/automations/base/manager.py | 4 +++- apps/accounts/filters.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/accounts/automations/base/manager.py b/apps/accounts/automations/base/manager.py index b52dd4c2a..bc33f588e 100644 --- a/apps/accounts/automations/base/manager.py +++ b/apps/accounts/automations/base/manager.py @@ -138,10 +138,12 @@ class BaseChangeSecretPushManager(AccountBasePlaybookManager): account.secret = getattr(recorder, 'new_secret', account.secret) account.date_updated = timezone.now() + account.date_change_secret = timezone.now() + account.change_secret_status = ChangeSecretRecordStatusChoice.success with safe_db_connection(): recorder.save(update_fields=['status', 'date_finished']) - account.save(update_fields=['secret', 'date_updated']) + account.save(update_fields=['secret', 'date_updated', 'date_change_secret', 'change_secret_status']) self.summary['ok_accounts'] += 1 self.result['ok_accounts'].append( diff --git a/apps/accounts/filters.py b/apps/accounts/filters.py index 406f5b832..b305196df 100644 --- a/apps/accounts/filters.py +++ b/apps/accounts/filters.py @@ -7,6 +7,7 @@ from django_filters import rest_framework as drf_filters from assets.models import Node from common.drf.filters import BaseFilterSet from common.utils.timezone import local_zero_hour, local_now +from .const.automation import ChangeSecretRecordStatusChoice from .models import Account, GatheredAccount, ChangeSecretRecord, PushSecretRecord, IntegrationApplication @@ -104,7 +105,7 @@ class AccountFilterSet(BaseFilterSet): if name == "latest_secret_change_failed": queryset = queryset.filter(date_change_secret__gt=date).exclude( - change_secret_status="ok" + change_secret_status=ChangeSecretRecordStatusChoice.success ) if kwargs: From 110b3a334d41118db3b43bcbf69b4668d05c5127 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 27 Feb 2025 13:27:35 +0800 Subject: [PATCH 4/6] perf: update i18n --- apps/i18n/lina/en.json | 8 +++++++- apps/i18n/lina/zh.json | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/i18n/lina/en.json b/apps/i18n/lina/en.json index 37f8d37e1..540a5a28a 100644 --- a/apps/i18n/lina/en.json +++ b/apps/i18n/lina/en.json @@ -176,7 +176,7 @@ "AuthUserAttrMapHelpText": "The key on the left belongs to the jumpserver user properties, and the value on the right belongs to the authentication platform user properties", "Authentication": "Authentication", "AutoPush": "Auto push", - "Automations": "Automations", + "Automation": "Automation | Automations", "AverageTimeCost": "Average spend time", "AwaitingMyApproval": "Assigned", "Azure": "Azure (China)", @@ -657,6 +657,7 @@ "InterfaceSettings": "Appearance", "Interval": "Interval", "IntervalOfCreateUpdatePage": "Unit: hour", + "Integration": "Integration", "InvalidJson": "Invalid json", "InviteSuccess": "Invitation successful", "InviteUser": "Invite", @@ -1072,6 +1073,11 @@ "RunningSummary": "Running summary", "SCP": "Sangfor cloud platform", "SMS": "Message", + "AccountPushTask": "Account push task | Account push tasks", + "DiscoverAccountTask": "Account discovery task | Account discovery tasks", + "ChangeSecretTask": "Change secret task | Change secret tasks", + "AccountBackupTask": "Account backup task | Account backup tasks", + "ExecutionHistory": "Execution history", "SMSProvider": "SMS service provider", "SMTP": "Server", "SPECIAL_CHAR_REQUIRED": "Must contain special characters", diff --git a/apps/i18n/lina/zh.json b/apps/i18n/lina/zh.json index 3986a318a..88e04bcfb 100644 --- a/apps/i18n/lina/zh.json +++ b/apps/i18n/lina/zh.json @@ -168,7 +168,7 @@ "AuthUserAttrMapHelpText": "左侧的键为 JumpServer 用户属性,右侧的值为认证平台用户属性", "Authentication": "认证", "AutoPush": "自动推送", - "Automations": "自动化", + "Automation": "自动化", "AverageTimeCost": "平均花费时间", "AwaitingMyApproval": "待我审批", "Azure": "Azure (中国)", @@ -635,6 +635,7 @@ "InterfaceSettings": "界面设置", "Interval": "间隔", "IntervalOfCreateUpdatePage": "单位:时", + "Integration": "应用集成", "InvalidJson": "不是合法 JSON", "InviteSuccess": "邀请成功", "InviteUser": "邀请用户", @@ -1049,6 +1050,11 @@ "RunningPathHelpText": "填写脚本的运行路径,此设置仅 shell 脚本生效", "RunningTimes": "最近5次运行时间", "RunningSummary": "运行中", + "AccountPushTask": "账号推送任务", + "DiscoverAccountTask": "账号发现任务", + "ChangeSecretTask": "账号改密任务", + "AccountBackupTask": "账号备份任务", + "ExecutionHistory": "执行历史", "SCP": "深信服云平台", "SMS": "短信", "SMSProvider": "短信服务商", From 4b4d7b6787816f7d5f4bde4ee467077a19e64ca4 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Wed, 26 Feb 2025 18:43:47 +0800 Subject: [PATCH 5/6] perf: During MFA authentication, if the current code has been used and successfully authenticated, it cannot be used again for authentication --- apps/authentication/api/mfa.py | 4 +--- apps/authentication/mfa/__init__.py | 8 ++++---- apps/authentication/mfa/base.py | 23 ++++++++++++++++++++++- apps/authentication/mfa/custom.py | 2 +- apps/authentication/mfa/face.py | 4 ++-- apps/authentication/mfa/otp.py | 5 ++--- apps/authentication/mfa/radius.py | 2 +- apps/authentication/mfa/sms.py | 2 +- apps/common/decorators.py | 1 - apps/users/views/profile/otp.py | 2 +- 10 files changed, 35 insertions(+), 18 deletions(-) diff --git a/apps/authentication/api/mfa.py b/apps/authentication/api/mfa.py index 4a22ad6a1..9ce1a07be 100644 --- a/apps/authentication/api/mfa.py +++ b/apps/authentication/api/mfa.py @@ -3,7 +3,7 @@ from django.shortcuts import get_object_or_404 from django.utils.translation import gettext as _ from rest_framework import exceptions -from rest_framework.generics import CreateAPIView, RetrieveAPIView +from rest_framework.generics import CreateAPIView from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.serializers import ValidationError @@ -23,8 +23,6 @@ __all__ = [ ] - - # MFASelectAPi 原来的名字 class MFASendCodeApi(AuthMixin, CreateAPIView): """ diff --git a/apps/authentication/mfa/__init__.py b/apps/authentication/mfa/__init__.py index 29d9e2937..3aa3b34a1 100644 --- a/apps/authentication/mfa/__init__.py +++ b/apps/authentication/mfa/__init__.py @@ -1,5 +1,5 @@ -from .otp import MFAOtp, otp_failed_msg -from .sms import MFASms -from .radius import MFARadius from .custom import MFACustom -from .face import MFAFace \ No newline at end of file +from .face import MFAFace +from .otp import MFAOtp, otp_failed_msg +from .radius import MFARadius +from .sms import MFASms diff --git a/apps/authentication/mfa/base.py b/apps/authentication/mfa/base.py index 8575997d6..ace5a1424 100644 --- a/apps/authentication/mfa/base.py +++ b/apps/authentication/mfa/base.py @@ -1,10 +1,12 @@ import abc +from django.core.cache import cache from django.utils.translation import gettext_lazy as _ class BaseMFA(abc.ABC): placeholder = _('Please input security code') + skip_cache_check = False def __init__(self, user): """ @@ -14,6 +16,25 @@ class BaseMFA(abc.ABC): self.user = user self.request = None + def check_code(self, code): + if self.skip_cache_check: + return self._check_code(code) + + cache_key = f'{self.name}_{self.user.username}' + cache_code = cache.get(cache_key) + if cache_code == code: + return False, _( + "The two-factor code you entered has either already been used or has expired. " + "Please request a new one." + ) + + ok, msg = self._check_code(code) + if not ok: + return False, msg + + cache.set(cache_key, code, 60 * 5) + return True, msg + def is_authenticated(self): return self.user and self.user.is_authenticated @@ -38,7 +59,7 @@ class BaseMFA(abc.ABC): pass @abc.abstractmethod - def check_code(self, code) -> tuple: + def _check_code(self, code) -> tuple: return False, 'Error msg' @abc.abstractmethod diff --git a/apps/authentication/mfa/custom.py b/apps/authentication/mfa/custom.py index 455b852f3..3b207ea3c 100644 --- a/apps/authentication/mfa/custom.py +++ b/apps/authentication/mfa/custom.py @@ -26,7 +26,7 @@ class MFACustom(BaseMFA): display_name = MFAType.Custom.name placeholder = _("MFA custom verification code") - def check_code(self, code): + def _check_code(self, code): assert self.is_authenticated() ok = False try: diff --git a/apps/authentication/mfa/face.py b/apps/authentication/mfa/face.py index 4edf46655..085fa38cf 100644 --- a/apps/authentication/mfa/face.py +++ b/apps/authentication/mfa/face.py @@ -10,9 +10,9 @@ class MFAFace(BaseMFA, AuthFaceMixin): name = MFAType.Face.value display_name = MFAType.Face.name placeholder = 'Face Recognition' + skip_cache_check = True - def check_code(self, code): - + def _check_code(self, code): assert self.is_authenticated() try: diff --git a/apps/authentication/mfa/otp.py b/apps/authentication/mfa/otp.py index fc7decee1..6e56f8a14 100644 --- a/apps/authentication/mfa/otp.py +++ b/apps/authentication/mfa/otp.py @@ -1,10 +1,9 @@ -from django.utils.translation import gettext_lazy as _ from django.shortcuts import reverse +from django.utils.translation import gettext_lazy as _ from .base import BaseMFA from ..const import MFAType - otp_failed_msg = _("OTP code invalid, or server time error") @@ -13,7 +12,7 @@ class MFAOtp(BaseMFA): display_name = MFAType.OTP.name placeholder = _('OTP verification code') - def check_code(self, code): + def _check_code(self, code): from users.utils import check_otp_code assert self.is_authenticated() diff --git a/apps/authentication/mfa/radius.py b/apps/authentication/mfa/radius.py index 8b24897bd..ff4bb9e04 100644 --- a/apps/authentication/mfa/radius.py +++ b/apps/authentication/mfa/radius.py @@ -13,7 +13,7 @@ class MFARadius(BaseMFA): display_name = MFAType.Radius.name placeholder = _("Radius verification code") - def check_code(self, code=None): + def _check_code(self, code=None): assert self.is_authenticated() backend = RadiusBackend() username = self.user.username diff --git a/apps/authentication/mfa/sms.py b/apps/authentication/mfa/sms.py index 218931584..72a9bc02c 100644 --- a/apps/authentication/mfa/sms.py +++ b/apps/authentication/mfa/sms.py @@ -24,7 +24,7 @@ class MFASms(BaseMFA): phone, backend=self.name, user_info=user_info ) - def check_code(self, code): + def _check_code(self, code): assert self.is_authenticated() ok = False msg = '' diff --git a/apps/common/decorators.py b/apps/common/decorators.py index 2f213e9a8..6e9cc0926 100644 --- a/apps/common/decorators.py +++ b/apps/common/decorators.py @@ -381,4 +381,3 @@ def bulk_update_decorator(instance_model, batch_size=50, update_fields=None, tim instance_model.objects.bulk_update(cache, update_fields) return bulk_handle(handle, batch_size, timeout) - diff --git a/apps/users/views/profile/otp.py b/apps/users/views/profile/otp.py index aca1251fc..fce38a798 100644 --- a/apps/users/views/profile/otp.py +++ b/apps/users/views/profile/otp.py @@ -7,8 +7,8 @@ from django.http.response import HttpResponseRedirect from django.shortcuts import redirect from django.templatetags.static import static from django.urls import reverse -from django.utils.translation import gettext as _ from django.utils._os import safe_join +from django.utils.translation import gettext as _ from django.views.generic.base import TemplateView from django.views.generic.edit import FormView From 6217733aba521cb980ea2bb950e2dc86f0714598 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 27 Feb 2025 16:59:52 +0800 Subject: [PATCH 6/6] perf: update some i18n --- apps/i18n/lina/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/i18n/lina/en.json b/apps/i18n/lina/en.json index 540a5a28a..6bad13d9a 100644 --- a/apps/i18n/lina/en.json +++ b/apps/i18n/lina/en.json @@ -176,7 +176,7 @@ "AuthUserAttrMapHelpText": "The key on the left belongs to the jumpserver user properties, and the value on the right belongs to the authentication platform user properties", "Authentication": "Authentication", "AutoPush": "Auto push", - "Automation": "Automation | Automations", + "Automation": "Automation", "AverageTimeCost": "Average spend time", "AwaitingMyApproval": "Assigned", "Azure": "Azure (China)",