Merge pull request #4134 from jumpserver/fix-mfa-1.5

Fix mfa 1.5
pull/4147/head 2.0.1
老广 2020-06-22 19:05:15 +08:00 committed by GitHub
commit 2acc1dc875
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 4 deletions

View File

@ -10,3 +10,7 @@ class HttpResponseTemporaryRedirect(HttpResponse):
def __init__(self, redirect_to): def __init__(self, redirect_to):
HttpResponse.__init__(self) HttpResponse.__init__(self)
self['Location'] = iri_to_uri(redirect_to) self['Location'] = iri_to_uri(redirect_to)
def get_remote_addr(request):
return request.META.get("HTTP_X_FORWARDED_HOST") or request.META.get("REMOTE_ADDR")

View File

@ -86,6 +86,8 @@ TASK_LOG_KEEP_DAYS = CONFIG.TASK_LOG_KEEP_DAYS
ORG_CHANGE_TO_URL = CONFIG.ORG_CHANGE_TO_URL ORG_CHANGE_TO_URL = CONFIG.ORG_CHANGE_TO_URL
WINDOWS_SKIP_ALL_MANUAL_PASSWORD = CONFIG.WINDOWS_SKIP_ALL_MANUAL_PASSWORD WINDOWS_SKIP_ALL_MANUAL_PASSWORD = CONFIG.WINDOWS_SKIP_ALL_MANUAL_PASSWORD
AUTH_EXPIRED_SECONDS = 60 * 5
# XPACK # XPACK
XPACK_LICENSE_IS_VALID = DYNAMIC.XPACK_LICENSE_IS_VALID XPACK_LICENSE_IS_VALID = DYNAMIC.XPACK_LICENSE_IS_VALID

View File

@ -5,8 +5,8 @@ import re
import pyotp import pyotp
import base64 import base64
import logging import logging
import time
from django.http import Http404
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.core.cache import cache from django.core.cache import cache
@ -333,3 +333,15 @@ def get_source_choices():
if settings.AUTH_CAS: if settings.AUTH_CAS:
choices.append((User.SOURCE_CAS, choices_all[User.SOURCE_CAS])) choices.append((User.SOURCE_CAS, choices_all[User.SOURCE_CAS]))
return choices return choices
def is_auth_time_valid(session, key):
return True if session.get(key, 0) > time.time() else False
def is_auth_password_time_valid(session):
return is_auth_time_valid(session, 'auth_password_expired_at')
def is_auth_otp_time_valid(session):
return is_auth_time_valid(session, 'auth_opt_expired_at')

View File

@ -1,4 +1,5 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
import time
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy, reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -6,13 +7,17 @@ from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from django.contrib.auth import logout as auth_logout from django.contrib.auth import logout as auth_logout
from django.conf import settings from django.conf import settings
from django.http.response import HttpResponseForbidden
from common.utils import get_logger from authentication.mixins import AuthMixin
from users.models import User
from common.utils import get_logger, get_object_or_none
from common.permissions import IsValidUser from common.permissions import IsValidUser
from ... import forms from ... import forms
from .password import UserVerifyPasswordView from .password import UserVerifyPasswordView
from ...utils import ( from ...utils import (
generate_otp_uri, check_otp_code, get_user_or_pre_auth_user, generate_otp_uri, check_otp_code, get_user_or_pre_auth_user,
is_auth_password_time_valid, is_auth_otp_time_valid
) )
__all__ = [ __all__ = [
@ -46,11 +51,50 @@ class UserOtpEnableInstallAppView(TemplateView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class UserOtpEnableBindView(TemplateView, FormView): class UserOtpEnableBindView(AuthMixin, TemplateView, FormView):
template_name = 'users/user_otp_enable_bind.html' template_name = 'users/user_otp_enable_bind.html'
form_class = forms.UserCheckOtpCodeForm form_class = forms.UserCheckOtpCodeForm
success_url = reverse_lazy('authentication:user-otp-settings-success') success_url = reverse_lazy('authentication:user-otp-settings-success')
def get(self, request, *args, **kwargs):
if self._check_can_bind():
return super().get(request, *args, **kwargs)
return HttpResponseForbidden()
def post(self, request, *args, **kwargs):
if self._check_can_bind():
return super().post(request, *args, **kwargs)
return HttpResponseForbidden()
def _check_authenticated_user_can_bind(self):
user = self.request.user
session = self.request.session
if not user.mfa_enabled:
return is_auth_password_time_valid(session)
if not user.otp_secret_key:
return is_auth_password_time_valid(session)
return is_auth_otp_time_valid(session)
def _check_unauthenticated_user_can_bind(self):
session_user = None
if not self.request.session.is_empty():
user_id = self.request.session.get('user_id')
session_user = get_object_or_none(User, pk=user_id)
if session_user:
if all((is_auth_password_time_valid(self.request.session), session_user.mfa_enabled, not session_user.otp_secret_key)):
return True
return False
def _check_can_bind(self):
if self.request.user.is_authenticated:
return self._check_authenticated_user_can_bind()
else:
return self._check_unauthenticated_user_can_bind()
def form_valid(self, form): def form_valid(self, form):
otp_code = form.cleaned_data.get('otp_code') otp_code = form.cleaned_data.get('otp_code')
otp_secret_key = self.request.session.get('otp_secret_key', '') otp_secret_key = self.request.session.get('otp_secret_key', '')
@ -116,6 +160,7 @@ class UserOtpUpdateView(FormView):
valid = user.check_mfa(otp_code) valid = user.check_mfa(otp_code)
if valid: if valid:
self.request.session['auth_opt_expired_at'] = time.time() + settings.AUTH_EXPIRED_SECONDS
return super().form_valid(form) return super().form_valid(form)
else: else:
error = _('MFA code invalid, or ntp sync server time') error = _('MFA code invalid, or ntp sync server time')

View File

@ -1,8 +1,10 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
import time
from django.conf import settings
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic.edit import UpdateView, FormView from django.views.generic.edit import UpdateView, FormView
from django.contrib.auth import logout as auth_logout from django.contrib.auth import logout as auth_logout
@ -76,6 +78,7 @@ class UserVerifyPasswordView(FormView):
user.save() user.save()
self.request.session['user_id'] = str(user.id) self.request.session['user_id'] = str(user.id)
self.request.session['auth_password'] = 1 self.request.session['auth_password'] = 1
self.request.session['auth_password_expired_at'] = time.time() + settings.AUTH_EXPIRED_SECONDS
return redirect(self.get_success_url()) return redirect(self.get_success_url())
def get_success_url(self): def get_success_url(self):