mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-02 15:38:15 +00:00
sysadmin reconstruct links backend api (#4149)
This commit is contained in:
@@ -28,6 +28,7 @@ from seahub.utils import gen_file_get_url, gen_dir_zip_download_url, \
|
|||||||
from seahub.utils.timeutils import timestamp_to_isoformat_timestr, \
|
from seahub.utils.timeutils import timestamp_to_isoformat_timestr, \
|
||||||
datetime_to_isoformat_timestr
|
datetime_to_isoformat_timestr
|
||||||
from seahub.views.file import send_file_access_msg
|
from seahub.views.file import send_file_access_msg
|
||||||
|
from seahub.wiki.models import Wiki
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -86,6 +87,53 @@ def get_share_link_info(fileshare):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class AdminShareLinks(APIView):
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAdminUser,)
|
||||||
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
""" Get all share links.
|
||||||
|
|
||||||
|
Permission checking:
|
||||||
|
1. only admin can perform this action.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
current_page = int(request.GET.get('page', '1'))
|
||||||
|
per_page = int(request.GET.get('per_page', '100'))
|
||||||
|
except ValueError:
|
||||||
|
current_page = 1
|
||||||
|
per_page = 100
|
||||||
|
|
||||||
|
start = (current_page - 1) * per_page
|
||||||
|
end = start + per_page
|
||||||
|
|
||||||
|
share_links = FileShare.objects.all().order_by('ctime')[start:end]
|
||||||
|
count = FileShare.objects.all().count()
|
||||||
|
|
||||||
|
# Use dict to reduce memcache fetch cost in large for-loop.
|
||||||
|
nickname_dict = {}
|
||||||
|
owner_email_set = set([link.username for link in share_links])
|
||||||
|
for e in owner_email_set:
|
||||||
|
if e not in nickname_dict:
|
||||||
|
nickname_dict[e] = email2nickname(e)
|
||||||
|
|
||||||
|
share_links_info = []
|
||||||
|
for link in share_links:
|
||||||
|
link_info = {}
|
||||||
|
link_info['obj_name'] = link.get_obj_name()
|
||||||
|
link_info['token'] = link.token
|
||||||
|
|
||||||
|
owner_email = link.username
|
||||||
|
link_info['creator_email'] = owner_email
|
||||||
|
link_info['creator_name'] = nickname_dict.get(owner_email, '')
|
||||||
|
link_info['ctime'] = datetime_to_isoformat_timestr(link.ctime)
|
||||||
|
link_info['view_cnt'] = link.view_cnt
|
||||||
|
share_links_info.append(link_info)
|
||||||
|
|
||||||
|
return Response({"share_link_list": share_links_info, "count": count})
|
||||||
|
|
||||||
|
|
||||||
class AdminShareLink(APIView):
|
class AdminShareLink(APIView):
|
||||||
|
|
||||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
@@ -115,12 +163,24 @@ class AdminShareLink(APIView):
|
|||||||
1. only admin can perform this action.
|
1. only admin can perform this action.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
fs = FileShare.objects.get(token=token)
|
share_link = FileShare.objects.get(token=token)
|
||||||
except FileShare.DoesNotExist:
|
except FileShare.DoesNotExist:
|
||||||
return Response({'success': True})
|
return Response({'success': True})
|
||||||
|
|
||||||
|
has_published_library = False
|
||||||
|
if share_link.path == '/':
|
||||||
|
try:
|
||||||
|
Wiki.objects.get(repo_id=share_link.repo_id)
|
||||||
|
has_published_library = True
|
||||||
|
except Wiki.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if has_published_library:
|
||||||
|
error_msg = 'There is an associated published library.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fs.delete()
|
share_link.delete()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
error_msg = 'Internal Server Error'
|
error_msg = 'Internal Server Error'
|
||||||
|
@@ -63,6 +63,52 @@ def get_upload_link_info(uls):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
class AdminUploadLinks(APIView):
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAdminUser,)
|
||||||
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
""" Get all upload links.
|
||||||
|
|
||||||
|
Permission checking:
|
||||||
|
1. only admin can perform this action.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
current_page = int(request.GET.get('page', '1'))
|
||||||
|
per_page = int(request.GET.get('per_page', '100'))
|
||||||
|
except ValueError:
|
||||||
|
current_page = 1
|
||||||
|
per_page = 100
|
||||||
|
|
||||||
|
start = (current_page - 1) * per_page
|
||||||
|
end = start + per_page
|
||||||
|
|
||||||
|
upload_links = UploadLinkShare.objects.all().order_by('ctime')[start:end]
|
||||||
|
count = UploadLinkShare.objects.all().count()
|
||||||
|
|
||||||
|
# Use dict to reduce memcache fetch cost in large for-loop.
|
||||||
|
nickname_dict = {}
|
||||||
|
owner_email_set = set([link.username for link in upload_links])
|
||||||
|
for e in owner_email_set:
|
||||||
|
if e not in nickname_dict:
|
||||||
|
nickname_dict[e] = email2nickname(e)
|
||||||
|
|
||||||
|
upload_links_info = []
|
||||||
|
for link in upload_links:
|
||||||
|
link_info = {}
|
||||||
|
link_info['path'] = link.path
|
||||||
|
link_info['token'] = link.token
|
||||||
|
|
||||||
|
owner_email = link.username
|
||||||
|
link_info['creator_email'] = owner_email
|
||||||
|
link_info['creator_name'] = nickname_dict.get(owner_email, '')
|
||||||
|
link_info['ctime'] = datetime_to_isoformat_timestr(link.ctime)
|
||||||
|
link_info['view_cnt'] = link.view_cnt
|
||||||
|
upload_links_info.append(link_info)
|
||||||
|
|
||||||
|
return Response({"upload_link_list": upload_links_info, "count": count})
|
||||||
|
|
||||||
|
|
||||||
class AdminUploadLink(APIView):
|
class AdminUploadLink(APIView):
|
||||||
|
|
||||||
@@ -93,12 +139,12 @@ class AdminUploadLink(APIView):
|
|||||||
1. only admin can perform this action.
|
1. only admin can perform this action.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
fs = UploadLinkShare.objects.get(token=token)
|
upload_link = UploadLinkShare.objects.get(token=token)
|
||||||
except UploadLinkShare.DoesNotExist:
|
except UploadLinkShare.DoesNotExist:
|
||||||
return Response({'success': True})
|
return Response({'success': True})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fs.delete()
|
upload_link.delete()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
error_msg = 'Internal Server Error'
|
error_msg = 'Internal Server Error'
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
import operator
|
import operator
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
@@ -366,6 +367,11 @@ class FileShare(models.Model):
|
|||||||
assert False
|
assert False
|
||||||
return perm_dict
|
return perm_dict
|
||||||
|
|
||||||
|
def get_obj_name(self):
|
||||||
|
if self.path:
|
||||||
|
return '/' if self.path == '/' else os.path.basename(self.path.rstrip('/'))
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
class OrgFileShareManager(models.Manager):
|
class OrgFileShareManager(models.Manager):
|
||||||
def set_org_file_share(self, org_id, file_share):
|
def set_org_file_share(self, org_id, file_share):
|
||||||
|
@@ -120,10 +120,10 @@ from seahub.api2.endpoints.admin.groups import AdminGroups, AdminGroup
|
|||||||
from seahub.api2.endpoints.admin.group_libraries import AdminGroupLibraries, AdminGroupLibrary
|
from seahub.api2.endpoints.admin.group_libraries import AdminGroupLibraries, AdminGroupLibrary
|
||||||
from seahub.api2.endpoints.admin.group_members import AdminGroupMembers, AdminGroupMember
|
from seahub.api2.endpoints.admin.group_members import AdminGroupMembers, AdminGroupMember
|
||||||
from seahub.api2.endpoints.admin.shares import AdminShares
|
from seahub.api2.endpoints.admin.shares import AdminShares
|
||||||
from seahub.api2.endpoints.admin.share_links import AdminShareLink, \
|
from seahub.api2.endpoints.admin.share_links import AdminShareLinks, AdminShareLink, \
|
||||||
AdminShareLinkDownload, AdminShareLinkCheckPassword, \
|
AdminShareLinkDownload, AdminShareLinkCheckPassword, \
|
||||||
AdminShareLinkDirents
|
AdminShareLinkDirents
|
||||||
from seahub.api2.endpoints.admin.upload_links import AdminUploadLink, \
|
from seahub.api2.endpoints.admin.upload_links import AdminUploadLinks, AdminUploadLink, \
|
||||||
AdminUploadLinkUpload, AdminUploadLinkCheckPassword
|
AdminUploadLinkUpload, AdminUploadLinkCheckPassword
|
||||||
from seahub.api2.endpoints.admin.users_batch import AdminUsersBatch, AdminAdminUsersBatch
|
from seahub.api2.endpoints.admin.users_batch import AdminUsersBatch, AdminAdminUsersBatch
|
||||||
from seahub.api2.endpoints.admin.operation_logs import AdminOperationLogs
|
from seahub.api2.endpoints.admin.operation_logs import AdminOperationLogs
|
||||||
@@ -501,6 +501,7 @@ urlpatterns = [
|
|||||||
url(r'^api/v2.1/admin/admin-login-logs/$', AdminLoginLogs.as_view(), name='api-v2.1-admin-admin-login-logs'),
|
url(r'^api/v2.1/admin/admin-login-logs/$', AdminLoginLogs.as_view(), name='api-v2.1-admin-admin-login-logs'),
|
||||||
|
|
||||||
## admin::share-links
|
## admin::share-links
|
||||||
|
url(r'^api/v2.1/admin/share-links/$', AdminShareLinks.as_view(), name='api-v2.1-admin-share-links'),
|
||||||
url(r'^api/v2.1/admin/share-links/(?P<token>[a-f0-9]+)/$', AdminShareLink.as_view(), name='api-v2.1-admin-share-link'),
|
url(r'^api/v2.1/admin/share-links/(?P<token>[a-f0-9]+)/$', AdminShareLink.as_view(), name='api-v2.1-admin-share-link'),
|
||||||
url(r'^api/v2.1/admin/share-links/(?P<token>[a-f0-9]+)/download/$',
|
url(r'^api/v2.1/admin/share-links/(?P<token>[a-f0-9]+)/download/$',
|
||||||
AdminShareLinkDownload.as_view(), name='api-v2.1-admin-share-link-download'),
|
AdminShareLinkDownload.as_view(), name='api-v2.1-admin-share-link-download'),
|
||||||
@@ -510,6 +511,7 @@ urlpatterns = [
|
|||||||
AdminShareLinkDirents.as_view(), name='api-v2.1-admin-share-link-dirents'),
|
AdminShareLinkDirents.as_view(), name='api-v2.1-admin-share-link-dirents'),
|
||||||
|
|
||||||
## admin::upload-links
|
## admin::upload-links
|
||||||
|
url(r'^api/v2.1/admin/upload-links/$', AdminUploadLinks.as_view(), name='api-v2.1-admin-upload-links'),
|
||||||
url(r'^api/v2.1/admin/upload-links/(?P<token>[a-f0-9]+)/$', AdminUploadLink.as_view(), name='api-v2.1-admin-upload-link'),
|
url(r'^api/v2.1/admin/upload-links/(?P<token>[a-f0-9]+)/$', AdminUploadLink.as_view(), name='api-v2.1-admin-upload-link'),
|
||||||
url(r'^api/v2.1/admin/upload-links/(?P<token>[a-f0-9]+)/upload/$',
|
url(r'^api/v2.1/admin/upload-links/(?P<token>[a-f0-9]+)/upload/$',
|
||||||
AdminUploadLinkUpload.as_view(), name='api-v2.1-admin-upload-link-upload'),
|
AdminUploadLinkUpload.as_view(), name='api-v2.1-admin-upload-link-upload'),
|
||||||
|
@@ -13,6 +13,54 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
LOCAL_PRO_DEV_ENV = False
|
LOCAL_PRO_DEV_ENV = False
|
||||||
|
|
||||||
|
class AdminShareLinksTest(BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.repo_id = self.repo.id
|
||||||
|
self.file_path= self.file
|
||||||
|
self.folder_path= self.folder
|
||||||
|
self.invalid_token = '00000000000000000000'
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_repo()
|
||||||
|
|
||||||
|
def _add_file_share_link(self, password=None):
|
||||||
|
fs = FileShare.objects.create_file_link(
|
||||||
|
self.user.username, self.repo.id, self.file, password, None)
|
||||||
|
|
||||||
|
return fs.token
|
||||||
|
|
||||||
|
def _add_dir_share_link(self, password=None):
|
||||||
|
fs = FileShare.objects.create_dir_link(
|
||||||
|
self.user.username, self.repo.id, self.folder, password, None)
|
||||||
|
|
||||||
|
return fs.token
|
||||||
|
|
||||||
|
def _remove_share_link(self, token):
|
||||||
|
link = FileShare.objects.get(token=token)
|
||||||
|
link.delete()
|
||||||
|
|
||||||
|
def test_get_share_links(self):
|
||||||
|
self.login_as(self.admin)
|
||||||
|
token1 = self._add_file_share_link()
|
||||||
|
token2 = self._add_dir_share_link()
|
||||||
|
|
||||||
|
url = reverse('api-v2.1-admin-share-links')
|
||||||
|
resp = self.client.get(url)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
self._remove_share_link(token1)
|
||||||
|
self._remove_share_link(token2)
|
||||||
|
|
||||||
|
def test_get_share_links_with_invalid_permission(self):
|
||||||
|
self.login_as(self.user)
|
||||||
|
token = self._add_file_share_link()
|
||||||
|
|
||||||
|
url = reverse('api-v2.1-admin-share-links')
|
||||||
|
resp = self.client.get(url)
|
||||||
|
self.assertEqual(403, resp.status_code)
|
||||||
|
|
||||||
|
self._remove_share_link(token)
|
||||||
|
|
||||||
class AdminShareLinkTest(BaseTestCase):
|
class AdminShareLinkTest(BaseTestCase):
|
||||||
|
|
||||||
@@ -90,6 +138,24 @@ class AdminShareLinkTest(BaseTestCase):
|
|||||||
resp = self.client.get(url)
|
resp = self.client.get(url)
|
||||||
self.assertEqual(404, resp.status_code)
|
self.assertEqual(404, resp.status_code)
|
||||||
|
|
||||||
|
def test_can_delete_share_link_by_token(self):
|
||||||
|
self.login_as(self.admin)
|
||||||
|
token = self._add_dir_share_link()
|
||||||
|
|
||||||
|
url = reverse('api-v2.1-admin-share-link', args=[token])
|
||||||
|
resp = self.client.delete(url)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
def test_delete_share_link_with_invalid_permission(self):
|
||||||
|
self.login_as(self.user)
|
||||||
|
token = self._add_dir_share_link()
|
||||||
|
|
||||||
|
url = reverse('api-v2.1-admin-share-link', args=[token])
|
||||||
|
resp = self.client.delete(url)
|
||||||
|
self.assertEqual(403, resp.status_code)
|
||||||
|
|
||||||
|
self._remove_share_link(token)
|
||||||
|
|
||||||
|
|
||||||
class AdminShareLinkDirentsTest(BaseTestCase):
|
class AdminShareLinkDirentsTest(BaseTestCase):
|
||||||
|
|
||||||
|
@@ -11,6 +11,47 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
LOCAL_PRO_DEV_ENV = False
|
LOCAL_PRO_DEV_ENV = False
|
||||||
|
|
||||||
|
|
||||||
|
class AdminUploadLinksTest(BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.repo_id = self.repo.id
|
||||||
|
self.folder_path= self.folder
|
||||||
|
self.invalid_token = '00000000000000000000'
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_repo()
|
||||||
|
|
||||||
|
def _add_upload_link(self, password=None):
|
||||||
|
fs = UploadLinkShare.objects.create_upload_link_share(
|
||||||
|
self.user.username, self.repo.id, self.folder_path, password, None)
|
||||||
|
|
||||||
|
return fs.token
|
||||||
|
|
||||||
|
def _remove_upload_link(self, token):
|
||||||
|
link = UploadLinkShare.objects.get(token=token)
|
||||||
|
link.delete()
|
||||||
|
|
||||||
|
def test_get_share_links(self):
|
||||||
|
self.login_as(self.admin)
|
||||||
|
token = self._add_upload_link()
|
||||||
|
|
||||||
|
url = reverse('api-v2.1-admin-upload-links')
|
||||||
|
resp = self.client.get(url)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
self._remove_upload_link(token)
|
||||||
|
|
||||||
|
def test_get_share_links_with_invalid_permission(self):
|
||||||
|
self.login_as(self.user)
|
||||||
|
token = self._add_upload_link()
|
||||||
|
|
||||||
|
url = reverse('api-v2.1-admin-upload-links')
|
||||||
|
resp = self.client.get(url)
|
||||||
|
self.assertEqual(403, resp.status_code)
|
||||||
|
|
||||||
|
self._remove_upload_link(token)
|
||||||
|
|
||||||
class AdminUploadLinkTest(BaseTestCase):
|
class AdminUploadLinkTest(BaseTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -63,6 +104,24 @@ class AdminUploadLinkTest(BaseTestCase):
|
|||||||
resp = self.client.get(url)
|
resp = self.client.get(url)
|
||||||
self.assertEqual(404, resp.status_code)
|
self.assertEqual(404, resp.status_code)
|
||||||
|
|
||||||
|
def test_can_delete_upload_link_by_token(self):
|
||||||
|
self.login_as(self.admin)
|
||||||
|
token = self._add_upload_link()
|
||||||
|
|
||||||
|
url = reverse('api-v2.1-admin-upload-link', args=[token])
|
||||||
|
resp = self.client.delete(url)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
def test_delete_upload_link_with_invalid_permission(self):
|
||||||
|
self.login_as(self.user)
|
||||||
|
token = self._add_upload_link()
|
||||||
|
|
||||||
|
url = reverse('api-v2.1-admin-upload-link', args=[token])
|
||||||
|
resp = self.client.delete(url)
|
||||||
|
self.assertEqual(403, resp.status_code)
|
||||||
|
|
||||||
|
self._remove_upload_link(token)
|
||||||
|
|
||||||
|
|
||||||
class AdminUploadLinkUploadTest(BaseTestCase):
|
class AdminUploadLinkUploadTest(BaseTestCase):
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user