feat: 用户登录堡垒机时通知管理员 (#11686)

Co-authored-by: feng <1304903146@qq.com>
pull/11687/head
fit2bot 1 year ago committed by GitHub
parent 0f3ddc3bf1
commit 4065baf785
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,3 +7,4 @@ class ActionChoices(models.TextChoices):
accept = 'accept', _('Accept')
review = 'review', _('Review')
warning = 'warning', _('Warning')
notice = 'notice', _('Notifications')

@ -0,0 +1,38 @@
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
from audits.models import UserLoginLog
from notifications.notifications import UserMessage
from users.models import User
class UserLoginReminderMsg(UserMessage):
subject = _('User login reminder')
def __init__(self, user, user_log: UserLoginLog):
self.user_log = user_log
super().__init__(user)
def get_html_msg(self) -> dict:
user_log = self.user_log
context = {
'ip': user_log.ip,
'city': user_log.city,
'username': user_log.username,
'recipient': self.user.username,
'user_agent': user_log.user_agent,
}
message = render_to_string('acls/user_login_reminder.html', context)
print('message', message)
return {
'subject': str(self.subject),
'message': message
}
@classmethod
def gen_test_msg(cls):
user = User.objects.first()
user_log = UserLoginLog.objects.first()
return cls(user, user_log)

@ -0,0 +1,14 @@
{% load i18n %}
<h3>{% trans 'Respectful' %}{{ recipient }}</h3>
<hr>
<p><strong>{% trans 'Username' %}:</strong> [{{ username }}]</p>
<p><strong>IP:</strong> [{{ ip }}]</p>
<p><strong>{% trans 'Login city' %}:</strong> [{{ city }}]</p>
<p><strong>{% trans 'User agent' %}:</strong> [{{ user_agent }}]</p>
<hr>
<p>{% trans 'The user has just successfully logged into the system. Please ensure that this is an authorized operation. If you suspect that this is an unauthorized access, please take appropriate measures immediately.' %}</p>
<p>{% trans 'Thank you' %}</p>

@ -11,6 +11,9 @@ from django.utils.functional import LazyObject
from django.utils.translation import gettext_lazy as _
from rest_framework.request import Request
from acls.const import ActionChoices
from acls.models import LoginACL
from acls.notifications import UserLoginReminderMsg
from audits.models import UserLoginLog
from authentication.signals import post_auth_failed, post_auth_success
from authentication.utils import check_different_city_login_if_need
@ -102,21 +105,37 @@ def create_user_session(request, user_id, instance: UserLoginLog):
request.session['user_session_id'] = user_session.id
def send_login_info_to_reviewers(instance: UserLoginLog, reviewers):
for reviewer in reviewers:
UserLoginReminderMsg(reviewer, instance).publish_async()
@receiver(post_auth_success)
def on_user_auth_success(sender, user, request, login_type=None, **kwargs):
logger.debug('User login success: {}'.format(user.username))
check_different_city_login_if_need(user, request)
data = generate_data(
user.username, request, login_type=login_type
)
request.session['login_time'] = data['datetime'].strftime("%Y-%m-%d %H:%M:%S")
data = generate_data(user.username, request, login_type=login_type)
request.session['login_time'] = data['datetime'].strftime('%Y-%m-%d %H:%M:%S')
data.update({'mfa': int(user.mfa_enabled), 'status': True})
instance = write_login_log(**data)
# TODO 目前只记录 web 登录的 session
if instance.type != LoginTypeChoices.web:
return
create_user_session(request, user.id, instance)
auth_notice_required = request.session.get('auth_notice_required')
if not auth_notice_required:
return
auth_acl_id = request.session.get('auth_acl_id')
acl = LoginACL.objects.filter(id=auth_acl_id, action=ActionChoices.notice).first()
if not acl or not acl.reviewers.exists():
return
reviewers = acl.reviewers.all()
send_login_info_to_reviewers(instance, reviewers)
@receiver(post_auth_failed)
def on_user_auth_failed(sender, username, request, reason='', **kwargs):

@ -355,6 +355,11 @@ class AuthACLMixin:
self.request.session['auth_acl_id'] = str(acl.id)
return
if acl.is_action(acl.ActionChoices.notice):
self.request.session['auth_notice_required'] = '1'
self.request.session['auth_acl_id'] = str(acl.id)
return
def _check_third_party_login_acl(self):
request = self.request
error_message = getattr(request, 'error_message', None)
@ -513,7 +518,7 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost
def clear_auth_mark(self):
keys = [
'auth_password', 'user_id', 'auth_confirm_required',
'auth_ticket_id', 'auth_acl_id'
'auth_notice_required', 'auth_ticket_id', 'auth_acl_id'
]
for k in keys:
self.request.session.pop(k, '')

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2ffdd50c364a510b4f5cfe7922a5f1a604e8bc7b03aa43ece1dff0250ccde6d6
size 160575
oid sha256:47cea504e9acfdcc94a45f2ea96216dddde065d134a92a0ef3e92815a12df6fc
size 162024

File diff suppressed because it is too large Load Diff

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:78fa10c674b853ebde73bbdef255beeb794a7a7b4bf5483ac1464c282aab0819
size 131200
oid sha256:096cdc44514bd9f43b5e0062d878625c220ed7826a57a27968db3cb97e7eb011
size 132403

File diff suppressed because it is too large Load Diff

@ -98,7 +98,7 @@ class BaseHandler:
context = self._diff_prev_approve_context(state)
context.update({'approve_info': approve_info})
body = self.safe_html_script(
render_to_string('tickets/ticket_approve_diff.html', context)
render_to_string('tickets/user_login_reminder.html', context)
)
data = {
'body': body,

Loading…
Cancel
Save