From 9ded919a2ff65839152ff3d73f1efc53126bb3f0 Mon Sep 17 00:00:00 2001 From: lian Date: Thu, 30 Sep 2021 14:41:02 +0800 Subject: [PATCH 01/11] use allowed_hosts argument for is_safe_url func --- seahub/auth/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seahub/auth/views.py b/seahub/auth/views.py index d946b60be6..e2cd724edf 100644 --- a/seahub/auth/views.py +++ b/seahub/auth/views.py @@ -234,7 +234,7 @@ def login_simple_check(request): # Ensure the user-originating redirection url is safe. if REDIRECT_FIELD_NAME in request.GET: next_page = request.GET[REDIRECT_FIELD_NAME] - if not is_safe_url(url=next_page, host=request.get_host()): + if not is_safe_url(url=next_page, allowed_hosts=request.get_host()): next_page = settings.LOGIN_REDIRECT_URL else: next_page = settings.SITE_ROOT From 2a6b89082f0f5ff52e4518e3ef000606cb488f5c Mon Sep 17 00:00:00 2001 From: lian Date: Wed, 13 Oct 2021 15:11:51 +0800 Subject: [PATCH 02/11] add admin generate user auth token api --- .../admin/generate_user_auth_token.py | 54 +++++++++++++++++++ seahub/settings.py | 11 ++-- seahub/urls.py | 4 ++ 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 seahub/api2/endpoints/admin/generate_user_auth_token.py diff --git a/seahub/api2/endpoints/admin/generate_user_auth_token.py b/seahub/api2/endpoints/admin/generate_user_auth_token.py new file mode 100644 index 0000000000..e239326aa0 --- /dev/null +++ b/seahub/api2/endpoints/admin/generate_user_auth_token.py @@ -0,0 +1,54 @@ +# Copyright (c) 2012-2016 Seafile Ltd. +import logging + +from rest_framework.authentication import SessionAuthentication +from rest_framework.permissions import IsAdminUser +from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework import status + + +from seahub.api2.authentication import TokenAuthentication +from seahub.api2.throttling import UserRateThrottle +from seahub.api2.utils import api_error, get_token_v1 + +from seahub.base.accounts import User + +logger = logging.getLogger(__name__) + + +class AdminGenerateUserAuthToken(APIView): + + authentication_classes = (TokenAuthentication, SessionAuthentication) + throttle_classes = (UserRateThrottle,) + permission_classes = (IsAdminUser,) + + def post(self, request): + + email = request.data.get('email') + if not email: + error_msg = 'email invalid.' + 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_404_NOT_FOUND, error_msg) + + if not user.is_active: + error_msg = 'User %s is not active.' % email + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + try: + token = get_token_v1(email) + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + result = { + 'token': token.key + } + + return Response(result) diff --git a/seahub/settings.py b/seahub/settings.py index d935e3ef6a..aefdaa09bf 100644 --- a/seahub/settings.py +++ b/seahub/settings.py @@ -22,8 +22,8 @@ MANAGERS = ADMINS DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': '%s/seahub/seahub.db' % PROJECT_ROOT, # Or path to database file if using sqlite3. + 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. + 'NAME': '%s/seahub/seahub.db' % PROJECT_ROOT, # Or path to database file if using sqlite3. 'USER': '', # Not used with sqlite3. 'PASSWORD': '', # Not used with sqlite3. 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. @@ -300,10 +300,13 @@ LOGOUT_REDIRECT_URL = None ACCOUNT_ACTIVATION_DAYS = 7 -# allow seafile amdin view user's repo +# allow seafile admin view user's repo ENABLE_SYS_ADMIN_VIEW_REPO = False -#allow search from LDAP directly during auto-completion (not only search imported users) +# allow seafile admin generate user auth token +ENABLE_SYS_ADMIN_GENERATE_USER_AUTH_TOKEN = False + +# allow search from LDAP directly during auto-completion (not only search imported users) ENABLE_SEARCH_FROM_LDAP_DIRECTLY = False # show traffic on the UI diff --git a/seahub/urls.py b/seahub/urls.py index 8ab11913a7..ac687a49a1 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -118,6 +118,7 @@ from seahub.api2.endpoints.admin.file_audit import FileAudit from seahub.api2.endpoints.admin.file_update import FileUpdate from seahub.api2.endpoints.admin.perm_audit import PermAudit from seahub.api2.endpoints.admin.sysinfo import SysInfo +from seahub.api2.endpoints.admin.generate_user_auth_token import AdminGenerateUserAuthToken from seahub.api2.endpoints.admin.web_settings import AdminWebSettings from seahub.api2.endpoints.admin.statistics import ( FileOperationsView, TotalStorageView, ActiveUsersView, SystemTrafficView, \ @@ -500,6 +501,9 @@ urlpatterns = [ url(r'^api/v2.1/admin/abuse-reports/(?P\d+)/$', AdminAbuseReportView.as_view(), name='api-v2.1-admin-abuse-report'), + ## admin::generate user auth token + url(r'^api/v2.1/admin/generate-user-auth-token/$', AdminGenerateUserAuthToken.as_view(), name='api-v2.1-admin-generate-user-auth-token'), + ## admin::sysinfo url(r'^api/v2.1/admin/sysinfo/$', SysInfo.as_view(), name='api-v2.1-sysinfo'), From 23dc12f6b0e09e0b67a5af30dad95b4d49545331 Mon Sep 17 00:00:00 2001 From: lian Date: Thu, 14 Oct 2021 14:55:23 +0800 Subject: [PATCH 03/11] search group member --- .../dialog/manage-members-dialog.js | 80 ++++++++++++++----- frontend/src/css/manage-members-dialog.css | 11 ++- seahub/api2/endpoints/group_members.py | 34 +++++++- seahub/urls.py | 3 +- 4 files changed, 102 insertions(+), 26 deletions(-) diff --git a/frontend/src/components/dialog/manage-members-dialog.js b/frontend/src/components/dialog/manage-members-dialog.js index 3885013b3d..0bbddd761a 100644 --- a/frontend/src/components/dialog/manage-members-dialog.js +++ b/frontend/src/components/dialog/manage-members-dialog.js @@ -1,6 +1,6 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; -import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Table } from 'reactstrap'; +import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Table, Input, Label, FormGroup } from 'reactstrap'; import { Utils } from '../../utils/utils'; import { gettext } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; @@ -31,7 +31,8 @@ class ManageMembersDialog extends React.Component { hasNextPage: false, selectedOption: null, errMessage: [], - isItemFreezed: false + isItemFreezed: false, + searchGroupMemberInputValue: '', }; } @@ -49,7 +50,7 @@ class ManageMembersDialog extends React.Component { isLoadingMore: false, page: page, hasNextPage: members.length < perPage ? false : true, - groupMembers: groupMembers.concat(members) + groupMembers: groupMembers.concat(members) }); }).catch(error => { let errMessage = Utils.getErrorMsg(error); @@ -92,6 +93,24 @@ class ManageMembersDialog extends React.Component { }); } + handleSearchGroupMemberInputChange = (e) => { + this.setState({ + searchGroupMemberInputValue: e.target.value + }); + } + + searchGroupMember = () => { + + seafileAPI.searchGroupMember(this.props.groupID, this.state.searchGroupMemberInputValue).then((res) => { + this.setState({ + groupMembers: res.data, + }); + }).catch(error => { + let errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + }); + } + toggleItemFreezed = (isFreezed) => { this.setState({ isItemFreezed: isFreezed @@ -112,10 +131,10 @@ class ManageMembersDialog extends React.Component { const isBottom = (clientHeight + scrollTop + 1 >= scrollHeight); if (isBottom) { // scroll to the bottom this.setState({isLoadingMore: true}, () => { - this.listGroupMembers(page + 1); - }); - } - } + this.listGroupMembers(page + 1); + }); + } + } } changeMember = (targetMember) => { @@ -143,20 +162,39 @@ class ManageMembersDialog extends React.Component { {gettext('Manage group members')} -

{gettext('Add group member')}

-
- - {this.state.selectedOption ? - : - - } -
+ +

{gettext('Add group member')}

+
+ + {this.state.selectedOption ? + : + + } +
+
+ +

{gettext('Search group member')}

+
+ + {this.state.searchGroupMemberInputValue ? + : + + } +
+
{ this.state.errMessage.length > 0 && this.state.errMessage.map((item, index = 0) => { diff --git a/frontend/src/css/manage-members-dialog.css b/frontend/src/css/manage-members-dialog.css index 265f31bdd2..8e553c1722 100644 --- a/frontend/src/css/manage-members-dialog.css +++ b/frontend/src/css/manage-members-dialog.css @@ -33,17 +33,22 @@ display: none; } -.add-members { +.add-members, +.search-members { display: flex; justify-content: space-between; } -.add-members .add-members-select { +.add-members .add-members-select, +.search-members .search-members-input { width: 385px; } -.add-members .btn { + +.add-members .btn, +.search-members .btn { width: 75px; } + .group-error { margin-top: 10px; } \ No newline at end of file diff --git a/seahub/api2/endpoints/group_members.py b/seahub/api2/endpoints/group_members.py index 725a1fe4d5..b5e0726a30 100644 --- a/seahub/api2/endpoints/group_members.py +++ b/seahub/api2/endpoints/group_members.py @@ -133,10 +133,42 @@ class GroupMembers(APIView): return Response(member_info, status=status.HTTP_201_CREATED) +class GroupSearchMember(APIView): + + authentication_classes = (TokenAuthentication, SessionAuthentication) + permission_classes = (IsAuthenticated,) + throttle_classes = (UserRateThrottle,) + + @api_check_group + def get(self, request, group_id, format=None): + """ + Search group member by email. + """ + + q = request.GET.get('q', '') + if not q: + error_msg = 'q invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not is_group_member(group_id, request.user.username): + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + group_members = [] + members = ccnet_api.search_group_members(group_id, q) + for member in members: + + member_info = get_group_member_info(request, group_id, member.user_name) + + group_members.append(member_info) + + return Response(group_members) + + class GroupMember(APIView): authentication_classes = (TokenAuthentication, SessionAuthentication) permission_classes = (IsAuthenticated,) - throttle_classes = (UserRateThrottle, ) + throttle_classes = (UserRateThrottle,) @api_check_group def get(self, request, group_id, email): diff --git a/seahub/urls.py b/seahub/urls.py index 8ab11913a7..d9eeaa2f5f 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -36,7 +36,7 @@ from seahub.api2.endpoints.group_owned_libraries import GroupOwnedLibraries, \ from seahub.api2.endpoints.address_book.groups import AddressBookGroupsSubGroups from seahub.api2.endpoints.address_book.members import AddressBookGroupsSearchMember -from seahub.api2.endpoints.group_members import GroupMembers, GroupMember, \ +from seahub.api2.endpoints.group_members import GroupMembers, GroupSearchMember, GroupMember, \ GroupMembersBulk, GroupMembersImport, GroupMembersImportExample from seahub.api2.endpoints.search_group import SearchGroup from seahub.api2.endpoints.share_links import ShareLinks, ShareLink, \ @@ -307,6 +307,7 @@ urlpatterns = [ url(r'^api/v2.1/groups/(?P\d+)/group-owned-libraries/$', GroupOwnedLibraries.as_view(), name='api-v2.1-group-owned-libraries'), url(r'^api/v2.1/groups/(?P\d+)/group-owned-libraries/(?P[-0-9a-f]{36})/$', GroupOwnedLibrary.as_view(), name='api-v2.1-owned-group-library'), url(r'^api/v2.1/groups/(?P\d+)/members/$', GroupMembers.as_view(), name='api-v2.1-group-members'), + url(r'^api/v2.1/groups/(?P\d+)/search-member/$', GroupSearchMember.as_view(), name='api-v2.1-group-search-member'), url(r'^api/v2.1/groups/(?P\d+)/members/bulk/$', GroupMembersBulk.as_view(), name='api-v2.1-group-members-bulk'), url(r'^api/v2.1/groups/(?P\d+)/members/import/$', GroupMembersImport.as_view(), name='api-v2.1-group-members-import'), url(r'^api/v2.1/group-members-import-example/$', GroupMembersImportExample.as_view(), name='api-v2.1-group-members-import-example'), From 63697c57939f895921a90c495ade4f63e40078b0 Mon Sep 17 00:00:00 2001 From: lian Date: Thu, 14 Oct 2021 14:58:27 +0800 Subject: [PATCH 04/11] update --- frontend/src/components/dialog/manage-members-dialog.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/components/dialog/manage-members-dialog.js b/frontend/src/components/dialog/manage-members-dialog.js index 0bbddd761a..f1b35f23f7 100644 --- a/frontend/src/components/dialog/manage-members-dialog.js +++ b/frontend/src/components/dialog/manage-members-dialog.js @@ -100,7 +100,6 @@ class ManageMembersDialog extends React.Component { } searchGroupMember = () => { - seafileAPI.searchGroupMember(this.props.groupID, this.state.searchGroupMemberInputValue).then((res) => { this.setState({ groupMembers: res.data, From 9842a95d0c3e63479b9b0b8edd16beb194e0c1a6 Mon Sep 17 00:00:00 2001 From: lian Date: Mon, 18 Oct 2021 13:54:33 +0800 Subject: [PATCH 05/11] remove escapejs when show title on wiki page --- seahub/templates/wiki/wiki.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seahub/templates/wiki/wiki.html b/seahub/templates/wiki/wiki.html index c164e2fbce..12592bfe99 100644 --- a/seahub/templates/wiki/wiki.html +++ b/seahub/templates/wiki/wiki.html @@ -4,7 +4,7 @@ {% render_bundle 'wiki' 'css' %} {% endblock %} -{% block sub_title %} {{filename|escapejs}} - {% endblock %} +{% block sub_title %} {{filename}} - {% endblock %} {% block extra_script %}