mirror of https://github.com/jumpserver/jumpserver
[Update] 修改MFA
parent
c9ee8edeaf
commit
bb1349e962
|
@ -24,7 +24,7 @@ class MFAChallengeApi(AuthMixin, CreateAPIView):
|
||||||
try:
|
try:
|
||||||
user = self.get_user_from_session()
|
user = self.get_user_from_session()
|
||||||
code = serializer.validated_data.get('code')
|
code = serializer.validated_data.get('code')
|
||||||
valid = user.check_otp(code)
|
valid = user.check_mfa(code)
|
||||||
if not valid:
|
if not valid:
|
||||||
self.request.session['auth_mfa'] = ''
|
self.request.session['auth_mfa'] = ''
|
||||||
raise errors.MFAFailedError(
|
raise errors.MFAFailedError(
|
||||||
|
@ -52,7 +52,7 @@ class UserOtpVerifyApi(CreateAPIView):
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
code = serializer.validated_data["code"]
|
code = serializer.validated_data["code"]
|
||||||
|
|
||||||
if request.user.check_otp(code):
|
if request.user.check_mfa(code):
|
||||||
request.session["MFA_VERIFY_TIME"] = int(time.time())
|
request.session["MFA_VERIFY_TIME"] = int(time.time())
|
||||||
return Response({"ok": "1"})
|
return Response({"ok": "1"})
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -27,23 +27,6 @@ class CreateUserMixin:
|
||||||
user.save()
|
user.save()
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def _get_auth_packet(self, username, password, client):
|
|
||||||
"""
|
|
||||||
Get the pyrad authentication packet for the username/password and the
|
|
||||||
given pyrad client.
|
|
||||||
"""
|
|
||||||
pkt = client.CreateAuthPacket(code=AccessRequest,
|
|
||||||
User_Name=username)
|
|
||||||
if settings.CONFIG.RADIUS_ENCRYPT_PASSWORD:
|
|
||||||
password = pkt.PwCrypt(password)
|
|
||||||
else:
|
|
||||||
password = password
|
|
||||||
pkt["User-Password"] = password
|
|
||||||
pkt["NAS-Identifier"] = 'django-radius'
|
|
||||||
for key, val in list(getattr(settings, 'RADIUS_ATTRIBUTES', {}).items()):
|
|
||||||
pkt[key] = val
|
|
||||||
return pkt
|
|
||||||
|
|
||||||
|
|
||||||
class RadiusBackend(CreateUserMixin, RADIUSBackend):
|
class RadiusBackend(CreateUserMixin, RADIUSBackend):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -109,8 +109,7 @@ class CredentialError(AuthFailedNeedLogMixin, AuthFailedNeedBlockMixin, AuthFail
|
||||||
|
|
||||||
|
|
||||||
class MFAFailedError(AuthFailedNeedLogMixin, AuthFailedError):
|
class MFAFailedError(AuthFailedNeedLogMixin, AuthFailedError):
|
||||||
reason = reason_mfa_failed
|
error = reason_mfa_failed
|
||||||
error = 'mfa_failed'
|
|
||||||
msg = mfa_failed_msg
|
msg = mfa_failed_msg
|
||||||
|
|
||||||
def __init__(self, username, request):
|
def __init__(self, username, request):
|
||||||
|
|
|
@ -97,7 +97,7 @@ class AuthMixin:
|
||||||
|
|
||||||
def check_user_mfa(self, code):
|
def check_user_mfa(self, code):
|
||||||
user = self.get_user_from_session()
|
user = self.get_user_from_session()
|
||||||
ok = user.check_otp(code)
|
ok = user.check_mfa(code)
|
||||||
if ok:
|
if ok:
|
||||||
self.request.session['auth_mfa'] = 1
|
self.request.session['auth_mfa'] = 1
|
||||||
self.request.session['auth_mfa_time'] = time.time()
|
self.request.session['auth_mfa_time'] = time.time()
|
||||||
|
|
|
@ -50,7 +50,7 @@ class LoginConfirmSetting(CommonModelMixin):
|
||||||
|
|
||||||
def create_confirm_ticket(self, request=None):
|
def create_confirm_ticket(self, request=None):
|
||||||
from tickets.models import Ticket
|
from tickets.models import Ticket
|
||||||
title = '[' + __('Login confirm') + ']: {}'.format(self.user)
|
title = _('Login confirm') + '{}'.format(self.user)
|
||||||
if request:
|
if request:
|
||||||
remote_addr = get_request_ip(request)
|
remote_addr = get_request_ip(request)
|
||||||
city = get_ip_city(remote_addr)
|
city = get_ip_city(remote_addr)
|
||||||
|
|
|
@ -20,6 +20,6 @@ class UserLoginOtpView(mixins.AuthMixin, FormView):
|
||||||
self.check_user_mfa(otp_code)
|
self.check_user_mfa(otp_code)
|
||||||
return redirect_to_guard_view()
|
return redirect_to_guard_view()
|
||||||
except errors.MFAFailedError as e:
|
except errors.MFAFailedError as e:
|
||||||
form.add_error('otp_code', e.reason)
|
form.add_error('otp_code', e.msg)
|
||||||
return super().form_invalid(form)
|
return super().form_invalid(form)
|
||||||
|
|
||||||
|
|
|
@ -172,8 +172,8 @@ class UserResetOTPApi(UserQuerysetMixin, generics.RetrieveAPIView):
|
||||||
if user == request.user:
|
if user == request.user:
|
||||||
msg = _("Could not reset self otp, use profile reset instead")
|
msg = _("Could not reset self otp, use profile reset instead")
|
||||||
return Response({"error": msg}, status=401)
|
return Response({"error": msg}, status=401)
|
||||||
if user.mfa_enabled and user.otp_secret_key:
|
if user.mfa_enabled:
|
||||||
user.otp_secret_key = ''
|
user.reset_mfa()
|
||||||
user.save()
|
user.save()
|
||||||
logout(request)
|
logout(request)
|
||||||
return Response({"msg": "success"})
|
return Response({"msg": "success"})
|
||||||
|
|
|
@ -158,8 +158,8 @@ class UserUpdateForm(UserCreateUpdateFormMixin):
|
||||||
|
|
||||||
|
|
||||||
class UserProfileForm(forms.ModelForm):
|
class UserProfileForm(forms.ModelForm):
|
||||||
username = forms.CharField(disabled=True)
|
username = forms.CharField(disabled=True, label=_("Username"))
|
||||||
name = forms.CharField(disabled=True)
|
name = forms.CharField(disabled=True, label=_("Name"))
|
||||||
email = forms.CharField(disabled=True)
|
email = forms.CharField(disabled=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -375,13 +375,17 @@ class MFAMixin:
|
||||||
self.mfa_level = 0
|
self.mfa_level = 0
|
||||||
self.otp_secret_key = None
|
self.otp_secret_key = None
|
||||||
|
|
||||||
|
def reset_mfa(self):
|
||||||
|
if self.mfa_is_otp():
|
||||||
|
self.otp_secret_key = ''
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mfa_is_otp():
|
def mfa_is_otp():
|
||||||
if settings.CONFIG.OTP_IN_RADIUS:
|
if settings.CONFIG.OTP_IN_RADIUS:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def check_otp_on_radius(self, code):
|
def check_radius(self, code):
|
||||||
from authentication.backends.radius import RadiusBackend
|
from authentication.backends.radius import RadiusBackend
|
||||||
backend = RadiusBackend()
|
backend = RadiusBackend()
|
||||||
user = backend.authenticate(None, username=self.username, password=code)
|
user = backend.authenticate(None, username=self.username, password=code)
|
||||||
|
@ -391,13 +395,17 @@ class MFAMixin:
|
||||||
|
|
||||||
def check_otp(self, code):
|
def check_otp(self, code):
|
||||||
from ..utils import check_otp_code
|
from ..utils import check_otp_code
|
||||||
|
return check_otp_code(self.otp_secret_key, code)
|
||||||
|
|
||||||
|
def check_mfa(self, code):
|
||||||
if settings.CONFIG.OTP_IN_RADIUS:
|
if settings.CONFIG.OTP_IN_RADIUS:
|
||||||
return self.check_otp_on_radius(code)
|
return self.check_radius(code)
|
||||||
else:
|
else:
|
||||||
return check_otp_code(self.otp_secret_key, code)
|
return self.check_otp(code)
|
||||||
|
|
||||||
def mfa_enabled_but_not_set(self):
|
def mfa_enabled_but_not_set(self):
|
||||||
if self.mfa_enabled and self.mfa_is_otp() and not self.otp_secret_key:
|
if self.mfa_enabled and \
|
||||||
|
self.mfa_is_otp() and not self.otp_secret_key:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
|
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
|
||||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
||||||
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
|
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
|
||||||
<script src="{% static "js/vue.min.js" %}"></script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="wrapper wrapper-content animated fadeInRight">
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
@ -158,8 +157,9 @@
|
||||||
</span></td>
|
</span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% trans 'Force enabled MFA' %}:</td>
|
<td>{% trans 'Force enabled MFA' %}:</td>
|
||||||
<td><span class="pull-right">
|
<td>
|
||||||
|
<span class="pull-right">
|
||||||
<div class="switch">
|
<div class="switch">
|
||||||
<div class="onoffswitch">
|
<div class="onoffswitch">
|
||||||
<input type="checkbox" class="onoffswitch-checkbox" {% if user_object.mfa_force_enabled %} checked {% endif %}
|
<input type="checkbox" class="onoffswitch-checkbox" {% if user_object.mfa_force_enabled %} checked {% endif %}
|
||||||
|
@ -170,7 +170,8 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</span></td>
|
</span>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% trans 'Reset MFA' %}:</td>
|
<td>{% trans 'Reset MFA' %}:</td>
|
||||||
|
|
|
@ -158,7 +158,7 @@
|
||||||
<span class="pull-right">
|
<span class="pull-right">
|
||||||
<a type="button" class="btn btn-primary btn-xs" style="width: 54px" id=""
|
<a type="button" class="btn btn-primary btn-xs" style="width: 54px" id=""
|
||||||
href="
|
href="
|
||||||
{% if request.user.mfa_enabled and request.user.otp_secret_key %}
|
{% if request.user.mfa_enabled %}
|
||||||
{% if request.user.mfa_force_enabled %}
|
{% if request.user.mfa_force_enabled %}
|
||||||
" disabled >{% trans 'Disable' %}
|
" disabled >{% trans 'Disable' %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -183,7 +183,7 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if request.user.mfa_enabled and request.user.otp_secret_key %}
|
{% if request.user.mfa_enabled %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% trans 'Update MFA' %}:</td>
|
<td>{% trans 'Update MFA' %}:</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
Loading…
Reference in New Issue