diff --git a/frontend/src/pages/dtable/dtable.js b/frontend/src/pages/dtable/dtable.js index efa53754e2..0e626f0606 100644 --- a/frontend/src/pages/dtable/dtable.js +++ b/frontend/src/pages/dtable/dtable.js @@ -26,7 +26,6 @@ const tablePropTypes = { onUnfreezedItem: PropTypes.func.isRequired, onFreezedItem: PropTypes.func.isRequired, isItemFreezed: PropTypes.bool.isRequired, - isPersonal: PropTypes.bool.isRequired, }; class Table extends Component { @@ -135,9 +134,7 @@ class Table extends Component { {gettext('Rename')} {gettext('Delete')} - {this.props.isPersonal && {gettext('Share')} - } } @@ -258,7 +255,6 @@ class Workspace extends Component { onFreezedItem={this.onFreezedItem} onUnfreezedItem={this.onUnfreezedItem} isItemFreezed={isItemFreezed} - isPersonal={isPersonal} /> ); })} diff --git a/seahub/api2/endpoints/dtable.py b/seahub/api2/endpoints/dtable.py index ab4c5d17af..968fe98b57 100644 --- a/seahub/api2/endpoints/dtable.py +++ b/seahub/api2/endpoints/dtable.py @@ -112,20 +112,19 @@ class WorkspacesView(APIView): logger.warning('Library %s not found.' % repo_id) continue - if '@seafile_group' in owner: - group_id = int(owner.split('@')[0]) - owner_name = group_id_to_name(group_id) - owner_type = "Group" - else: - owner_name = email2nickname(owner) - owner_type = "Personal" + res = workspace.to_dict() table_list = DTables.objects.get_dtable_by_workspace(workspace) - - res = workspace.to_dict() - res["owner_name"] = owner_name - res["owner_type"] = owner_type res["table_list"] = table_list + + if '@seafile_group' in owner: + group_id = owner.split('@')[0] + res["owner_name"] = group_id_to_name(group_id) + res["owner_type"] = "Group" + else: + res["owner_name"] = email2nickname(owner) + res["owner_type"] = "Personal" + workspace_list.append(res) return Response({"workspace_list": workspace_list}, status=status.HTTP_200_OK) diff --git a/seahub/api2/endpoints/dtable_share.py b/seahub/api2/endpoints/dtable_share.py index aeac0ca50f..526d81f949 100644 --- a/seahub/api2/endpoints/dtable_share.py +++ b/seahub/api2/endpoints/dtable_share.py @@ -7,7 +7,7 @@ from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework import status from rest_framework.response import Response -from django.utils.translation import ugettext as _ +import seaserv from seaserv import seafile_api from seahub.base.accounts import User @@ -22,7 +22,7 @@ from seahub.utils import normalize_file_path from seahub.constants import PERMISSION_ADMIN, PERMISSION_PREVIEW, PERMISSION_PREVIEW_EDIT, \ PERMISSION_READ, PERMISSION_READ_WRITE from seahub.api2.endpoints.dtable import FILE_TYPE -from seahub.group.utils import group_id_to_name +from seahub.group.utils import group_id_to_name, is_group_member from seahub.utils.timeutils import datetime_to_isoformat_timestr logger = logging.getLogger(__name__) @@ -66,7 +66,12 @@ class SharedDTablesView(APIView): dtable_info['updated_at'] = datetime_to_isoformat_timestr(dtable.updated_at) dtable_info['permission'] = permission dtable_info['from_user'] = from_user - dtable_info['from_user_name'] = email2nickname(from_user) + + if '@seafile_group' in from_user: + group_id = from_user.split('@')[0] + dtable_info['from_user_name'] = group_id_to_name(group_id) + else: + dtable_info['from_user_name'] = email2nickname(from_user) table_list.append(dtable_info) @@ -108,6 +113,14 @@ class DTableShareView(APIView): error_msg = 'Workspace %s not found.' % workspace_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) + group_id = '' + if '@seafile_group' in workspace.owner: + group_id = workspace.owner.split('@')[0] + group = seaserv.get_group(group_id) + if not group: + error_msg = 'Group %s not found.' % group_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + repo_id = workspace.repo_id repo = seafile_api.get_repo(repo_id) if not repo: @@ -126,13 +139,24 @@ class DTableShareView(APIView): return api_error(status.HTTP_404_NOT_FOUND, error_msg) # permission check - if from_user != dtable.creator: - error_msg = 'Permission denied.' - return api_error(status.HTTP_403_FORBIDDEN, error_msg) + username = request.user.username + if group_id: + if not is_group_member(group_id, username): + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) - if to_user == from_user: - error_msg = 'table %s can not be shared to owner.' % table_name - return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + if is_group_member(group_id, to_user): + error_msg = 'table %s can not be shared to group member.' % table_name + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + from_user = group_id + GROUP_DOMAIN + else: + if from_user != dtable.creator: + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + if from_user == to_user: + error_msg = 'table %s can not be shared to owner.' % table_name + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) # org check if is_org_context(request): @@ -164,7 +188,7 @@ class DTableShareView(APIView): def get(self, request, workspace_id, name): """list share users in dtable share """ - from_user = request.user.username + username = request.user.username table_name = name table_file_name = table_name + FILE_TYPE @@ -174,6 +198,14 @@ class DTableShareView(APIView): error_msg = 'Workspace %s not found.' % workspace_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) + group_id = '' + if '@seafile_group' in workspace.owner: + group_id = workspace.owner.split('@')[0] + group = seaserv.get_group(group_id) + if not group: + error_msg = 'Group %s not found.' % group_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + repo_id = workspace.repo_id repo = seafile_api.get_repo(repo_id) if not repo: @@ -192,9 +224,14 @@ class DTableShareView(APIView): return api_error(status.HTTP_404_NOT_FOUND, error_msg) # permission check - if from_user != dtable.creator: - error_msg = 'Permission denied.' - return api_error(status.HTTP_403_FORBIDDEN, error_msg) + if group_id: + if not is_group_member(group_id, username): + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + else: + if username != dtable.creator: + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) # main try: @@ -215,7 +252,7 @@ class DTableShareView(APIView): def put(self, request, workspace_id, name): """modify dtable share permission """ - from_user = request.user.username + username = request.user.username table_name = name table_file_name = table_name + FILE_TYPE @@ -242,6 +279,14 @@ class DTableShareView(APIView): error_msg = 'Workspace %s not found.' % workspace_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) + group_id = '' + if '@seafile_group' in workspace.owner: + group_id = workspace.owner.split('@')[0] + group = seaserv.get_group(group_id) + if not group: + error_msg = 'Group %s not found.' % group_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + repo_id = workspace.repo_id repo = seafile_api.get_repo(repo_id) if not repo: @@ -260,13 +305,22 @@ class DTableShareView(APIView): return api_error(status.HTTP_404_NOT_FOUND, error_msg) # permission check - if from_user != dtable.creator: - error_msg = 'Permission denied.' - return api_error(status.HTTP_403_FORBIDDEN, error_msg) + if group_id: + if not is_group_member(group_id, username): + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) - if to_user == from_user: - error_msg = 'table %s can not be shared to owner.' % table_name - return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + if is_group_member(group_id, to_user): + error_msg = 'table %s can not be shared to group member.' % table_name + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + else: + if username != dtable.creator: + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + if username == to_user: + error_msg = 'table %s can not be shared to owner.' % table_name + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) # main try: @@ -312,6 +366,14 @@ class DTableShareView(APIView): error_msg = 'Workspace %s not found.' % workspace_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) + group_id = '' + if '@seafile_group' in workspace.owner: + group_id = workspace.owner.split('@')[0] + group = seaserv.get_group(group_id) + if not group: + error_msg = 'Group %s not found.' % group_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + repo_id = workspace.repo_id repo = seafile_api.get_repo(repo_id) if not repo: @@ -337,9 +399,14 @@ class DTableShareView(APIView): return api_error(status.HTTP_404_NOT_FOUND, error_msg) # permission check - if username not in (obj.to_user, obj.from_user): - error_msg = 'Permission denied.' - return api_error(status.HTTP_403_FORBIDDEN, error_msg) + if group_id: + if not is_group_member(group_id, username) and username != obj.to_user: + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + else: + if username not in (obj.to_user, obj.from_user): + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) obj.delete() except Exception as e: diff --git a/tests/api/endpoints/test_dtable_share.py b/tests/api/endpoints/test_dtable_share.py index 2e0d4fae59..21484c3d8c 100644 --- a/tests/api/endpoints/test_dtable_share.py +++ b/tests/api/endpoints/test_dtable_share.py @@ -1,5 +1,6 @@ import json +from django.utils.translation import ugettext as _ from django.core.urlresolvers import reverse from seahub.dtable.models import Workspaces, DTableShare, DTables from seaserv import seafile_api @@ -13,6 +14,8 @@ try: except ImportError: LOCAL_PRO_DEV_ENV = False +GROUP_DOMAIN = '@seafile_group' + class SharedDTablesViewTest(BaseTestCase): def setUp(self): @@ -80,15 +83,55 @@ class DTableShareViewTest(BaseTestCase): self.url = reverse('api-v2.1-dtable-share', args=[self.workspace.id, self.dtable.name]) + # add user to group + self.login_as(self.user) + self.group_id = self.group.id + self.endpoint = reverse('api-v2.1-group-members', args=[self.group_id]) + self.client.post(self.endpoint, { + 'email': self.user.email + }) + self.logout() + + # create group workspace + group_repo_id = seafile_api.create_repo( + _("My Workspace"), + _("My Workspace"), + "dtable@seafile" + ) + self.group_workspace = Workspaces.objects.create_workspace( + str(self.group_id) + GROUP_DOMAIN, group_repo_id + ) + assert len(Workspaces.objects.all()) == 2 + + # create group dtable + seafile_api.post_empty_file( + group_repo_id, '/', 'group.dtable', self.user.username + ) + self.group_dtable = DTables.objects.create_dtable( + self.user.username, self.group_workspace, 'group' + ) + assert len(DTables.objects.all()) == 2 + + # share group dtable to admin + DTableShare.objects.add( + self.group_dtable, str(self.group_id) + GROUP_DOMAIN, self.admin.username, 'rw' + ) + assert len(DTableShare.objects.all()) == 2 + + self.url2 = reverse( + 'api-v2.1-dtable-share', args=[self.group_workspace.id, self.group_dtable.name] + ) + def tearDown(self): workspace = Workspaces.objects.get_workspace_by_owner(self.user.username) workspace_id = workspace.id Workspaces.objects.delete_workspace(workspace_id) self.remove_repo() + self.remove_group() def test_can_post(self): - assert len(DTableShare.objects.all()) == 1 + assert len(DTableShare.objects.all()) == 2 DTableShare.objects.all().delete() assert len(DTableShare.objects.all()) == 0 @@ -102,9 +145,25 @@ class DTableShareViewTest(BaseTestCase): self.assertEqual(201, resp.status_code) assert len(DTableShare.objects.all()) == 1 - def test_can_not_post_with_already_share(self): + def test_can_post_group_share(self): + assert len(DTableShare.objects.all()) == 2 + DTableShare.objects.all().delete() + assert len(DTableShare.objects.all()) == 0 + + self.login_as(self.user) + + data = { + 'email': self.admin.username, + 'permission': 'rw', + } + + resp = self.client.post(self.url2, data) + self.assertEqual(201, resp.status_code) assert len(DTableShare.objects.all()) == 1 + def test_can_not_post_with_already_share(self): + assert len(DTableShare.objects.all()) == 2 + self.login_as(self.user) data = { @@ -115,7 +174,7 @@ class DTableShareViewTest(BaseTestCase): self.assertEqual(409, resp.status_code) def test_can_not_post_with_not_owner(self): - assert len(DTableShare.objects.all()) == 1 + assert len(DTableShare.objects.all()) == 2 DTableShare.objects.all().delete() assert len(DTableShare.objects.all()) == 0 @@ -129,7 +188,7 @@ class DTableShareViewTest(BaseTestCase): self.assertEqual(403, resp.status_code) def test_can_not_post_with_share_to_owner(self): - assert len(DTableShare.objects.all()) == 1 + assert len(DTableShare.objects.all()) == 2 DTableShare.objects.all().delete() assert len(DTableShare.objects.all()) == 0 @@ -142,11 +201,25 @@ class DTableShareViewTest(BaseTestCase): resp = self.client.post(self.url, data) self.assertEqual(400, resp.status_code) + def test_can_not_post_to_group_member(self): + assert len(DTableShare.objects.all()) == 2 + DTableShare.objects.all().delete() + assert len(DTableShare.objects.all()) == 0 + + self.login_as(self.user) + + data = { + 'email': self.user.username, + 'permission': 'rw', + } + resp = self.client.post(self.url2, data) + self.assertEqual(400, resp.status_code) + def test_can_not_post_with_share_to_org_user(self): if not LOCAL_PRO_DEV_ENV: return - assert len(DTableShare.objects.all()) == 1 + assert len(DTableShare.objects.all()) == 2 DTableShare.objects.all().delete() assert len(DTableShare.objects.all()) == 0 @@ -193,8 +266,22 @@ class DTableShareViewTest(BaseTestCase): assert DTableShare.objects.get_by_dtable_and_to_user( self.dtable, self.admin.username).permission == 'r' + def test_can_put_group_share(self): + self.login_as(self.user) + + data = { + 'email': self.admin.username, + 'permission': 'r', + } + resp = self.client.put(self.url2, json.dumps(data), 'application/json') + self.assertEqual(200, resp.status_code) + + assert DTableShare.objects.get_by_dtable_and_to_user( + self.group_dtable, self.admin.username + ).permission == 'r' + def test_can_not_put_with_not_shared(self): - assert len(DTableShare.objects.all()) == 1 + assert len(DTableShare.objects.all()) == 2 DTableShare.objects.all().delete() assert len(DTableShare.objects.all()) == 0 @@ -228,6 +315,7 @@ class DTableShareViewTest(BaseTestCase): self.assertEqual(400, resp.status_code) def test_can_delete(self): + assert len(DTableShare.objects.all()) == 2 self.login_as(self.user) data = { @@ -235,7 +323,18 @@ class DTableShareViewTest(BaseTestCase): } resp = self.client.delete(self.url, json.dumps(data), 'application/json') self.assertEqual(200, resp.status_code) - assert len(DTableShare.objects.all()) == 0 + assert len(DTableShare.objects.all()) == 1 + + def test_can_delete_group_share(self): + assert len(DTableShare.objects.all()) == 2 + self.login_as(self.user) + + data = { + 'email': self.admin.username, + } + resp = self.client.delete(self.url2, json.dumps(data), 'application/json') + self.assertEqual(200, resp.status_code) + assert len(DTableShare.objects.all()) == 1 def test_can_delete_with_share_user(self): self.login_as(self.admin) @@ -245,10 +344,10 @@ class DTableShareViewTest(BaseTestCase): } resp = self.client.delete(self.url, json.dumps(data), 'application/json') self.assertEqual(200, resp.status_code) - assert len(DTableShare.objects.all()) == 0 + assert len(DTableShare.objects.all()) == 1 def test_can_not_delete_with_not_shared(self): - assert len(DTableShare.objects.all()) == 1 + assert len(DTableShare.objects.all()) == 2 DTableShare.objects.all().delete() assert len(DTableShare.objects.all()) == 0