From 66c52951def5efe544bf7ecfbfbc9ba6b50fefff Mon Sep 17 00:00:00 2001 From: lian Date: Mon, 10 Dec 2018 16:43:37 +0800 Subject: [PATCH 1/8] update user profile page add ENABLE_DELETE_ACCOUNT and ENABLE_UPDATE_USER_INFO settings --- seahub/profile/forms.py | 5 +++++ seahub/profile/templates/profile/set_profile.html | 13 ++++++++++--- seahub/profile/views.py | 9 +++++++++ seahub/settings.py | 3 +++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/seahub/profile/forms.py b/seahub/profile/forms.py index 7d50814f45..3a53d05fa7 100644 --- a/seahub/profile/forms.py +++ b/seahub/profile/forms.py @@ -5,6 +5,8 @@ from django.utils.translation import ugettext_lazy as _ from seahub.profile.models import Profile, DetailedProfile +from seahub.settings import ENABLE_UPDATE_USER_INFO + class ProfileForm(forms.Form): nickname = forms.CharField(max_length=64, required=False) intro = forms.CharField(max_length=256, required=False) @@ -13,6 +15,9 @@ class ProfileForm(forms.Form): """ Validates that nickname should not include '/' """ + if not ENABLE_UPDATE_USER_INFO: + raise forms.ValidationError(_(u"Permission denied.")) + if "/" in self.cleaned_data["nickname"]: raise forms.ValidationError(_(u"Name should not include '/'.")) diff --git a/seahub/profile/templates/profile/set_profile.html b/seahub/profile/templates/profile/set_profile.html index 88b4be18dc..495fb8dccc 100644 --- a/seahub/profile/templates/profile/set_profile.html +++ b/seahub/profile/templates/profile/set_profile.html @@ -27,7 +27,9 @@
  • {% trans "Two-Factor Authentication" %}
  • {% endif %} + {% if ENABLE_DELETE_ACCOUNT %}
  • {% trans "Delete Account" %}
  • + {% endif %} @@ -46,7 +48,7 @@
    {% csrf_token %} - + {% for error in form.nickname.errors %} {{ error|escape }} {% endfor %} @@ -65,14 +67,15 @@ {% endif %} {% if form.telephone %} - + {% for error in form.telephone.errors %} {{ error|escape }} {% endfor %}
    {% endif %} - + {% if ENABLE_UPDATE_USER_INFO %} + {% endif %}
    @@ -150,6 +153,7 @@ {% endif %} +{% if ENABLE_DELETE_ACCOUNT %}

    {% trans "Delete Account" %}

    {% trans "This operation will not be reverted. Please think twice!" %}

    @@ -158,6 +162,7 @@
    +{% endif %} @@ -213,6 +218,7 @@ $('#user-avatar-input').on('change', function() { $('#user-basic-info .submit').css({'margin-left': $('#user-basic-info label').outerWidth(true)}).removeClass('vh'); +{% if ENABLE_DELETE_ACCOUNT %} $('#account-delete-btn').on('click', function () { var title = "{% trans "Delete Account" %}", con = "{% trans "Really want to delete your account?" %}"; @@ -225,6 +231,7 @@ $('#account-delete-btn').on('click', function () { $('#account-delete-form').trigger('submit'); }); }); +{% endif %} {% if ENABLE_ADDRESSBOOK_OPT_IN %} $("#list-in-address-book input[type='checkbox']").on('change', function() { diff --git a/seahub/profile/views.py b/seahub/profile/views.py index 320f6fdd7e..a0ed306529 100644 --- a/seahub/profile/views.py +++ b/seahub/profile/views.py @@ -23,6 +23,8 @@ from seahub.utils import is_ldap_user from seahub.utils.two_factor_auth import has_two_factor_auth from seahub.views import get_owned_repo_list +from seahub.settings import ENABLE_DELETE_ACCOUNT, ENABLE_UPDATE_USER_INFO + @login_required def edit_profile(request): """ @@ -93,6 +95,8 @@ def edit_profile(request): 'two_factor_auth_enabled': has_two_factor_auth(), 'ENABLE_CHANGE_PASSWORD': settings.ENABLE_CHANGE_PASSWORD, 'ENABLE_WEBDAV_SECRET': settings.ENABLE_WEBDAV_SECRET, + 'ENABLE_DELETE_ACCOUNT': ENABLE_DELETE_ACCOUNT, + 'ENABLE_UPDATE_USER_INFO': ENABLE_UPDATE_USER_INFO, 'webdav_passwd': webdav_passwd, } @@ -173,6 +177,11 @@ def get_user_profile(request, user): @login_required def delete_user_account(request): + if not ENABLE_DELETE_ACCOUNT: + messages.error(request, _(u'Permission denied.')) + next = request.META.get('HTTP_REFERER', settings.SITE_ROOT) + return HttpResponseRedirect(next) + if request.method != 'POST': raise Http404 diff --git a/seahub/settings.py b/seahub/settings.py index 7da40c234f..b099df210d 100644 --- a/seahub/settings.py +++ b/seahub/settings.py @@ -361,6 +361,9 @@ FORCE_PASSWORD_CHANGE = True # Enable a user to change password in 'settings' page. ENABLE_CHANGE_PASSWORD = True +ENABLE_DELETE_ACCOUNT = True +ENABLE_UPDATE_USER_INFO = True + # Enable or disable repo history setting ENABLE_REPO_HISTORY_SETTING = True From 6fcc789f8816c9429fba48833f02fdcf061b2bdb Mon Sep 17 00:00:00 2001 From: lian Date: Sat, 22 Dec 2018 15:28:09 +0800 Subject: [PATCH 2/8] add new org user/info api (#2625) --- seahub/api2/endpoints/admin/org_users.py | 66 +++++++++++++-- seahub/api2/endpoints/admin/organizations.py | 45 +++++++++-- seahub/urls.py | 3 +- tests/api/endpoints/admin/test_org_users.py | 24 ++++++ tests/api/endpoints/admin/test_orgs.py | 85 ++++++++++++++++++++ 5 files changed, 209 insertions(+), 14 deletions(-) create mode 100644 tests/api/endpoints/admin/test_orgs.py diff --git a/seahub/api2/endpoints/admin/org_users.py b/seahub/api2/endpoints/admin/org_users.py index e40c609b2a..af9b49e62a 100644 --- a/seahub/api2/endpoints/admin/org_users.py +++ b/seahub/api2/endpoints/admin/org_users.py @@ -34,9 +34,7 @@ logger = logging.getLogger(__name__) def get_org_user_info(org_id, email): user_info = {} - user_obj = User.objects.get(email=email) user_info['org_id'] = org_id - user_info['active'] = user_obj.is_active user_info['email'] = email user_info['name'] = email2nickname(email) user_info['contact_email'] = email2contact_email(email) @@ -46,7 +44,6 @@ def get_org_user_info(org_id, email): org_user_quota_usage = seafile_api.get_org_user_quota_usage(org_id, email) user_info['quota_usage'] = org_user_quota_usage / get_file_size_unit('MB') - return user_info def check_org_user(func): @@ -94,6 +91,32 @@ class AdminOrgUsers(APIView): throttle_classes = (UserRateThrottle,) permission_classes = (IsAdminUser, IsProVersion) + def get(self, request, org_id): + """ Get all users in an org. + + Permission checking: + 1. only admin can perform this action. + """ + # argument check + org_id = int(org_id) + if org_id == 0: + error_msg = 'org_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + org = ccnet_api.get_org_by_id(org_id) + if not org: + error_msg = 'Organization %d not found.' % org_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + result = [] + org_users = ccnet_api.get_org_emailusers(org.url_prefix, -1, -1) + for org_user in org_users: + user_info = get_org_user_info(org_id, org_user.email) + user_info['active'] = org_user.is_active + result.append(user_info) + + return Response({'users': result}) + def post(self, request, org_id): """ Add new user to org. @@ -121,6 +144,14 @@ class AdminOrgUsers(APIView): error_msg = 'password invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + active = request.POST.get('active', 'true') + active = active.lower() + if active not in ('true', 'false'): + error_msg = 'active invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + is_active = active == 'true' + try: User.objects.get(email=email) user_exists = True @@ -147,7 +178,8 @@ class AdminOrgUsers(APIView): # create user try: - User.objects.create_user(email, password, is_staff=False, is_active=True) + User.objects.create_user(email, password, is_staff=False, + is_active=is_active) except User.DoesNotExist as e: logger.error(e) error_msg = 'Fail to add user %s.' % email @@ -170,6 +202,7 @@ class AdminOrgUsers(APIView): UserOptions.objects.set_force_passwd_change(email) user_info = get_org_user_info(org_id, email) + user_info['active'] = is_active return Response(user_info) @@ -188,7 +221,24 @@ class AdminOrgUser(APIView): """ # argument check + org_id = int(org_id) + if org_id == 0: + error_msg = 'org_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + org = ccnet_api.get_org_by_id(org_id) + if not org: + error_msg = 'Organization %d not found.' % org_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + try: + user_obj = User.objects.get(email=email) + except User.DoesNotExist: + error_msg = 'User %s not found.' % email + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + user_info = get_org_user_info(org_id, email) + user_info['active'] = user_obj.is_active return Response(user_info) @check_org_user @@ -199,6 +249,12 @@ class AdminOrgUser(APIView): 1. only admin can perform this action. """ + try: + user = User.objects.get(email=email) + except User.DoesNotExist: + error_msg = 'User %s not found.' % email + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + # update active active = request.data.get('active', None) if active: @@ -207,7 +263,6 @@ class AdminOrgUser(APIView): error_msg = "active invalid, should be 'true' or 'false'." return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - user = User.objects.get(email=email) if active == 'true': user.is_active = True else: @@ -267,6 +322,7 @@ class AdminOrgUser(APIView): seafile_api.set_org_user_quota(org_id, email, user_quota) user_info = get_org_user_info(org_id, email) + user_info['active'] = user.is_active return Response(user_info) @check_org_user diff --git a/seahub/api2/endpoints/admin/organizations.py b/seahub/api2/endpoints/admin/organizations.py index fc3efdb663..43cd226559 100644 --- a/seahub/api2/endpoints/admin/organizations.py +++ b/seahub/api2/endpoints/admin/organizations.py @@ -9,8 +9,6 @@ from rest_framework import status from seaserv import ccnet_api, seafile_api -from seaserv import seafserv_threaded_rpc - from seahub.utils.file_size import get_file_size_unit from seahub.utils.timeutils import timestamp_to_isoformat_timestr from seahub.base.templatetags.seahub_tags import email2nickname, \ @@ -40,12 +38,11 @@ except ImportError: logger = logging.getLogger(__name__) -def get_org_info(org_id): +def get_org_info(org): + + org_id = org.org_id org_info = {} - - org = ccnet_api.get_org_by_id(org_id) - org_info['org_id'] = org_id org_info['org_name'] = org.org_name org_info['ctime'] = timestamp_to_isoformat_timestr(org.ctime) @@ -57,12 +54,44 @@ def get_org_info(org_id): org_info['creator_contact_email'] = email2contact_email(creator) org_info['quota'] = seafile_api.get_org_quota(org_id) + org_info['quota_usage'] = seafile_api.get_org_quota_usage(org_id) if ORG_MEMBER_QUOTA_ENABLED: org_info['max_user_number'] = OrgMemberQuota.objects.get_quota(org_id) return org_info +class AdminOrganizations(APIView): + + authentication_classes = (TokenAuthentication, SessionAuthentication) + permission_classes = (IsAdminUser, IsProVersion) + throttle_classes = (UserRateThrottle,) + + def get(self, request): + """ Get all organizations + + Permission checking: + 1. only admin can perform this action. + """ + + if not (CLOUD_MODE and MULTI_TENANCY): + error_msg = 'Feature is not enabled.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + try: + orgs = ccnet_api.get_all_orgs(-1, -1) + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + result = [] + for org in orgs: + org_info = get_org_info(org) + result.append(org_info) + + return Response({'organizations': result}) + class AdminOrganization(APIView): @@ -92,7 +121,7 @@ class AdminOrganization(APIView): return api_error(status.HTTP_404_NOT_FOUND, error_msg) try: - org_info = get_org_info(org_id) + org_info = get_org_info(org) except Exception as e: logger.error(e) error_msg = 'Internal Server Error' @@ -174,7 +203,7 @@ class AdminOrganization(APIView): error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) - org_info = get_org_info(org_id) + org_info = get_org_info(org) return Response(org_info) def delete(self, request, org_id): diff --git a/seahub/urls.py b/seahub/urls.py index 251b35f3f8..d50efbda57 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -104,7 +104,7 @@ from seahub.api2.endpoints.admin.upload_links import AdminUploadLink, \ AdminUploadLinkUpload, AdminUploadLinkCheckPassword from seahub.api2.endpoints.admin.users_batch import AdminUsersBatch from seahub.api2.endpoints.admin.operation_logs import AdminOperationLogs -from seahub.api2.endpoints.admin.organizations import AdminOrganization +from seahub.api2.endpoints.admin.organizations import AdminOrganizations, AdminOrganization from seahub.api2.endpoints.admin.org_users import AdminOrgUsers, AdminOrgUser from seahub.api2.endpoints.admin.org_stats import AdminOrgStatsTraffic from seahub.api2.endpoints.admin.logo import AdminLogo @@ -403,6 +403,7 @@ urlpatterns = [ url(r'^api/v2.1/admin/admin-role/$', AdminAdminRole.as_view(), name='api-v2.1-admin-admin-role'), ## admin::organizations + url(r'^api/v2.1/admin/organizations/$', AdminOrganizations.as_view(), name='api-v2.1-admin-organizations'), url(r'^api/v2.1/admin/organizations/(?P\d+)/$', AdminOrganization.as_view(), name='api-v2.1-admin-organization'), url(r'^api/v2.1/admin/organizations/(?P\d+)/users/$', AdminOrgUsers.as_view(), name='api-v2.1-admin-org-users'), url(r'^api/v2.1/admin/organizations/(?P\d+)/users/(?P[^/]+)/$', AdminOrgUser.as_view(), name='api-v2.1-admin-org-user'), diff --git a/tests/api/endpoints/admin/test_org_users.py b/tests/api/endpoints/admin/test_org_users.py index a562e689d6..a2516942ce 100644 --- a/tests/api/endpoints/admin/test_org_users.py +++ b/tests/api/endpoints/admin/test_org_users.py @@ -57,6 +57,30 @@ class OrgUsersTest(BaseTestCase): remove_org(self.org_id) self.remove_user(self.org_creator) + def test_can_get_users(self): + + if not LOCAL_PRO_DEV_ENV: + return + + self.login_as(self.admin) + resp = self.client.get(self.org_users_url) + json_resp = json.loads(resp.content) + self.assertEqual(200, resp.status_code) + + users = json_resp['organizaton_members'] + assert len(users) > 0 + assert users[0]['org_id'] == self.org_id + assert users[0]['email'] == self.org_creator + + def test_can_not_get_users_if_not_admin(self): + + if not LOCAL_PRO_DEV_ENV: + return + + self.login_as(self.user) + resp = self.client.get(self.org_users_url) + self.assertEqual(403, resp.status_code) + def test_can_create(self): if not LOCAL_PRO_DEV_ENV: diff --git a/tests/api/endpoints/admin/test_orgs.py b/tests/api/endpoints/admin/test_orgs.py new file mode 100644 index 0000000000..8275eb3ee1 --- /dev/null +++ b/tests/api/endpoints/admin/test_orgs.py @@ -0,0 +1,85 @@ +import json + +from seaserv import ccnet_api +from django.core.urlresolvers import reverse +from seahub.test_utils import BaseTestCase +from tests.common.utils import randstring + +from seaserv import seafserv_threaded_rpc + +try: + from seahub.settings import LOCAL_PRO_DEV_ENV +except ImportError: + LOCAL_PRO_DEV_ENV = False + +def remove_org(org_id): + org_id = int(org_id) + org = ccnet_api.get_org_by_id(org_id) + if org: + users =ccnet_api.get_org_emailusers(org.url_prefix, -1, -1) + for u in users: + ccnet_api.remove_org_user(org_id, u.email) + + groups = ccnet_api.get_org_groups(org.org_id, -1, -1) + for g in groups: + ccnet_api.remove_org_group(org_id, g.gid) + + # remove org repos + seafserv_threaded_rpc.remove_org_repo_by_org_id(org_id) + + # remove org + ccnet_api.remove_org(org_id) + +class OrgsTest(BaseTestCase): + + def setUp(self): + + self.user_name = self.user.username + self.admin_name = self.admin.username + + if LOCAL_PRO_DEV_ENV: + self.org_name = randstring(6) + self.org_url_prefix = randstring(6) + tmp_user = self.create_user(email='%s@%s.com' % (randstring(6), randstring(6))) + self.org_creator = tmp_user.username + self.org_id = ccnet_api.create_org(self.org_name, + self.org_url_prefix, self.org_creator) + self.orgs_url = reverse('api-v2.1-admin-organizations') + + def tearDown(self): + self.remove_group() + self.remove_repo() + + if LOCAL_PRO_DEV_ENV: + remove_org(self.org_id) + self.remove_user(self.org_creator) + + def test_can_get_orgs(self): + + if not LOCAL_PRO_DEV_ENV: + return + + self.login_as(self.admin) + resp = self.client.get(self.orgs_url) + json_resp = json.loads(resp.content) + self.assertEqual(200, resp.status_code) + + users = json_resp['organizations'] + assert len(users) > 0 + assert users[0].has_key('org_id') + assert users[0].has_key('org_name') + assert users[0].has_key('ctime') + assert users[0].has_key('org_url_prefix') + assert users[0].has_key('creator_email') + assert users[0].has_key('creator_name') + assert users[0].has_key('creator_contact_email') + assert users[0].has_key('quota') + + def test_can_not_get_orgs_if_not_admin(self): + + if not LOCAL_PRO_DEV_ENV: + return + + self.login_as(self.user) + resp = self.client.get(self.orgs_url) + self.assertEqual(403, resp.status_code) From e1b9e2750302709f4a09d1381522d054cdfe6078 Mon Sep 17 00:00:00 2001 From: lian Date: Wed, 26 Dec 2018 16:30:46 +0800 Subject: [PATCH 3/8] update water mark show shared_by in water mark when anonymous user view shared file --- seahub/templates/snippets/add_watermark.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/seahub/templates/snippets/add_watermark.html b/seahub/templates/snippets/add_watermark.html index ec38070262..e972b85efc 100644 --- a/seahub/templates/snippets/add_watermark.html +++ b/seahub/templates/snippets/add_watermark.html @@ -3,6 +3,9 @@ watermark.load({ {% if request.user.username %} watermark_txt: "{{site_name}} {{request.user.username|email2nickname|escapejs}}", + {# used when view shared file #} + {% elif shared_by %} + watermark_txt: "{{site_name}} {{shared_by|email2nickname|escapejs}}", {% else %} watermark_txt: "{% trans "Anonymous User" %}", {% endif %} From 38fcedbc2d6053a73498e37195144e5399798c3d Mon Sep 17 00:00:00 2001 From: lian Date: Sat, 29 Dec 2018 10:28:49 +0800 Subject: [PATCH 4/8] gettext('A file is being uploaded. Are you...') --- static/scripts/app/router.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/scripts/app/router.js b/static/scripts/app/router.js index 2baf222570..76ad2fcdf1 100644 --- a/static/scripts/app/router.js +++ b/static/scripts/app/router.js @@ -133,7 +133,7 @@ define([ if (this.currentView == this.dirView) { if ($('#upload-file-dialog').is(':visible') && $('#upload-file-dialog .status').text() == window.fileuploading) { - if (!window.confirm('A file is being uploaded. Are you sure you want to leave this page?')) { + if (!window.confirm(gettext('A file is being uploaded. Are you sure you want to leave this page?'))) { return false; } } From 7063fd03d4895c0d0f86ccd525703e0218b503e8 Mon Sep 17 00:00:00 2001 From: lian Date: Sat, 29 Dec 2018 11:59:04 +0800 Subject: [PATCH 5/8] Don't show form error when not ENABLE_UPDATE_USER_INFO --- seahub/profile/templates/profile/set_profile.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/seahub/profile/templates/profile/set_profile.html b/seahub/profile/templates/profile/set_profile.html index 495fb8dccc..54911746b6 100644 --- a/seahub/profile/templates/profile/set_profile.html +++ b/seahub/profile/templates/profile/set_profile.html @@ -49,9 +49,12 @@
    {% csrf_token %} + + {% if ENABLE_UPDATE_USER_INFO %} {% for error in form.nickname.errors %} {{ error|escape }} {% endfor %} + {% endif %}
    {% if form.data.login_id %} From fac93bfe82bf38b0518081d3a3448a9c5346ff1f Mon Sep 17 00:00:00 2001 From: lian Date: Sat, 29 Dec 2018 14:22:59 +0800 Subject: [PATCH 6/8] update admin org users api use bytes as unit when return org user quota usage --- seahub/api2/endpoints/admin/org_users.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/seahub/api2/endpoints/admin/org_users.py b/seahub/api2/endpoints/admin/org_users.py index af9b49e62a..b80aefd1f5 100644 --- a/seahub/api2/endpoints/admin/org_users.py +++ b/seahub/api2/endpoints/admin/org_users.py @@ -40,10 +40,11 @@ def get_org_user_info(org_id, email): user_info['contact_email'] = email2contact_email(email) org_user_quota = seafile_api.get_org_user_quota(org_id, email) - user_info['quota_total'] = org_user_quota / get_file_size_unit('MB') + user_info['quota_total'] = org_user_quota org_user_quota_usage = seafile_api.get_org_user_quota_usage(org_id, email) - user_info['quota_usage'] = org_user_quota_usage / get_file_size_unit('MB') + user_info['quota_usage'] = org_user_quota_usage + return user_info def check_org_user(func): From aa699dd40f23002962a95b85581400356f724d14 Mon Sep 17 00:00:00 2001 From: zhengxie Date: Wed, 2 Jan 2019 11:11:16 +0800 Subject: [PATCH 7/8] [password session] Disable for non-database users --- seahub/password_session/middleware.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/seahub/password_session/middleware.py b/seahub/password_session/middleware.py index 10893dc2f9..37cf7b0f7b 100644 --- a/seahub/password_session/middleware.py +++ b/seahub/password_session/middleware.py @@ -8,5 +8,9 @@ class CheckPasswordHash(object): """Logout user if value of hash key in session is not equal to current password hash""" def process_view(self, request, *args, **kwargs): if getattr(request.user, 'is_authenticated') and request.user.is_authenticated(): + if request.user.enc_password == '!': + # Disable for LDAP/Shibboleth/SAML/... users. + return None + if request.session.get(PASSWORD_HASH_KEY) != get_password_hash(request.user): - logout(request) \ No newline at end of file + logout(request) From 390e4963abc815bbb8f16153a1c9f5a25d95bb2c Mon Sep 17 00:00:00 2001 From: zhengxie Date: Wed, 2 Jan 2019 12:11:26 +0800 Subject: [PATCH 8/8] [two factor] Fix sms method for desktop/mobile client. --- seahub/api2/serializers.py | 6 ++++- seahub/two_factor/gateways/seaf_messenger.py | 26 ++++++++++++++++++++ seahub/two_factor/views/login.py | 11 ++++----- 3 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 seahub/two_factor/gateways/seaf_messenger.py diff --git a/seahub/api2/serializers.py b/seahub/api2/serializers.py index 99bfee3bf4..1378fb8e94 100644 --- a/seahub/api2/serializers.py +++ b/seahub/api2/serializers.py @@ -8,6 +8,7 @@ from seahub.auth import authenticate from seahub.api2.models import DESKTOP_PLATFORMS from seahub.api2.utils import get_token_v1, get_token_v2 from seahub.profile.models import Profile +from seahub.two_factor.models import default_device from seahub.two_factor.views.login import is_device_remembered from seahub.utils.two_factor_auth import has_two_factor_auth, \ two_factor_auth_enabled, verify_two_factor_token @@ -118,10 +119,13 @@ class AuthTokenSerializer(serializers.Serializer): token = request.META.get('HTTP_X_SEAFILE_OTP', '') if not token: + # Generate challenge(send sms/call/...) if token is not provided. + default_device(user).generate_challenge() + self.two_factor_auth_failed = True msg = 'Two factor auth token is missing.' raise serializers.ValidationError(msg) - if not verify_two_factor_token(user.username, token): + if not verify_two_factor_token(user, token): self.two_factor_auth_failed = True msg = 'Two factor auth token is invalid.' raise serializers.ValidationError(msg) diff --git a/seahub/two_factor/gateways/seaf_messenger.py b/seahub/two_factor/gateways/seaf_messenger.py new file mode 100644 index 0000000000..7f380259e4 --- /dev/null +++ b/seahub/two_factor/gateways/seaf_messenger.py @@ -0,0 +1,26 @@ +# Copyright (c) 2012-2016 Seafile Ltd. +import logging + +from django.conf import settings +import requests + + +logger = logging.getLogger(__name__) + + +class SeafMessenger(object): + @staticmethod + def make_call(device, token): + logger.info('Fake call to %s: "Your token is: %s"', device.number, token) + + @staticmethod + def send_sms(device, token): + api_token = settings.SEAF_MESSAGER_API_TOKEN + url = settings.SEAF_MESSAGER_SMS_API + + values = { + 'phone_num': device.number, + 'code': token, + } + requests.post(url, data=values, + headers={'Authorization': 'Token %s' % api_token}) diff --git a/seahub/two_factor/views/login.py b/seahub/two_factor/views/login.py index 52c17f1289..53c83532ac 100644 --- a/seahub/two_factor/views/login.py +++ b/seahub/two_factor/views/login.py @@ -215,14 +215,13 @@ def handle_two_factor_auth(request, user, redirect_to): request.session[SESSION_KEY_TWO_FACTOR_FAILED_ATTEMPT] = 0 return redirect(reverse('two_factor_auth')) -def verify_two_factor_token(username, token): +def verify_two_factor_token(user, token): """ - This function is called when doing the api authentication. We only support - totp here to simply the case. Backup token is not supported, because if the - user has the backup token, he can always login the website and re-setup the - totp. + This function is called when doing the api authentication. + Backup token is not supported, because if the user has the backup token, + he can always login the website and re-setup the totp. """ - device = TOTPDevice.objects.device_for_user(username) + device = default_device(user) if device: return device.verify_token(token)