feat(login): 登录日志要体现用哪个backend登录的 #4472 (#5199)

Co-authored-by: xinwen <coderWen@126.com>
pull/5201/head
fit2bot 2020-12-09 18:43:13 +08:00 committed by GitHub
parent 80b03e73f6
commit 7c7de96158
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 67 additions and 34 deletions

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1 on 2020-12-09 03:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('audits', '0010_auto_20200811_1122'),
]
operations = [
migrations.AddField(
model_name='userloginlog',
name='backend',
field=models.CharField(default='', max_length=32, verbose_name='Login backend'),
),
]

View File

@ -105,6 +105,7 @@ class UserLoginLog(models.Model):
reason = models.CharField(default='', max_length=128, blank=True, verbose_name=_('Reason')) reason = models.CharField(default='', max_length=128, blank=True, verbose_name=_('Reason'))
status = models.BooleanField(max_length=2, default=True, choices=STATUS_CHOICE, verbose_name=_('Status')) status = models.BooleanField(max_length=2, default=True, choices=STATUS_CHOICE, verbose_name=_('Status'))
datetime = models.DateTimeField(default=timezone.now, verbose_name=_('Date login')) datetime = models.DateTimeField(default=timezone.now, verbose_name=_('Date login'))
backend = models.CharField(max_length=32, default='', verbose_name=_('Login backend'))
@classmethod @classmethod
def get_login_logs(cls, date_from=None, date_to=None, user=None, keyword=None): def get_login_logs(cls, date_from=None, date_to=None, user=None, keyword=None):

View File

@ -31,7 +31,8 @@ class UserLoginLogSerializer(serializers.ModelSerializer):
model = models.UserLoginLog model = models.UserLoginLog
fields = ( fields = (
'id', 'username', 'type', 'type_display', 'ip', 'city', 'user_agent', 'id', 'username', 'type', 'type_display', 'ip', 'city', 'user_agent',
'mfa', 'reason', 'status', 'status_display', 'datetime', 'mfa_display' 'mfa', 'reason', 'status', 'status_display', 'datetime', 'mfa_display',
'backend'
) )
extra_kwargs = { extra_kwargs = {
"user_agent": {'label': _('User agent')} "user_agent": {'label': _('User agent')}

View File

@ -5,6 +5,8 @@ from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from django.db import transaction from django.db import transaction
from django.utils import timezone from django.utils import timezone
from django.contrib.auth import BACKEND_SESSION_KEY
from django.utils.translation import ugettext_lazy as _
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
from rest_framework.request import Request from rest_framework.request import Request
@ -32,6 +34,19 @@ MODELS_NEED_RECORD = (
) )
LOGIN_BACKEND = {
'PublicKeyAuthBackend': _('SSH Key'),
'RadiusBackend': User.Source.radius.label,
'RadiusRealmBackend': User.Source.radius.label,
'LDAPAuthorizationBackend': User.Source.ldap.label,
'ModelBackend': _('Password'),
'SSOAuthentication': _('SSO'),
'CASBackend': User.Source.cas.label,
'OIDCAuthCodeBackend': User.Source.openid.label,
'OIDCAuthPasswordBackend': User.Source.openid.label,
}
def create_operate_log(action, sender, resource): def create_operate_log(action, sender, resource):
user = current_request.user if current_request else None user = current_request.user if current_request else None
if not user or not user.is_authenticated: if not user or not user.is_authenticated:
@ -109,6 +124,12 @@ def on_audits_log_create(sender, instance=None, **kwargs):
sys_logger.info(msg) sys_logger.info(msg)
def get_login_backend(request):
backend = request.session.get(BACKEND_SESSION_KEY, '')
backend = backend.rsplit('.', maxsplit=1)[-1]
return LOGIN_BACKEND.get(backend, '')
def generate_data(username, request): def generate_data(username, request):
user_agent = request.META.get('HTTP_USER_AGENT', '') user_agent = request.META.get('HTTP_USER_AGENT', '')
login_ip = get_request_ip(request) or '0.0.0.0' login_ip = get_request_ip(request) or '0.0.0.0'
@ -122,7 +143,8 @@ def generate_data(username, request):
'ip': login_ip, 'ip': login_ip,
'type': login_type, 'type': login_type,
'user_agent': user_agent, 'user_agent': user_agent,
'datetime': timezone.now() 'datetime': timezone.now(),
'backend': get_login_backend(request)
} }
return data return data

View File

@ -23,7 +23,7 @@ class CreateUserMixin:
email_suffix = settings.EMAIL_SUFFIX email_suffix = settings.EMAIL_SUFFIX
email = '{}@{}'.format(username, email_suffix) email = '{}@{}'.format(username, email_suffix)
user = User(username=username, name=username, email=email) user = User(username=username, name=username, email=email)
user.source = user.SOURCE_RADIUS user.source = user.Source.radius.value
user.save() user.save()
return user return user

View File

@ -1,11 +1,9 @@
import uuid import uuid
from functools import partial
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _, ugettext as __ from django.utils.translation import ugettext_lazy as _, ugettext as __
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from django.conf import settings from django.conf import settings
from django.utils.crypto import get_random_string
from common.db import models from common.db import models
from common.mixins.models import CommonModelMixin from common.mixins.models import CommonModelMixin

View File

@ -333,7 +333,7 @@ class LDAPImportUtil(object):
def update_or_create(self, user): def update_or_create(self, user):
user['email'] = self.get_user_email(user) user['email'] = self.get_user_email(user)
if user['username'] not in ['admin']: if user['username'] not in ['admin']:
user['source'] = User.SOURCE_LDAP user['source'] = User.Source.ldap.value
obj, created = User.objects.update_or_create( obj, created = User.objects.update_or_create(
username=user['username'], defaults=user username=user['username'], defaults=user
) )

View File

@ -28,7 +28,7 @@ class UserCreateUpdateFormMixin(OrgModelForm):
) )
source = forms.ChoiceField( source = forms.ChoiceField(
choices=get_source_choices, required=True, choices=get_source_choices, required=True,
initial=User.SOURCE_LOCAL, label=_("Source") initial=User.Source.local.value, label=_("Source")
) )
public_key = forms.CharField( public_key = forms.CharField(
label=_('ssh public key'), max_length=5000, required=False, label=_('ssh public key'), max_length=5000, required=False,

View File

@ -7,10 +7,10 @@ import string
import random import random
from django.conf import settings from django.conf import settings
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.core.cache import cache from django.core.cache import cache
from django.db import models from django.db import models
from django.db.models import TextChoices
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone from django.utils import timezone
@ -481,18 +481,12 @@ class MFAMixin:
class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser): class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
SOURCE_LOCAL = 'local' class Source(TextChoices):
SOURCE_LDAP = 'ldap' local = 'local', _('Local')
SOURCE_OPENID = 'openid' ldap = 'ldap', 'LDAP/AD'
SOURCE_RADIUS = 'radius' openid = 'openid', 'OpenID'
SOURCE_CAS = 'cas' radius = 'radius', 'Radius'
SOURCE_CHOICES = ( cas = 'cas', 'CAS'
(SOURCE_LOCAL, _('Local')),
(SOURCE_LDAP, 'LDAP/AD'),
(SOURCE_OPENID, 'OpenID'),
(SOURCE_RADIUS, 'Radius'),
(SOURCE_CAS, 'CAS'),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
username = models.CharField( username = models.CharField(
@ -542,7 +536,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
max_length=30, default='', blank=True, verbose_name=_('Created by') max_length=30, default='', blank=True, verbose_name=_('Created by')
) )
source = models.CharField( source = models.CharField(
max_length=30, default=SOURCE_LOCAL, choices=SOURCE_CHOICES, max_length=30, default=Source.local.value, choices=Source.choices,
verbose_name=_('Source') verbose_name=_('Source')
) )
date_password_last_updated = models.DateTimeField( date_password_last_updated = models.DateTimeField(
@ -593,7 +587,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
@property @property
def is_local(self): def is_local(self):
return self.source == self.SOURCE_LOCAL return self.source == self.Source.local.value
def set_unprovide_attr_if_need(self): def set_unprovide_attr_if_need(self):
if not self.name: if not self.name:
@ -663,6 +657,6 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
user.groups.add(UserGroup.initial()) user.groups.add(UserGroup.initial())
def can_send_created_mail(self): def can_send_created_mail(self):
if self.email and self.source == self.SOURCE_LOCAL: if self.email and self.source == self.Source.local.value:
return True return True
return False return False

View File

@ -28,7 +28,7 @@ def on_user_create(sender, user=None, **kwargs):
@receiver(cas_user_authenticated) @receiver(cas_user_authenticated)
def on_cas_user_authenticated(sender, user, created, **kwargs): def on_cas_user_authenticated(sender, user, created, **kwargs):
if created: if created:
user.source = user.SOURCE_CAS user.source = user.Source.cas.value
user.save() user.save()
@ -37,7 +37,7 @@ def on_ldap_create_user(sender, user, ldap_user, **kwargs):
if user and user.username not in ['admin']: if user and user.username not in ['admin']:
exists = User.objects.filter(username=user.username).exists() exists = User.objects.filter(username=user.username).exists()
if not exists: if not exists:
user.source = user.SOURCE_LDAP user.source = user.Source.ldap.value
user.save() user.save()
@ -46,9 +46,9 @@ def on_openid_create_or_update_user(sender, request, user, created, name, userna
if created: if created:
logger.debug( logger.debug(
"Receive OpenID user created signal: {}, " "Receive OpenID user created signal: {}, "
"Set user source is: {}".format(user, User.SOURCE_OPENID) "Set user source is: {}".format(user, User.Source.openid.value)
) )
user.source = User.SOURCE_OPENID user.source = User.Source.openid.value
user.save() user.save()
elif not created and settings.AUTH_OPENID_ALWAYS_UPDATE_USER: elif not created and settings.AUTH_OPENID_ALWAYS_UPDATE_USER:
logger.debug( logger.debug(

View File

@ -22,7 +22,7 @@ logger = get_logger(__file__)
@shared_task @shared_task
def check_password_expired(): def check_password_expired():
users = User.objects.filter(source=User.SOURCE_LOCAL).exclude(role=User.ROLE.APP) users = User.objects.filter(source=User.Source.local.value).exclude(role=User.ROLE.APP)
for user in users: for user in users:
if not user.is_valid: if not user.is_valid:
continue continue

View File

@ -362,18 +362,17 @@ def get_current_org_members(exclude=()):
def get_source_choices(): def get_source_choices():
from .models import User from .models import User
choices_all = dict(User.SOURCE_CHOICES)
choices = [ choices = [
(User.SOURCE_LOCAL, choices_all[User.SOURCE_LOCAL]), (User.Source.local.value, User.Source.local.label),
] ]
if settings.AUTH_LDAP: if settings.AUTH_LDAP:
choices.append((User.SOURCE_LDAP, choices_all[User.SOURCE_LDAP])) choices.append((User.Source.ldap.value, User.Source.ldap.label))
if settings.AUTH_OPENID: if settings.AUTH_OPENID:
choices.append((User.SOURCE_OPENID, choices_all[User.SOURCE_OPENID])) choices.append((User.Source.openid.value, User.Source.openid.label))
if settings.AUTH_RADIUS: if settings.AUTH_RADIUS:
choices.append((User.SOURCE_RADIUS, choices_all[User.SOURCE_RADIUS])) choices.append((User.Source.radius.value, User.Source.radius.label))
if settings.AUTH_CAS: if settings.AUTH_CAS:
choices.append((User.SOURCE_CAS, choices_all[User.SOURCE_CAS])) choices.append((User.Source.cas.value, User.Source.cas.label))
return choices return choices