diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 2ba1a3124..a76d02075 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -487,6 +487,7 @@ class Config(dict): 'SECURITY_LUNA_REMEMBER_AUTH': True, 'SECURITY_WATERMARK_ENABLED': True, 'SECURITY_MFA_VERIFY_TTL': 3600, + 'SECURITY_UNCOMMON_USERS_TTL': 30, 'VERIFY_CODE_TTL': 60, 'SECURITY_SESSION_SHARE': True, 'SECURITY_CHECK_DIFFERENT_CITY_LOGIN': True, diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index 71c90cef3..78b888ced 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -54,6 +54,7 @@ SECURITY_PASSWORD_RULES = [ ] VERIFY_CODE_TTL = CONFIG.VERIFY_CODE_TTL SECURITY_MFA_VERIFY_TTL = CONFIG.SECURITY_MFA_VERIFY_TTL +SECURITY_UNCOMMON_USERS_TTL = CONFIG.SECURITY_UNCOMMON_USERS_TTL SECURITY_VIEW_AUTH_NEED_MFA = CONFIG.SECURITY_VIEW_AUTH_NEED_MFA SECURITY_SERVICE_ACCOUNT_REGISTRATION = CONFIG.SECURITY_SERVICE_ACCOUNT_REGISTRATION SECURITY_LOGIN_CAPTCHA_ENABLED = CONFIG.SECURITY_LOGIN_CAPTCHA_ENABLED diff --git a/apps/settings/serializers/security.py b/apps/settings/serializers/security.py index 329cc8ebe..8c000cd8e 100644 --- a/apps/settings/serializers/security.py +++ b/apps/settings/serializers/security.py @@ -190,6 +190,11 @@ class SecuritySettingSerializer(SecurityPasswordRuleSerializer, SecurityAuthSeri required=True, label=_('Session share'), help_text=_("Enabled, Allows user active session to be shared with other users") ) + SECURITY_UNCOMMON_USERS_TTL = serializers.IntegerField( + min_value=30, max_value=99999, required=False, + label=_('Unused user timeout (day)'), + help_text=_("Detect infrequent users daily and disable them if they exceed the predetermined time limit.") + ) SECURITY_CHECK_DIFFERENT_CITY_LOGIN = serializers.BooleanField( required=False, label=_('Remote Login Protection'), help_text=_( diff --git a/apps/users/tasks.py b/apps/users/tasks.py index bc146bf12..c363940fe 100644 --- a/apps/users/tasks.py +++ b/apps/users/tasks.py @@ -2,15 +2,18 @@ # from celery import shared_task +from django.conf import settings +from django.db.models import Max from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from audits.models import UserLoginLog from common.const.crontab import CRONTAB_AT_AM_TEN, CRONTAB_AT_PM_TWO from common.utils import get_logger -from ops.celery.decorator import after_app_ready_start -from ops.celery.utils import ( - create_or_update_celery_periodic_tasks -) +from common.utils.timezone import utc_now +from ops.celery.decorator import after_app_ready_start, register_as_period_task +from ops.celery.utils import create_or_update_celery_periodic_tasks +from orgs.utils import tmp_to_root_org from users.notifications import PasswordExpirationReminderMsg from users.notifications import UserExpirationReminderMsg from .models import User @@ -75,3 +78,23 @@ def check_user_expired_periodic(): } } create_or_update_celery_periodic_tasks(tasks) + + +@shared_task(verbose_name=_('Check unused users')) +@register_as_period_task(crontab=CRONTAB_AT_PM_TWO) +@tmp_to_root_org() +def check_unused_users(): + now = utc_now() + unused_usernames = [] + usernames_max_datetime = UserLoginLog.objects.values('username').annotate(max_datetime=Max('datetime')) + for i in usernames_max_datetime: + username = i['username'] + max_datetime = i['max_datetime'] + uncommon_users_ttl = settings.SECURITY_UNCOMMON_USERS_TTL + if (now - max_datetime).seconds > uncommon_users_ttl * 24 * 60 * 60: + unused_usernames.append(username) + + if not unused_usernames: + return + + User.objects.filter(username__in=unused_usernames).update(is_active=False)