1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-05 08:53:14 +00:00

Rewrite 2fa, do not import models in package level

This commit is contained in:
zhengxie
2018-02-06 16:28:54 +08:00
parent 24b4e4a805
commit 2d56fd73c0
18 changed files with 117 additions and 125 deletions

View File

@@ -9,7 +9,7 @@ from seahub.api2.base import APIView
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import json_response, api_error
from seahub.api2.authentication import TokenAuthentication
from seahub.two_factor import devices_for_user
from seahub.two_factor.models import devices_for_user
class TwoFactorAuthView(APIView):

View File

@@ -1,12 +1,12 @@
# Copyright (c) 2012-2016 Seafile Ltd.
import datetime
from importlib import import_module
from warnings import warn
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.importlib import import_module
from seahub.auth.signals import user_logged_in
from seahub.utils import is_org_context
from constance import config
@@ -101,6 +101,8 @@ def logout(request):
if hasattr(request, 'user'):
from seahub.base.accounts import User
if isinstance(request.user, User):
# Do not directly/indirectly import models in package root level.
from seahub.utils import is_org_context
if is_org_context(request):
org_id = request.user.org.org_id
request.user.remove_org_repo_passwds(org_id)

View File

@@ -88,8 +88,7 @@ def edit_profile(request):
}
if has_two_factor_auth():
from seahub.two_factor.models import StaticDevice
from seahub.two_factor.utils import default_device
from seahub.two_factor.models import StaticDevice, default_device
try:
backup_tokens = StaticDevice.objects.get(

View File

@@ -1,9 +1,4 @@
# Copyright (c) 2012-2016 Seafile Ltd.
import sys
from seahub.auth.signals import user_logged_in
from seahub.two_factor.models import (StaticDevice, TOTPDevice,
PhoneDevice)
############################## django_otp ##############################
DEVICE_ID_SESSION_KEY = 'otp_device_id'
@@ -29,82 +24,3 @@ def login(request, device):
if (user is not None) and (device is not None) and (device.user == user.email):
request.session[DEVICE_ID_SESSION_KEY] = device.persistent_id
request.user.otp_device = device
def _handle_auth_login(sender, request, user, **kwargs):
"""
Automatically persists an OTP device that was set by an OTP-aware
AuthenticationForm.
"""
if hasattr(user, 'otp_device'):
login(request, user.otp_device)
user_logged_in.connect(_handle_auth_login)
def match_token(user, token):
"""
Attempts to verify a :term:`token` on every device attached to the given
user until one of them succeeds. When possible, you should prefer to verify
tokens against specific devices.
:param user: The user supplying the token.
:type user: :class:`~django.contrib.auth.models.User`
:param string token: An OTP token to verify.
:returns: The device that accepted ``token``, if any.
:rtype: :class:`~django_otp.models.Device` or ``None``
"""
matches = (d for d in devices_for_user(user) if d.verify_token(token))
return next(matches, None)
def devices_for_user(user):
"""
Return an iterable of all devices registered to the given user.
Returns an empty iterable for anonymous users.
:param user: standard or custom user object.
:type user: :class:`~seahub.auth.models.User`
:rtype: iterable
"""
if user.is_anonymous():
return
for model in TOTPDevice, PhoneDevice, StaticDevice:
device = model.objects.device_for_user(user.username)
if device:
yield device
def user_has_device(user):
"""
Return ``True`` if the user has at least one device.
Returns ``False`` for anonymous users.
:param user: standard or custom user object.
:type user: :class:`~seahub.auth.models.User`
"""
try:
next(devices_for_user(user))
except StopIteration:
has_device = False
else:
has_device = True
return has_device
def import_class(path):
"""
Imports a class based on a full Python path ('pkg.pkg.mod.Class'). This
does not trap any exceptions if the path is not valid.
"""
module, name = path.rsplit('.', 1)
__import__(module)
mod = sys.modules[module]
cls = getattr(mod, name)
return cls

View File

@@ -3,7 +3,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera
from seahub.auth.decorators import user_passes_test
from seahub.two_factor import user_has_device
from seahub.two_factor.models import user_has_device
from seahub.two_factor.conf import settings
def otp_required(view=None, redirect_field_name='next', login_url=None, if_configured=False):

View File

@@ -6,11 +6,9 @@ from django import forms
from django.forms import ModelForm, Form
from django.utils.translation import ugettext_lazy as _
from seahub.two_factor import devices_for_user, match_token
from seahub.two_factor.oath import totp
from seahub.two_factor.models import (Device, TOTPDevice, StaticDevice,
PhoneDevice)
PhoneDevice, devices_for_user, match_token)
from .models import get_available_methods
from .utils import totp_digits

View File

@@ -7,7 +7,7 @@ except ImportError:
else:
User = get_user_model()
from seahub.two_factor import devices_for_user
from seahub.two_factor.models import devices_for_user
class Command(BaseCommand):

View File

@@ -2,7 +2,7 @@
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth import get_user_model
from ...utils import default_device
from seahub.two_factor.models import default_device
User = get_user_model()

View File

@@ -3,3 +3,81 @@ from seahub.two_factor.models.base import Device, get_available_methods
from seahub.two_factor.models.totp import TOTPDevice
from seahub.two_factor.models.phone import PhoneDevice
from seahub.two_factor.models.static import StaticDevice, StaticToken
def devices_for_user(user):
"""
Return an iterable of all devices registered to the given user.
Returns an empty iterable for anonymous users.
:param user: standard or custom user object.
:type user: :class:`~seahub.auth.models.User`
:rtype: iterable
"""
if user.is_anonymous():
return
for model in TOTPDevice, PhoneDevice, StaticDevice:
device = model.objects.device_for_user(user.username)
if device:
yield device
def user_has_device(user):
"""
Return ``True`` if the user has at least one device.
Returns ``False`` for anonymous users.
:param user: standard or custom user object.
:type user: :class:`~seahub.auth.models.User`
"""
try:
next(devices_for_user(user))
except StopIteration:
has_device = False
else:
has_device = True
return has_device
def default_device(user):
if not user or user.is_anonymous():
return
for device in devices_for_user(user):
if device:
return device
def match_token(user, token):
"""
Attempts to verify a :term:`token` on every device attached to the given
user until one of them succeeds. When possible, you should prefer to verify
tokens against specific devices.
:param user: The user supplying the token.
:type user: :class:`~django.contrib.auth.models.User`
:param string token: An OTP token to verify.
:returns: The device that accepted ``token``, if any.
:rtype: :class:`~django_otp.models.Device` or ``None``
"""
matches = (d for d in devices_for_user(user) if d.verify_token(token))
return next(matches, None)
########## handle signals
from django.dispatch import receiver
from seahub.auth.signals import user_logged_in
from seahub.two_factor import login
@receiver(user_logged_in)
def _handle_auth_login(sender, request, user, **kwargs):
"""
Automatically persists an OTP device that was set by an OTP-aware
AuthenticationForm.
"""
if hasattr(user, 'otp_device'):
login(request, user.otp_device)

View File

@@ -75,7 +75,7 @@ class Device(models.Model):
device == Device.from_persistent_id(device.persistent_id)
"""
from seahub.two_factor import import_class
from seahub.two_factor.utils import import_class
try:
device_type, device_id = path.rsplit('/', 1)

View File

@@ -1,6 +1,7 @@
# Copyright (c) 2012-2016 Seafile Ltd.
from __future__ import absolute_import, division, print_function, unicode_literals
import sys
from binascii import hexlify, unhexlify
from os import urandom
@@ -13,17 +14,6 @@ from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils import six
def default_device(user):
if not user or user.is_anonymous():
return
# to avoid circular import
from seahub.two_factor import devices_for_user
for device in devices_for_user(user):
if device:
return device
def get_otpauth_url(accountname, secret, issuer=None, digits=None):
# For a complete run-through of all the parameters, have a look at the
# specs at:
@@ -116,3 +106,18 @@ def random_hex(length=20):
:rtype: str
"""
return hexlify(urandom(length))
def import_class(path):
"""
Imports a class based on a full Python path ('pkg.pkg.mod.Class'). This
does not trap any exceptions if the path is not valid.
"""
module, name = path.rsplit('.', 1)
__import__(module)
mod = sys.modules[module]
cls = getattr(mod, name)
return cls
# # Move here to avoid circular import
# from seahub.two_factor.models import (StaticDevice, TOTPDevice, PhoneDevice)

View File

@@ -32,13 +32,12 @@ from seahub.auth.forms import AuthenticationForm
from seahub.two_factor import login as two_factor_login
from seahub.two_factor.decorators import otp_required
from seahub.two_factor.models import (StaticToken, StaticDevice,
PhoneDevice, get_available_methods)
from seahub.two_factor.utils import random_hex, totp_digits
from seahub.two_factor.models import (StaticDevice, PhoneDevice,
get_available_methods, default_device)
from seahub.two_factor.utils import random_hex, totp_digits, get_otpauth_url
from seahub.two_factor.forms import (MethodForm, TOTPDeviceForm,
PhoneNumberForm, DeviceValidationForm)
from seahub.two_factor.utils import get_otpauth_url, default_device
from seahub.two_factor.views.utils import (class_view_decorator,
CheckTwoFactorEnabledMixin,
IdempotentSessionWizardView)

View File

@@ -28,11 +28,10 @@ from seahub.utils.ip import get_remote_ip
from seahub.profile.models import Profile
from seahub.two_factor import login as two_factor_login
from seahub.two_factor import user_has_device
from seahub.two_factor.models import StaticDevice, TOTPDevice, PhoneDevice
from seahub.two_factor.models import (StaticDevice, TOTPDevice, default_device,
user_has_device)
from seahub.two_factor.forms import TOTPTokenAuthForm, BackupTokenAuthForm, AuthenticationTokenForm
from seahub.two_factor.utils import default_device
from seahub.two_factor.views.utils import class_view_decorator
from seahub.utils.auth import get_login_bg_image_path

View File

@@ -10,7 +10,7 @@ from seahub.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import PermissionDenied
from django.shortcuts import redirect
from ..utils import default_device
from seahub.two_factor.models import default_device
class OTPRequiredMixin(object):

View File

@@ -8,10 +8,9 @@ from django.views.generic import TemplateView, FormView
from seahub.auth.decorators import login_required
from seahub.two_factor import user_has_device, devices_for_user
from seahub.two_factor.forms import DisableForm
from seahub.two_factor.models import StaticDevice
from seahub.two_factor.utils import default_device
from seahub.two_factor.models import (StaticDevice, devices_for_user,
user_has_device, default_device)
from seahub.two_factor.views.utils import class_view_decorator, CheckTwoFactorEnabledMixin

View File

@@ -44,7 +44,7 @@ from seahub.invitations.models import Invitation
from seahub.role_permissions.utils import get_available_roles, \
get_available_admin_roles
from seahub.role_permissions.models import AdminRole
from seahub.two_factor.utils import default_device
from seahub.two_factor.models import default_device
from seahub.utils import IS_EMAIL_CONFIGURED, string2list, is_valid_username, \
is_pro_version, send_html_email, get_user_traffic_list, get_server_id, \
handle_virus_record, get_virus_record_by_id, \

View File

@@ -3,6 +3,7 @@ import pytest
from django.core.urlresolvers import reverse
from seahub.test_utils import BaseTestCase
from seahub.two_factor.models import TOTPDevice, devices_for_user
TRAVIS = 'TRAVIS' in os.environ
@@ -13,12 +14,9 @@ class TwoFactorAuthViewTest(BaseTestCase):
self.login_as(self.admin)
def test_can_disable_two_factor_auth(self):
from seahub.two_factor.models import (StaticDevice, TOTPDevice,
PhoneDevice)
totp = TOTPDevice(user=self.admin, name="", confirmed=1)
totp.save()
from seahub.two_factor import devices_for_user
devices = devices_for_user(self.admin)
i = 0
for device in devices_for_user(self.admin):

View File

@@ -1,8 +1,7 @@
from django.core.urlresolvers import reverse
from constance import config
from seahub.two_factor.models import StaticDevice
from seahub.two_factor import user_has_device
from seahub.two_factor.models import StaticDevice, user_has_device
from seahub.test_utils import BaseTestCase