feat: 用户异地登陆

pull/6973/head
feng626 2021-09-27 19:06:26 +08:00 committed by 老广
parent a558ee2ac0
commit 5588eab57e
6 changed files with 178 additions and 38 deletions

5
apps/audits/const.py Normal file
View File

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext_lazy as _
DEFAULT_CITY = _("Unknown")

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
from django.db.models.signals import (
post_save, post_delete, m2m_changed, pre_delete
post_save, m2m_changed, pre_delete
)
from django.dispatch import receiver
from django.conf import settings
@ -14,25 +14,25 @@ from rest_framework.renderers import JSONRenderer
from rest_framework.request import Request
from assets.models import Asset, SystemUser
from common.const.signals import POST_ADD, POST_REMOVE, POST_CLEAR
from authentication.signals import post_auth_failed, post_auth_success
from authentication.utils import check_different_city_login
from jumpserver.utils import current_request
from common.utils import get_request_ip, get_logger, get_syslogger
from users.models import User
from users.signals import post_user_change_password
from authentication.signals import post_auth_failed, post_auth_success
from terminal.models import Session, Command
from common.utils.encode import model_to_json
from .utils import write_login_log
from . import models
from .models import OperateLog
from orgs.utils import current_org
from perms.models import AssetPermission, ApplicationPermission
from common.const.signals import POST_ADD, POST_REMOVE, POST_CLEAR
from common.utils import get_request_ip, get_logger, get_syslogger
from common.utils.encode import model_to_json
logger = get_logger(__name__)
sys_logger = get_syslogger(__name__)
json_render = JSONRenderer()
MODELS_NEED_RECORD = (
# users
'User', 'UserGroup',
@ -165,7 +165,6 @@ M2M_NEED_RECORD = {
),
}
M2M_ACTION = {
POST_ADD: 'add',
POST_REMOVE: 'remove',
@ -305,6 +304,7 @@ def generate_data(username, request, login_type=None):
@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(user, request)
data = generate_data(user.username, request, login_type=login_type)
data.update({'mfa': int(user.mfa_enabled), 'status': True})
write_login_log(**data)

View File

@ -1,8 +1,8 @@
import csv
import codecs
from django.http import HttpResponse
from django.utils.translation import ugettext as _
from .const import DEFAULT_CITY
from common.utils import validate_ip, get_ip_city
@ -27,12 +27,12 @@ def write_content_to_excel(response, header=None, login_logs=None, fields=None):
def write_login_log(*args, **kwargs):
from audits.models import UserLoginLog
default_city = _("Unknown")
ip = kwargs.get('ip') or ''
if not (ip and validate_ip(ip)):
ip = ip[:15]
city = default_city
city = DEFAULT_CITY
else:
city = get_ip_city(ip) or default_city
city = get_ip_city(ip) or DEFAULT_CITY
kwargs.update({'ip': ip, 'city': city})
UserLoginLog.objects.create(**kwargs)

View File

@ -0,0 +1,75 @@
from django.utils import timezone
from django.utils.translation import ugettext as _
from notifications.notifications import UserMessage
from settings.api import PublicSettingApi
from common.utils import get_logger
logger = get_logger(__file__)
EMAIL_TEMPLATE = _(
""
"<h3>{subject}</h3>"
"<p>Dear {server_name} user, Hello!</p>"
"<p>Your account has remote login behavior, please pay attention.</p>"
"<p>User: {username}</p>"
"<p>Login time: {time}</p>"
"<p>Login location: {city} ({ip})</p>"
"<p>If you suspect that the login behavior is abnormal, please modify "
"<p>the account password in time.</p>"
"<br>"
"<p>Thank you for your attention to {server_name}!</p>")
class DifferentCityLoginMessage(UserMessage):
def __init__(self, user, ip, city):
self.ip = ip
self.city = city
super().__init__(user)
@property
def time(self):
return timezone.now().strftime("%Y-%m-%d %H:%M:%S")
@property
def subject(self):
return _('Different city login reminder')
def get_text_msg(self) -> dict:
message = _(
""
"{subject}\n"
"Dear {server_name} user, Hello!\n"
"Your account has remote login behavior, please pay attention.\n"
"User: {username}\n"
"Login time: {time}\n"
"Login location: {city} ({ip})\n"
"If you suspect that the login behavior is abnormal, please modify "
"the account password in time.\n"
"Thank you for your attention to {server_name}!\n"
).format(
subject=self.subject,
server_name=PublicSettingApi.get_login_title(),
username=self.user.username,
ip=self.ip,
time=self.time,
city=self.city,
)
return {
'subject': self.subject,
'message': message
}
def get_html_msg(self) -> dict:
message = EMAIL_TEMPLATE.format(
subject=self.subject,
server_name=PublicSettingApi.get_login_title(),
username=self.user.username,
ip=self.ip,
time=self.time,
city=self.city,
)
return {
'subject': self.subject,
'message': message
}

View File

@ -4,6 +4,12 @@ import base64
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_v1_5
from Cryptodome import Random
from .notifications import DifferentCityLoginMessage
from audits.models import UserLoginLog
from audits.const import DEFAULT_CITY
from common.utils import get_request_ip
from common.utils import validate_ip, get_ip_city
from common.utils import get_logger
logger = get_logger(__file__)
@ -43,3 +49,16 @@ def rsa_decrypt(cipher_text, rsa_private_key=None):
cipher_decoded = base64.b16decode(hex_fixed.upper())
message = cipher.decrypt(cipher_decoded, b'error').decode()
return message
def check_different_city_login(user, request):
ip = get_request_ip(request) or '0.0.0.0'
if not (ip and validate_ip(ip)):
city = DEFAULT_CITY
else:
city = get_ip_city(ip) or DEFAULT_CITY
last_user_login = UserLoginLog.objects.filter(username=user.username, status=True).first()
if last_user_login.city != city:
DifferentCityLoginMessage(user, ip, city).publish_async()

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-23 11:11+0800\n"
"POT-Creation-Date: 2021-09-29 10:53+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -552,7 +552,7 @@ msgstr "创建日期"
msgid "AuthBook"
msgstr "账号"
#: assets/models/base.py:30 assets/tasks/const.py:51 audits/utils.py:30
#: assets/models/base.py:30 assets/tasks/const.py:51 audits/const.py:5
msgid "Unknown"
msgstr "未知"
@ -1667,6 +1667,47 @@ msgstr "{} 需要 {} 复核"
msgid "Expired"
msgstr "过期时间"
#: authentication/notifications.py:11
#, python-brace-format
msgid ""
"<h3>{subject}</h3><p>Dear {server_name} user, Hello!</p><p>Your account has "
"remote login behavior, please pay attention.</p><p>User: {username}</"
"p><p>Login time: {time}</p><p>Login location: {city} ({ip})</p><p>If you "
"suspect that the login behavior is abnormal, please modify <p>the account "
"password in time.</p><br><p>Thank you for your attention to {server_name}!</"
"p>"
msgstr ""
"<h3>{subject}</h3><p>尊敬的{server_name}用户,&nbsp; &nbsp;您好!</p><p>您的账"
"号存在异地登录行为,请关注。</p><p>用户: {username}</p><p>登录时间: {time}</"
"p><p>登录地点: {city} ({ip})</p><p>若怀疑此次登录行为异常,请及时修改账号密"
"码。</p><br><p>感谢您对{server_name}的关注!</p>"
#: authentication/notifications.py:36
msgid "Different city login reminder"
msgstr "异地登录提醒"
#: authentication/notifications.py:40
#, python-brace-format
msgid ""
"{subject}\n"
"Dear {server_name} user, Hello!\n"
"Your account has remote login behavior, please pay attention.\n"
"User: {username}\n"
"Login time: {time}\n"
"Login location: {city} ({ip})\n"
"If you suspect that the login behavior is abnormal, please modify the "
"account password in time.\n"
"Thank you for your attention to {server_name}!\n"
msgstr ""
"{subject}\n"
"尊敬的{server_name}用户, 您好!\n"
"您的账号存在异地登录行为,请关注。\n"
"用户: {username}\n"
"登录时间: {time}\n"
"登录地点: {city} ({ip})\n"
"若怀疑此次登录行为异常,请及时修改账号密码。\n"
"感谢您对{server_name}的关注!\n"
#: authentication/sms_verify_code.py:17
msgid "The verification code has expired. Please resend it"
msgstr "验证码已过期,请重新发送"
@ -2544,7 +2585,7 @@ msgstr "测试手机号 该字段是必填项。"
msgid "Test success"
msgstr "测试成功"
#: settings/api/email.py:21
#: settings/api/email.py:22
msgid "Test mail sent to {}, please check"
msgstr "邮件已经发送{}, 请检查"
@ -2818,7 +2859,7 @@ msgid "SMS provider"
msgstr "短信服务商"
#: settings/serializers/auth/sms.py:17 settings/serializers/auth/sms.py:35
#: settings/serializers/auth/sms.py:43 settings/serializers/email.py:69
#: settings/serializers/auth/sms.py:43 settings/serializers/email.py:63
msgid "Signature"
msgstr "签名"
@ -2909,89 +2950,89 @@ msgstr "上传下载"
msgid "Cloud sync record keep days"
msgstr "云同步记录"
#: settings/serializers/email.py:24
#: settings/serializers/email.py:18
msgid "SMTP host"
msgstr "SMTP 主机"
#: settings/serializers/email.py:25
#: settings/serializers/email.py:19
msgid "SMTP port"
msgstr "SMTP 端口"
#: settings/serializers/email.py:26
#: settings/serializers/email.py:20
msgid "SMTP account"
msgstr "SMTP 账号"
#: settings/serializers/email.py:28
#: settings/serializers/email.py:22
msgid "SMTP password"
msgstr "SMTP 密码"
#: settings/serializers/email.py:29
#: settings/serializers/email.py:23
msgid "Tips: Some provider use token except password"
msgstr "提示:一些邮件提供商需要输入的是授权码"
#: settings/serializers/email.py:32
#: settings/serializers/email.py:26
msgid "Send user"
msgstr "发件人"
#: settings/serializers/email.py:33
#: settings/serializers/email.py:27
msgid "Tips: Send mail account, default SMTP account as the send account"
msgstr "提示:发送邮件账号,默认使用 SMTP 账号作为发送账号"
#: settings/serializers/email.py:36
#: settings/serializers/email.py:30
msgid "Test recipient"
msgstr "测试收件人"
#: settings/serializers/email.py:37
#: settings/serializers/email.py:31
msgid "Tips: Used only as a test mail recipient"
msgstr "提示:仅用来作为测试邮件收件人"
#: settings/serializers/email.py:40
#: settings/serializers/email.py:34
msgid "Use SSL"
msgstr "使用 SSL"
#: settings/serializers/email.py:41
#: settings/serializers/email.py:35
msgid "If SMTP port is 465, may be select"
msgstr "如果SMTP端口是465通常需要启用 SSL"
#: settings/serializers/email.py:44
#: settings/serializers/email.py:38
msgid "Use TLS"
msgstr "使用 TLS"
#: settings/serializers/email.py:45
#: settings/serializers/email.py:39
msgid "If SMTP port is 587, may be select"
msgstr "如果SMTP端口是587通常需要启用 TLS"
#: settings/serializers/email.py:48
#: settings/serializers/email.py:42
msgid "Subject prefix"
msgstr "主题前缀"
#: settings/serializers/email.py:55
#: settings/serializers/email.py:49
msgid "Create user email subject"
msgstr "邮件主题"
#: settings/serializers/email.py:56
#: settings/serializers/email.py:50
msgid ""
"Tips: When creating a user, send the subject of the email (eg:Create account "
"successfully)"
msgstr "提示: 创建用户时,发送设置密码邮件的主题 (例如: 创建用户成功)"
#: settings/serializers/email.py:60
#: settings/serializers/email.py:54
msgid "Create user honorific"
msgstr "邮件的敬语"
#: settings/serializers/email.py:61
#: settings/serializers/email.py:55
msgid "Tips: When creating a user, send the honorific of the email (eg:Hello)"
msgstr "提示: 创建用户时,发送设置密码邮件的敬语 (例如: 您好)"
#: settings/serializers/email.py:65
#: settings/serializers/email.py:59
msgid "Create user email content"
msgstr "邮件的内容"
#: settings/serializers/email.py:66
#: settings/serializers/email.py:60
msgid "Tips:When creating a user, send the content of the email"
msgstr "提示: 创建用户时,发送设置密码邮件的内容"
#: settings/serializers/email.py:70
#: settings/serializers/email.py:64
msgid "Tips: Email signature (eg:jumpserver)"
msgstr "邮件署名 (如:jumpserver)"
@ -3872,7 +3913,7 @@ msgstr "测试成功"
msgid "Test failure: Account invalid"
msgstr "测试失败: 账户无效"
#: terminal/api/terminal.py:39
#: terminal/api/terminal.py:41
msgid "Have online sessions"
msgstr "有在线会话"
@ -4265,7 +4306,7 @@ msgstr "文档类型"
msgid "Ignore Certificate Verification"
msgstr "忽略证书认证"
#: terminal/serializers/terminal.py:78 terminal/serializers/terminal.py:86
#: terminal/serializers/terminal.py:80 terminal/serializers/terminal.py:88
msgid "Not found"
msgstr "没有发现"