mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-19 10:26:17 +00:00
35
seahub/api2/endpoints/admin/two_factor_auth.py
Normal file
35
seahub/api2/endpoints/admin/two_factor_auth.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.permissions import IsAdminUser
|
||||||
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from seahub.base.accounts import User
|
||||||
|
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.utils.two_factor_auth import has_two_factor_auth, two_factor_auth_enabled
|
||||||
|
|
||||||
|
|
||||||
|
class TwoFactorAuthView(APIView):
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
permission_classes = (IsAdminUser,)
|
||||||
|
|
||||||
|
def delete(self, request, email):
|
||||||
|
if not email:
|
||||||
|
error_msg = "email can not be empty"
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
try:
|
||||||
|
_user = User.objects.get(email=email)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
error_msg = "User %s not found" % email
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
from seahub_extra.two_factor import devices_for_user
|
||||||
|
devices = devices_for_user(_user)
|
||||||
|
if devices:
|
||||||
|
for device in devices:
|
||||||
|
device.delete()
|
||||||
|
return Response({'success':True}, status=status.HTTP_200_OK)
|
@@ -4,6 +4,7 @@ from django.conf.urls import patterns, url, include
|
|||||||
from .views import *
|
from .views import *
|
||||||
from .views_misc import ServerInfoView
|
from .views_misc import ServerInfoView
|
||||||
from .views_auth import LogoutDeviceView, ClientLoginTokenView
|
from .views_auth import LogoutDeviceView, ClientLoginTokenView
|
||||||
|
from .endpoints.admin.two_factor_auth import TwoFactorAuthView
|
||||||
from .endpoints.dir_shared_items import DirSharedItemsEndpoint
|
from .endpoints.dir_shared_items import DirSharedItemsEndpoint
|
||||||
from .endpoints.account import Account
|
from .endpoints.account import Account
|
||||||
from .endpoints.shared_upload_links import SharedUploadLinksView
|
from .endpoints.shared_upload_links import SharedUploadLinksView
|
||||||
@@ -24,6 +25,7 @@ urlpatterns = patterns('',
|
|||||||
url(r'^server-info/$', ServerInfoView.as_view()),
|
url(r'^server-info/$', ServerInfoView.as_view()),
|
||||||
url(r'^logout-device/$', LogoutDeviceView.as_view()),
|
url(r'^logout-device/$', LogoutDeviceView.as_view()),
|
||||||
url(r'^client-login/$', ClientLoginTokenView.as_view()),
|
url(r'^client-login/$', ClientLoginTokenView.as_view()),
|
||||||
|
url(r'^two-factor-auth/(?P<email>\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/$', TwoFactorAuthView.as_view(), name="two-factor-auth-view"),
|
||||||
url(r'^device-wiped/$', RemoteWipeReportView.as_view()),
|
url(r'^device-wiped/$', RemoteWipeReportView.as_view()),
|
||||||
url(r'^wopi/', include('seahub.wopi.urls')),
|
url(r'^wopi/', include('seahub.wopi.urls')),
|
||||||
|
|
||||||
|
@@ -94,6 +94,19 @@
|
|||||||
</span>
|
</span>
|
||||||
<span id="set-quota" title="{% trans "Edit Quota" %}" class="sf2-icon-edit op-icon"></span>
|
<span id="set-quota" title="{% trans "Edit Quota" %}" class="sf2-icon-edit op-icon"></span>
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
|
{% if two_factor_auth_enabled %}
|
||||||
|
<dt>{% trans "Two-Factor Authentication" %}</dt>
|
||||||
|
<dd>
|
||||||
|
{% if default_device %}
|
||||||
|
<p class="txt-before-btn">{% trans "Status" %}: {% trans "enabled" %}</p>
|
||||||
|
<button id="disable-two-factor-auth">{% trans "Disable Two-Factor Authentication" %}</button>
|
||||||
|
{% else %}
|
||||||
|
<button disabled="disabled" class="btn-disabled">{% trans "Disable Two-Factor Authentication" %}</button>
|
||||||
|
{% endif %}
|
||||||
|
</dd>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<form id="set-name-form" method="post" action="" class="hide">{% csrf_token %}
|
<form id="set-name-form" method="post" action="" class="hide">{% csrf_token %}
|
||||||
@@ -129,7 +142,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="owned">
|
<div id="owned" class="hide">
|
||||||
{% if owned_repos %}
|
{% if owned_repos %}
|
||||||
<table class="repo-list">
|
<table class="repo-list">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -178,7 +191,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="shared">
|
<div id="shared" class="hide">
|
||||||
{% if in_repos %}
|
{% if in_repos %}
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -216,7 +229,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="shared-links">
|
<div id="shared-links" class="hide">
|
||||||
{% if user_shared_links%}
|
{% if user_shared_links%}
|
||||||
<table class="sharelink-list">
|
<table class="sharelink-list">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -264,7 +277,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="user-admin-groups">
|
<div id="user-admin-groups" class="hide">
|
||||||
{% if personal_groups %}
|
{% if personal_groups %}
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -332,6 +345,27 @@ $('#set-quota').click(function() {
|
|||||||
$('#simplemodal-container').css({'width':'auto', 'height':'auto'});
|
$('#simplemodal-container').css({'width':'auto', 'height':'auto'});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#disable-two-factor-auth").click(function(){
|
||||||
|
$.ajax({
|
||||||
|
url: '{% url 'two-factor-auth-view' email %}',
|
||||||
|
type: 'DELETE',
|
||||||
|
cache: false,
|
||||||
|
beforeSend: prepareCSRFToken,
|
||||||
|
success:function(data){
|
||||||
|
location.reload(true);
|
||||||
|
},
|
||||||
|
error: function(xhr, textStatus, errorThrown) {
|
||||||
|
var err_msg;
|
||||||
|
if (xhr.responseText) {
|
||||||
|
err_msg = $.parseJSON(xhr.responseText).error_msg;
|
||||||
|
} else {
|
||||||
|
err_msg = "{% trans "Failed. Please check the network." %}";
|
||||||
|
}
|
||||||
|
feedback(err_msg, 'error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$('#set-name-form').submit(function() {
|
$('#set-name-form').submit(function() {
|
||||||
var nickname = $.trim($('[name="nickname"]', $(this)).val());
|
var nickname = $.trim($('[name="nickname"]', $(this)).val());
|
||||||
var $name = $('#nickname');
|
var $name = $('#nickname');
|
||||||
@@ -488,6 +522,7 @@ $('#set-quota-form').submit(function() {
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
addConfirmTo($('.rm-grp'), {
|
addConfirmTo($('.rm-grp'), {
|
||||||
'title': "{% trans "Delete Group" %}",
|
'title': "{% trans "Delete Group" %}",
|
||||||
'con': "{% trans "Are you sure you want to delete %s ?" %}",
|
'con': "{% trans "Are you sure you want to delete %s ?" %}",
|
||||||
|
@@ -76,7 +76,7 @@ try:
|
|||||||
from seahub.settings import MULTI_TENANCY
|
from seahub.settings import MULTI_TENANCY
|
||||||
except ImportError:
|
except ImportError:
|
||||||
MULTI_TENANCY = False
|
MULTI_TENANCY = False
|
||||||
from seahub.utils.two_factor_auth import HAS_TWO_FACTOR_AUTH
|
from seahub.utils.two_factor_auth import has_two_factor_auth, HAS_TWO_FACTOR_AUTH
|
||||||
from termsandconditions.models import TermsAndConditions
|
from termsandconditions.models import TermsAndConditions
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -618,6 +618,13 @@ def user_info(request, email):
|
|||||||
else:
|
else:
|
||||||
g.role = _('Member')
|
g.role = _('Member')
|
||||||
|
|
||||||
|
_default_device = False
|
||||||
|
_has_two_factor_auth = has_two_factor_auth()
|
||||||
|
if _has_two_factor_auth:
|
||||||
|
from seahub_extra.two_factor.utils import default_device
|
||||||
|
_user = User.objects.get(email=email)
|
||||||
|
_default_device = default_device(_user)
|
||||||
|
|
||||||
return render_to_response(
|
return render_to_response(
|
||||||
'sysadmin/userinfo.html', {
|
'sysadmin/userinfo.html', {
|
||||||
'owned_repos': owned_repos,
|
'owned_repos': owned_repos,
|
||||||
@@ -631,6 +638,8 @@ def user_info(request, email):
|
|||||||
'user_shared_links': user_shared_links,
|
'user_shared_links': user_shared_links,
|
||||||
'enable_sys_admin_view_repo': ENABLE_SYS_ADMIN_VIEW_REPO,
|
'enable_sys_admin_view_repo': ENABLE_SYS_ADMIN_VIEW_REPO,
|
||||||
'personal_groups': personal_groups,
|
'personal_groups': personal_groups,
|
||||||
|
'two_factor_auth_enabled': _has_two_factor_auth,
|
||||||
|
'default_device': _default_device,
|
||||||
}, context_instance=RequestContext(request))
|
}, context_instance=RequestContext(request))
|
||||||
|
|
||||||
@login_required_ajax
|
@login_required_ajax
|
||||||
|
41
tests/api/endpoints/admin/test_two_factor_auth.py
Normal file
41
tests/api/endpoints/admin/test_two_factor_auth.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from seahub.test_utils import BaseTestCase
|
||||||
|
|
||||||
|
TRAVIS = 'TRAVIS' in os.environ
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(TRAVIS, reason="")
|
||||||
|
class TwoFactorAuthViewTest(BaseTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.login_as(self.admin)
|
||||||
|
|
||||||
|
def test_can_disable_two_factor_auth(self):
|
||||||
|
from seahub_extra.two_factor.models import (StaticDevice, TOTPDevice,
|
||||||
|
PhoneDevice)
|
||||||
|
totp = TOTPDevice(user=self.admin, name="", confirmed=1)
|
||||||
|
totp.save()
|
||||||
|
|
||||||
|
from seahub_extra.two_factor import devices_for_user
|
||||||
|
devices = devices_for_user(self.admin)
|
||||||
|
i = 0
|
||||||
|
for device in devices_for_user(self.admin):
|
||||||
|
if device:
|
||||||
|
i+=1
|
||||||
|
assert i > 0
|
||||||
|
resp = self.client.delete(reverse('two-factor-auth-view', args=[str(self.admin.username)]))
|
||||||
|
assert resp.status_code == 200
|
||||||
|
i = 0
|
||||||
|
for device in devices_for_user(self.admin):
|
||||||
|
if device:
|
||||||
|
i+=1
|
||||||
|
assert i == 0
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
try:
|
||||||
|
for device in devices_for_user(self.admin):
|
||||||
|
device.delete()
|
||||||
|
except:
|
||||||
|
pass
|
Reference in New Issue
Block a user