mirror of https://github.com/jumpserver/jumpserver
[Update] 修改mfa
parent
bfd8a9c66d
commit
c9ee8edeaf
|
@ -31,11 +31,11 @@
|
|||
<div class="form-group">
|
||||
<div class="col-sm-9 col-lg-9 col-sm-offset-2">
|
||||
<div class="checkbox checkbox-success">
|
||||
<input type="checkbox" name="enable_otp" checked id="id_enable_otp"><label for="id_enable_otp">{% trans 'Enable-MFA' %}</label>
|
||||
<input type="checkbox" name="enable_mfa" checked id="id_enable_mfa"><label for="id_enable_mfa">{% trans 'Enable-MFA' %}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
{% block modal_confirm_id %}btn_asset_group_bulk_update{% endblock %}
|
||||
{% block modal_confirm_id %}btn_asset_group_bulk_update{% endblock %}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.'),
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
),
|
||||
]
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
<h3>{% trans 'Auth' %}</h3>
|
||||
{% block password %}{% endblock %}
|
||||
{% bootstrap_field form.otp_level layout="horizontal" %}
|
||||
{% bootstrap_field form.mfa_level layout="horizontal" %}
|
||||
{% bootstrap_field form.source layout="horizontal" %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
|
@ -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()
|
||||
|
|
|
@ -91,9 +91,9 @@
|
|||
<tr>
|
||||
<td>{% trans 'MFA certification' %}:</td>
|
||||
<td><b>
|
||||
{% 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 @@
|
|||
<td><span class="pull-right">
|
||||
<div class="switch">
|
||||
<div class="onoffswitch">
|
||||
<input type="checkbox" class="onoffswitch-checkbox" {% if user_object.otp_force_enabled %} checked {% endif %}
|
||||
id="force_enable_otp">
|
||||
<label class="onoffswitch-label" for="force_enable_otp">
|
||||
<input type="checkbox" class="onoffswitch-checkbox" {% if user_object.mfa_force_enabled %} checked {% endif %}
|
||||
id="force_enable_mfa">
|
||||
<label class="onoffswitch-label" for="force_enable_mfa">
|
||||
<span class="onoffswitch-inner"></span>
|
||||
<span class="onoffswitch-switch"></span>
|
||||
</label>
|
||||
|
@ -370,7 +370,7 @@ $(document).ready(function() {
|
|||
success_message: success
|
||||
});
|
||||
})
|
||||
.on('click', '#force_enable_otp', function() {
|
||||
.on('click', '#force_enable_mfa', function() {
|
||||
{% if request.user == user_object %}
|
||||
toastr.error("{% trans 'Goto profile page enable MFA' %}");
|
||||
return;
|
||||
|
@ -378,16 +378,16 @@ $(document).ready(function() {
|
|||
|
||||
var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}";
|
||||
var checked = $(this).prop('checked');
|
||||
var otp_level;
|
||||
var mfa_level;
|
||||
var otp_secret_key;
|
||||
if(checked){
|
||||
otp_level = 2
|
||||
mfa_level = 2
|
||||
}else{
|
||||
otp_level = 0;
|
||||
mfa_level = 0;
|
||||
otp_secret_key = '';
|
||||
}
|
||||
var body = {
|
||||
'otp_level': otp_level,
|
||||
'mfa_level': mfa_level,
|
||||
'otp_secret_key': otp_secret_key
|
||||
};
|
||||
var success = '{% trans "Update successfully!" %}';
|
||||
|
|
|
@ -86,9 +86,9 @@
|
|||
<tr>
|
||||
<td class="text-navy">{% trans 'MFA certification' %}</td>
|
||||
<td>
|
||||
{% if user.otp_force_enabled %}
|
||||
{% if user.mfa_force_enabled %}
|
||||
{% trans 'Force enable' %}
|
||||
{% elif user.otp_enabled%}
|
||||
{% elif user.mfa_enabled%}
|
||||
{% trans 'Enable' %}
|
||||
{% else %}
|
||||
{% trans 'Disable' %}
|
||||
|
@ -158,8 +158,8 @@
|
|||
<span class="pull-right">
|
||||
<a type="button" class="btn btn-primary btn-xs" style="width: 54px" id=""
|
||||
href="
|
||||
{% if request.user.otp_enabled and request.user.otp_secret_key %}
|
||||
{% if request.user.otp_force_enabled %}
|
||||
{% if request.user.mfa_enabled and request.user.otp_secret_key %}
|
||||
{% if request.user.mfa_force_enabled %}
|
||||
" disabled >{% trans 'Disable' %}
|
||||
{% else %}
|
||||
{% url 'users:user-otp-disable-authentication' %}
|
||||
|
@ -183,7 +183,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if request.user.otp_enabled and request.user.otp_secret_key %}
|
||||
{% if request.user.mfa_enabled and request.user.otp_secret_key %}
|
||||
<tr>
|
||||
<td>{% trans 'Update MFA' %}:</td>
|
||||
<td>
|
||||
|
|
|
@ -171,12 +171,12 @@ class UserFirstLoginView(PermissionsMixin, SessionWizardView):
|
|||
form.instance = self.request.user
|
||||
|
||||
if isinstance(form, forms.UserMFAForm):
|
||||
choices = form.fields["otp_level"].choices
|
||||
if self.request.user.otp_force_enabled:
|
||||
choices = form.fields["mfa_level"].choices
|
||||
if self.request.user.mfa_force_enabled:
|
||||
choices = [(k, v) for k, v in choices if k == 2]
|
||||
else:
|
||||
choices = [(k, v) for k, v in choices if k in [0, 1]]
|
||||
form.fields["otp_level"].choices = choices
|
||||
form.fields["otp_level"].initial = self.request.user.otp_level
|
||||
form.fields["mfa_level"].choices = choices
|
||||
form.fields["mfa_level"].initial = self.request.user.mfa_level
|
||||
|
||||
return form
|
||||
|
|
|
@ -347,7 +347,12 @@ class UserOtpEnableAuthenticationView(FormView):
|
|||
if not user:
|
||||
form.add_error("password", _("Password invalid"))
|
||||
return self.form_invalid(form)
|
||||
return redirect(self.get_success_url())
|
||||
if user.mfa_is_otp():
|
||||
return redirect(self.get_success_url())
|
||||
else:
|
||||
user.enable_mfa()
|
||||
user.save()
|
||||
return redirect('users:user-otp-settings-success')
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('users:user-otp-enable-install-app')
|
||||
|
@ -395,7 +400,7 @@ class UserOtpEnableBindView(TemplateView, FormView):
|
|||
|
||||
def save_otp(self, otp_secret_key):
|
||||
user = get_user_or_tmp_user(self.request)
|
||||
user.enable_otp()
|
||||
user.enable_mfa()
|
||||
user.otp_secret_key = otp_secret_key
|
||||
user.save()
|
||||
|
||||
|
@ -411,7 +416,7 @@ class UserOtpDisableAuthenticationView(FormView):
|
|||
otp_secret_key = user.otp_secret_key
|
||||
|
||||
if check_otp_code(otp_secret_key, otp_code):
|
||||
user.disable_otp()
|
||||
user.disable_mfa()
|
||||
user.save()
|
||||
return super().form_valid(form)
|
||||
else:
|
||||
|
@ -447,7 +452,7 @@ class UserOtpSettingsSuccessView(TemplateView):
|
|||
auth_logout(self.request)
|
||||
title = _('MFA enable success')
|
||||
describe = _('MFA enable success, return login page')
|
||||
if not user.otp_enabled:
|
||||
if not user.mfa_enabled:
|
||||
title = _('MFA disable success')
|
||||
describe = _('MFA disable success, return login page')
|
||||
|
||||
|
|
Loading…
Reference in New Issue