mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-05-09 08:36:28 +00:00
Merge pull request #14943 from jumpserver/pr@dev@update_pam
merge: with dev
This commit is contained in:
commit
33f3281a1f
apps
accounts
authentication
common
i18n/lina
terminal
users/views/profile
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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():
|
||||
"""
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 = ''
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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"
|
||||
}
|
@ -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": "执行汇总"
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user