diff --git a/seahub/api2/endpoints/admin/upload_links.py b/seahub/api2/endpoints/admin/upload_links.py index 33f34b562d..4eb2b2bef4 100644 --- a/seahub/api2/endpoints/admin/upload_links.py +++ b/seahub/api2/endpoints/admin/upload_links.py @@ -94,7 +94,7 @@ class AdminUploadLinkUpload(APIView): throttle_classes = (UserRateThrottle,) def get(self, request, token): - """ Get FileServer url of the shared file. + """ Get FileServer url of the shared folder. Permission checking: 1. only admin can perform this action. diff --git a/seahub/api2/endpoints/upload_links.py b/seahub/api2/endpoints/upload_links.py index e2c68aaa6e..e5dc2116e2 100644 --- a/seahub/api2/endpoints/upload_links.py +++ b/seahub/api2/endpoints/upload_links.py @@ -16,56 +16,57 @@ from pysearpc import SearpcError from seahub.api2.utils import api_error from seahub.api2.authentication import TokenAuthentication -from seahub.api2.throttling import UserRateThrottle +from seahub.api2.throttling import AnonRateThrottle, UserRateThrottle from seahub.api2.permissions import CanGenerateUploadLink from seahub.share.models import UploadLinkShare -from seahub.utils import gen_shared_upload_link +from seahub.utils import gen_shared_upload_link, gen_file_upload_url from seahub.views import check_folder_permission from seahub.utils.timeutils import datetime_to_isoformat_timestr logger = logging.getLogger(__name__) +def get_upload_link_info(uls): + data = {} + token = uls.token + + repo_id = uls.repo_id + try: + repo = seafile_api.get_repo(repo_id) + except Exception as e: + logger.error(e) + repo = None + + path = uls.path + if path: + obj_name = '/' if path == '/' else os.path.basename(path.rstrip('/')) + else: + obj_name = '' + + if uls.ctime: + ctime = datetime_to_isoformat_timestr(uls.ctime) + else: + ctime = '' + + data['repo_id'] = repo_id + data['repo_name'] = repo.repo_name if repo else '' + data['path'] = path + data['obj_name'] = obj_name + data['view_cnt'] = uls.view_cnt + data['ctime'] = ctime + data['link'] = gen_shared_upload_link(token) + data['token'] = token + data['username'] = uls.username + + return data + + class UploadLinks(APIView): authentication_classes = (TokenAuthentication, SessionAuthentication) permission_classes = (IsAuthenticated, CanGenerateUploadLink) throttle_classes = (UserRateThrottle, ) - def _get_upload_link_info(self, uls): - data = {} - token = uls.token - - repo_id = uls.repo_id - try: - repo = seafile_api.get_repo(repo_id) - except Exception as e: - logger.error(e) - repo = None - - path = uls.path - if path: - obj_name = '/' if path == '/' else os.path.basename(path.rstrip('/')) - else: - obj_name = '' - - if uls.ctime: - ctime = datetime_to_isoformat_timestr(uls.ctime) - else: - ctime = '' - - data['repo_id'] = repo_id - data['repo_name'] = repo.repo_name if repo else '' - data['path'] = path - data['obj_name'] = obj_name - data['view_cnt'] = uls.view_cnt - data['ctime'] = ctime - data['link'] = gen_shared_upload_link(token) - data['token'] = token - data['username'] = uls.username - - return data - def get(self, request): """ Get all upload links of a user. @@ -108,7 +109,7 @@ class UploadLinks(APIView): result = [] for uls in upload_link_shares: - link_info = self._get_upload_link_info(uls) + link_info = get_upload_link_info(uls) result.append(link_info) if len(result) == 1: @@ -169,7 +170,7 @@ class UploadLinks(APIView): uls = UploadLinkShare.objects.create_upload_link_share(username, repo_id, path, password) - link_info = self._get_upload_link_info(uls) + link_info = get_upload_link_info(uls) return Response(link_info) class UploadLink(APIView): @@ -191,7 +192,7 @@ class UploadLink(APIView): error_msg = 'token %s not found.' % token return api_error(status.HTTP_404_NOT_FOUND, error_msg) - link_info = self._get_upload_link_info(uls) + link_info = get_upload_link_info(uls) return Response(link_info) def delete(self, request, token): @@ -220,3 +221,49 @@ class UploadLink(APIView): return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) return Response({'success': True}) + + +class UploadLinkUpload(APIView): + + throttle_classes = (AnonRateThrottle, ) + + def get(self, request, token): + """ Get file upload url according to upload link token. + + Permission checking: + 1. anyone has the upload link token can perform this action; + """ + + try: + uls = UploadLinkShare.objects.get(token=token) + except UploadLinkShare.DoesNotExist: + error_msg = 'token %s not found.' % token + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + # currently not support encrypted upload link + if uls.is_encrypted(): + error_msg = 'Upload link %s is encrypted.' % token + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + repo_id = uls.repo_id + repo = seafile_api.get_repo(repo_id) + if not repo: + error_msg = 'Library %s not found.' % repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + path = uls.path + dir_id = seafile_api.get_dir_id_by_path(repo_id, path) + if not dir_id: + error_msg = 'Folder %s not found.' % path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + token = seafile_api.get_fileserver_access_token(repo_id, + dir_id, 'upload', uls.username, use_onetime=False) + + if not token: + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + result = {} + result['upload_link'] = gen_file_upload_url(token, 'upload-api') + return Response(result) diff --git a/seahub/urls.py b/seahub/urls.py index 881de0d913..f2bc10e818 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -27,7 +27,8 @@ from seahub.api2.endpoints.search_group import SearchGroup from seahub.api2.endpoints.share_links import ShareLinks, ShareLink from seahub.api2.endpoints.shared_folders import SharedFolders from seahub.api2.endpoints.shared_repos import SharedRepos, SharedRepo -from seahub.api2.endpoints.upload_links import UploadLinks, UploadLink +from seahub.api2.endpoints.upload_links import UploadLinks, UploadLink, \ + UploadLinkUpload from seahub.api2.endpoints.repos_batch import ReposBatchView, \ ReposBatchCopyDirView, ReposBatchCreateDirView from seahub.api2.endpoints.repos import RepoView @@ -237,6 +238,7 @@ urlpatterns = patterns( ## user::shared-upload-links url(r'^api/v2.1/upload-links/$', UploadLinks.as_view(), name='api-v2.1-upload-links'), url(r'^api/v2.1/upload-links/(?P[a-f0-9]+)/$', UploadLink.as_view(), name='api-v2.1-upload-link'), + url(r'^api/v2.1/upload-links/(?P[a-f0-9]+)/upload/$', UploadLinkUpload.as_view(), name='api-v2.1-upload-link-upload'), ## user::revision-tags url(r'^api/v2.1/revision-tags/tagged-items/$', TaggedItemsView.as_view(), name='api-v2.1-revision-tags-tagged-items'), diff --git a/tests/api/endpoints/test_upload_links.py b/tests/api/endpoints/test_upload_links.py index c8d15777df..322ed9bc7a 100644 --- a/tests/api/endpoints/test_upload_links.py +++ b/tests/api/endpoints/test_upload_links.py @@ -3,6 +3,9 @@ import json from mock import patch from django.core.urlresolvers import reverse + +from tests.common.utils import upload_file_test, randstring + from seahub.test_utils import BaseTestCase from seahub.share.models import UploadLinkShare from seahub.api2.permissions import CanGenerateUploadLink @@ -163,3 +166,56 @@ class UploadLinksTest(BaseTestCase): url = reverse('api-v2.1-upload-link', args=[token]) resp = self.client.delete(url, {}, 'application/x-www-form-urlencoded') self.assertEqual(403, resp.status_code) + + +class UploadLinkUploadTest(BaseTestCase): + + def setUp(self): + + self.repo_id = self.repo.id + self.folder_path= self.folder + self.invalid_token = '00000000000000000000' + + 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_upload_link(self): + + token = self._add_upload_link() + + url = reverse('api-v2.1-upload-link-upload', args=[token]) + resp = self.client.get(url) + + self.assertEqual(200, resp.status_code) + json_resp = json.loads(resp.content) + assert '8082' in json_resp['upload_link'] + assert 'upload' in json_resp['upload_link'] + + # test upload file via `upload_link` + upload_file_test(json_resp['upload_link']) + + self._remove_upload_link(token) + + def test_can_not_get_upload_link_with_invalid_token(self): + + url = reverse('api-v2.1-upload-link-upload', + args=[self.invalid_token]) + + resp = self.client.get(url) + self.assertEqual(404, resp.status_code) + + def test_can_not_get_upload_link_for_encrypted_upload_link_share(self): + + token = self._add_upload_link(password=randstring(10)) + url = reverse('api-v2.1-upload-link-upload', args=[token]) + resp = self.client.get(url) + self.assertEqual(403, resp.status_code)