diff --git a/apps/assets/templates/assets/_asset_group_bulk_update_modal.html b/apps/assets/templates/assets/_asset_group_bulk_update_modal.html index 61ac04fa6..7df6c4ede 100644 --- a/apps/assets/templates/assets/_asset_group_bulk_update_modal.html +++ b/apps/assets/templates/assets/_asset_group_bulk_update_modal.html @@ -31,11 +31,11 @@
- +
{% endblock %} -{% block modal_confirm_id %}btn_asset_group_bulk_update{% endblock %} \ No newline at end of file +{% block modal_confirm_id %}btn_asset_group_bulk_update{% endblock %} diff --git a/apps/audits/signals_handler.py b/apps/audits/signals_handler.py index 393f51a37..95ce8c05b 100644 --- a/apps/audits/signals_handler.py +++ b/apps/audits/signals_handler.py @@ -128,7 +128,7 @@ def generate_data(username, request): def on_user_auth_success(sender, user, request, **kwargs): logger.debug('User login success: {}'.format(user.username)) data = generate_data(user.username, request) - data.update({'mfa': int(user.otp_enabled), 'status': True}) + data.update({'mfa': int(user.mfa_enabled), 'status': True}) write_login_log_async.delay(**data) diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 80f54dbc9..bb488791f 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -89,7 +89,9 @@ class AuthMixin: def check_user_mfa_if_need(self, user): if self.request.session.get('auth_mfa'): return - if not user.otp_enabled or not user.otp_secret_key: + if not user.mfa_enabled: + return + if not user.otp_secret_key and user.mfa_is_otp(): return raise errors.MFARequiredError() diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index d6bb2af6c..e24314717 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -130,8 +130,8 @@ class UserLoginGuardView(mixins.AuthMixin, RedirectView): auth_login(self.request, user) self.send_auth_signal(success=True, user=user) self.clear_auth_mark() - # 启用但是没有设置otp - if user.otp_enabled and not user.otp_secret_key: + # 启用但是没有设置otp, 排除radius + if user.mfa_enabled_but_not_set(): # 1,2,mfa_setting & F return reverse('users:user-otp-enable-authentication') url = redirect_user_first_login_or_index( diff --git a/apps/users/api/user.py b/apps/users/api/user.py index 2a033915c..11f82a764 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -172,7 +172,7 @@ class UserResetOTPApi(UserQuerysetMixin, generics.RetrieveAPIView): if user == request.user: msg = _("Could not reset self otp, use profile reset instead") return Response({"error": msg}, status=401) - if user.otp_enabled and user.otp_secret_key: + if user.mfa_enabled and user.otp_secret_key: user.otp_secret_key = '' user.save() logout(request) diff --git a/apps/users/forms.py b/apps/users/forms.py index 649f66ab9..dd60c48be 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -61,10 +61,10 @@ class UserCreateUpdateFormMixin(OrgModelForm): fields = [ 'username', 'name', 'email', 'groups', 'wechat', 'source', 'phone', 'role', 'date_expired', - 'comment', 'otp_level' + 'comment', 'mfa_level' ] widgets = { - 'otp_level': forms.RadioSelect(), + 'mfa_level': forms.RadioSelect(), 'groups': forms.SelectMultiple( attrs={ 'class': 'select2', @@ -126,13 +126,13 @@ class UserCreateUpdateFormMixin(OrgModelForm): def save(self, commit=True): password = self.cleaned_data.get('password') - otp_level = self.cleaned_data.get('otp_level') + mfa_level = self.cleaned_data.get('mfa_level') public_key = self.cleaned_data.get('public_key') user = super().save(commit=commit) if password: user.reset_password(password) - if otp_level: - user.otp_level = otp_level + if mfa_level: + user.mfa_level = mfa_level user.save() if public_key: user.public_key = public_key @@ -183,10 +183,10 @@ class UserMFAForm(forms.ModelForm): class Meta: model = User - fields = ['otp_level'] - widgets = {'otp_level': forms.RadioSelect()} + fields = ['mfa_level'] + widgets = {'mfa_level': forms.RadioSelect()} help_texts = { - 'otp_level': _('* Enable MFA authentication ' + 'mfa_level': _('* Enable MFA authentication ' 'to make the account more secure.'), } diff --git a/apps/users/migrations/0024_auto_20191118_1612.py b/apps/users/migrations/0024_auto_20191118_1612.py new file mode 100644 index 000000000..eb368be2f --- /dev/null +++ b/apps/users/migrations/0024_auto_20191118_1612.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.5 on 2019-11-18 08:12 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0023_auto_20190724_1525'), + ] + + operations = [ + migrations.RenameField( + model_name='user', + old_name='otp_level', + new_name='mfa_level', + ), + ] diff --git a/apps/users/models/user.py b/apps/users/models/user.py index e96fef6e0..0a4935daf 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -346,35 +346,41 @@ class TokenMixin: class MFAMixin: - otp_level = 0 + mfa_level = 0 otp_secret_key = '' - OTP_LEVEL_CHOICES = ( + MFA_LEVEL_CHOICES = ( (0, _('Disable')), (1, _('Enable')), (2, _("Force enable")), ) @property - def otp_enabled(self): - return self.otp_force_enabled or self.otp_level > 0 + def mfa_enabled(self): + return self.mfa_force_enabled or self.mfa_level > 0 @property - def otp_force_enabled(self): + def mfa_force_enabled(self): if settings.SECURITY_MFA_AUTH: return True - return self.otp_level == 2 + return self.mfa_level == 2 - def enable_otp(self): - if not self.otp_level == 2: - self.otp_level = 1 + def enable_mfa(self): + if not self.mfa_level == 2: + self.mfa_level = 1 - def force_enable_otp(self): - self.otp_level = 2 + def force_enable_mfa(self): + self.mfa_level = 2 - def disable_otp(self): - self.otp_level = 0 + def disable_mfa(self): + self.mfa_level = 0 self.otp_secret_key = None + @staticmethod + def mfa_is_otp(): + if settings.CONFIG.OTP_IN_RADIUS: + return False + return True + def check_otp_on_radius(self, code): from authentication.backends.radius import RadiusBackend backend = RadiusBackend() @@ -390,6 +396,11 @@ class MFAMixin: else: return check_otp_code(self.otp_secret_key, code) + def mfa_enabled_but_not_set(self): + if self.mfa_enabled and self.mfa_is_otp() and not self.otp_secret_key: + return True + return False + class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser): SOURCE_LOCAL = 'local' @@ -428,8 +439,8 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser): phone = models.CharField( max_length=20, blank=True, null=True, verbose_name=_('Phone') ) - otp_level = models.SmallIntegerField( - default=0, choices=MFAMixin.OTP_LEVEL_CHOICES, verbose_name=_('MFA') + mfa_level = models.SmallIntegerField( + default=0, choices=MFAMixin.MFA_LEVEL_CHOICES, verbose_name=_('MFA') ) otp_secret_key = fields.EncryptCharField(max_length=128, blank=True, null=True) # Todo: Auto generate key, let user download diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index b7ec186b7..9097b1565 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -27,7 +27,7 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer): fields = [ 'id', 'name', 'username', 'password', 'email', 'public_key', 'groups', 'groups_display', - 'role', 'role_display', 'wechat', 'phone', 'otp_level', + 'role', 'role_display', 'wechat', 'phone', 'mfa_level', 'comment', 'source', 'source_display', 'is_valid', 'is_expired', 'is_active', 'created_by', 'is_first_login', 'date_password_last_updated', 'date_expired', 'avatar_url', diff --git a/apps/users/templates/users/_user.html b/apps/users/templates/users/_user.html index 427f8258b..dbcf7f804 100644 --- a/apps/users/templates/users/_user.html +++ b/apps/users/templates/users/_user.html @@ -20,7 +20,7 @@

{% trans 'Auth' %}

{% block password %}{% endblock %} - {% bootstrap_field form.otp_level layout="horizontal" %} + {% bootstrap_field form.mfa_level layout="horizontal" %} {% bootstrap_field form.source layout="horizontal" %}
@@ -77,7 +77,7 @@ $(document).ready(function () { $('.select2').select2(); initDateRangePicker('#id_date_expired'); - var mfa_radio = $('#id_otp_level'); + var mfa_radio = $('#id_mfa_level'); mfa_radio.addClass("form-inline"); mfa_radio.children().css("margin-right","15px"); fieldDisplay() diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index 651763140..768fbdef2 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -91,9 +91,9 @@ {% trans 'MFA certification' %}: - {% if user_object.otp_force_enabled %} + {% if user_object.mfa_force_enabled %} {% trans 'Force enabled' %} - {% elif user_object.otp_enabled%} + {% elif user_object.mfa_enabled%} {% trans 'Enabled' %} {% else %} {% trans 'Disabled' %} @@ -162,9 +162,9 @@
- -