1
0
mirror of https://github.com/jumpserver/jumpserver.git synced 2025-05-09 08:36:28 +00:00

Merge pull request from jumpserver/pr@dev@update_pam

merge: with dev
This commit is contained in:
老广 2025-02-27 17:14:59 +08:00 committed by GitHub
commit 33f3281a1f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 77 additions and 26 deletions
apps
accounts
automations/base
filters.py
models
authentication
common
i18n/lina
terminal
models/session
signal_handlers
users/views/profile

View File

@ -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(

View File

@ -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,11 +105,11 @@ 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:
queryset = queryset.filter(date_last_login__gte=date)
queryset = queryset.filter(**kwargs)
return queryset
@staticmethod

View File

@ -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():
"""

View File

@ -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):
"""

View File

@ -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
from .face import MFAFace
from .otp import MFAOtp, otp_failed_msg
from .radius import MFARadius
from .sms import MFASms

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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()

View File

@ -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

View File

@ -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 = ''

View File

@ -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)

View File

@ -182,7 +182,7 @@
"Authentication": "Authentication",
"AuthorizedKeysChanged": "Authorized keys changed",
"AutoPush": "Auto push",
"Automations": "Automations",
"Automation": "Automation",
"AverageTimeCost": "Average spend time",
"AwaitingMyApproval": "Assigned",
"Azure": "Azure (China)",
@ -673,6 +673,7 @@
"InterfaceSettings": "Appearance",
"Interval": "Interval",
"IntervalOfCreateUpdatePage": "Unit: hour",
"Integration": "Integration",
"InvalidJson": "Invalid json",
"InviteSuccess": "Invitation successful",
"InviteUser": "Invite",
@ -1112,8 +1113,14 @@
"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",
"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",
@ -1310,6 +1317,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",
@ -1503,5 +1511,6 @@
"disallowSelfUpdateFields": "Not allowed to modify the current fields yourself",
"forceEnableMFAHelpText": "If force enable, user can not disable by themselves",
"removeWarningMsg": "Are you sure you want to remove",
"ExecutionSummary": "Execution summary",
"setVariable": "Set variable"
}

View File

@ -174,7 +174,7 @@
"Authentication": "认证",
"AuthorizedKeysChanged": "密钥变更",
"AutoPush": "自动推送",
"Automations": "自动化",
"Automation": "自动化",
"AverageTimeCost": "平均花费时间",
"AwaitingMyApproval": "待我审批",
"Azure": "Azure (中国)",
@ -647,6 +647,7 @@
"InterfaceSettings": "界面设置",
"Interval": "间隔",
"IntervalOfCreateUpdatePage": "单位:时",
"Integration": "应用集成",
"InvalidJson": "不是合法 JSON",
"InviteSuccess": "邀请成功",
"InviteUser": "邀请用户",
@ -1075,6 +1076,12 @@
"RunningPath": "运行路径",
"RunningPathHelpText": "填写脚本的运行路径,此设置仅 shell 脚本生效",
"RunningTimes": "最近5次运行时间",
"RunningSummary": "运行中",
"AccountPushTask": "账号推送任务",
"DiscoverAccountTask": "账号发现任务",
"ChangeSecretTask": "账号改密任务",
"AccountBackupTask": "账号备份任务",
"ExecutionHistory": "执行历史",
"SCP": "深信服云平台",
"SMS": "短信",
"SMSProvider": "短信服务商",
@ -1269,6 +1276,7 @@
"TaskList": "任务列表",
"TaskMonitor": "任务监控",
"TaskPath": "任务路径",
"TaskSummary": "任务汇总",
"TechnologyConsult": "技术咨询",
"TempPasswordTip": "临时密码有效期为 300 秒,使用后立刻失效",
"TempToken": "临时密码",
@ -1459,5 +1467,8 @@
"disallowSelfUpdateFields": "不允许自己修改当前字段",
"forceEnableMFAHelpText": "如果强制启用,用户无法自行禁用",
"removeWarningMsg": "你确定要移除",
"setVariable": "设置参数"
"setVariable": "设置参数",
"TableSetting": "表格偏好",
"AccountDiscoverTask": "账号发现",
"ExecutionSummary": "执行汇总"
}

View File

@ -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

View File

@ -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)

View File

@ -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