1# Copyright (c) 2012-2016 Seafile Ltd.
2import logging
3
4from rest_framework.authentication import SessionAuthentication
5from rest_framework.permissions import IsAuthenticated
6from rest_framework.response import Response
7from rest_framework.views import APIView
8from rest_framework import status
9
10import seaserv
11from seaserv import seafile_api, ccnet_api
12
13from seahub.api2.utils import api_error
14from seahub.api2.authentication import TokenAuthentication
15from seahub.api2.throttling import UserRateThrottle
16from seahub.profile.models import Profile
17from seahub.utils import is_org_context, is_valid_username, send_perm_audit_msg
18from seahub.utils.repo import get_available_repo_perms
19from seahub.base.templatetags.seahub_tags import email2nickname, email2contact_email
20from seahub.share.models import ExtraSharePermission, ExtraGroupsSharePermission
21from seahub.share.utils import update_user_dir_permission, update_group_dir_permission,\
22        check_user_share_out_permission, check_group_share_out_permission
23
24logger = logging.getLogger(__name__)
25
26class SharedRepos(APIView):
27
28    authentication_classes = (TokenAuthentication, SessionAuthentication)
29    permission_classes = (IsAuthenticated,)
30    throttle_classes = (UserRateThrottle,)
31
32    def get(self, request, format=None):
33        """ List all shared out repos.
34
35        Permission checking:
36        1. all authenticated user can perform this action.
37        """
38
39        shared_repos = []
40        username = request.user.username
41        try:
42            if is_org_context(request):
43                org_id = request.user.org.org_id
44                shared_repos += seafile_api.get_org_share_out_repo_list(org_id, username, -1, -1)
45                shared_repos += seafile_api.get_org_group_repos_by_owner(org_id, username)
46                shared_repos += seafile_api.list_org_inner_pub_repos_by_owner(org_id, username)
47            else:
48                shared_repos += seafile_api.get_share_out_repo_list(username, -1, -1)
49                shared_repos += seafile_api.get_group_repos_by_owner(username)
50                if not request.cloud_mode:
51                    shared_repos += seafile_api.list_inner_pub_repos_by_owner(username)
52        except Exception as e:
53            logger.error(e)
54            error_msg = 'Internal Server Error'
55            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
56
57        returned_result = []
58        shared_repos.sort(key=lambda x: x.repo_name)
59        usernames = []
60        gids = []
61        for repo in shared_repos:
62            if repo.is_virtual:
63                    continue
64
65            result = {}
66            result['repo_id'] = repo.repo_id
67            result['repo_name'] = repo.repo_name
68            result['encrypted'] = repo.encrypted
69            result['share_type'] = repo.share_type
70            result['share_permission'] = repo.permission
71            result['modifier_email'] = repo.last_modifier
72            result['modifier_name'] = email2nickname(repo.last_modifier)
73            result['modifier_contact_email'] = email2contact_email(repo.last_modifier)
74
75            if repo.share_type == 'personal':
76                result['user_name'] = email2nickname(repo.user)
77                result['user_email'] = repo.user
78                result['contact_email'] = Profile.objects.get_contact_email_by_user(repo.user)
79                usernames.append((repo.repo_id, repo.user))
80
81            if repo.share_type == 'group':
82                group = ccnet_api.get_group(repo.group_id)
83                result['group_id'] = repo.group_id
84                result['group_name'] = group.group_name if group else ''
85                gids.append(repo.group_id)
86
87            returned_result.append(result)
88
89        user_admins = ExtraSharePermission.objects.batch_is_admin(usernames)
90        group_admins = ExtraGroupsSharePermission.objects.batch_get_repos_with_admin_permission(gids)
91        for result in returned_result:
92            if result['share_type'] == 'group':
93                result['is_admin'] = (result['repo_id'], result['group_id']) in group_admins
94            elif result['share_type'] == 'personal':
95                result['is_admin'] = (result['repo_id'], result['user_email']) in user_admins
96
97        return Response(returned_result)
98
99
100class SharedRepo(APIView):
101    authentication_classes = (TokenAuthentication, SessionAuthentication)
102    permission_classes = (IsAuthenticated,)
103    throttle_classes = (UserRateThrottle,)
104
105    def put(self, request, repo_id, format=None):
106        """ Update permission of a shared repo.
107
108        Permission checking:
109        1. Only repo owner can update.
110        """
111
112        # argument check
113        permission = request.data.get('permission', None)
114        if permission not in get_available_repo_perms():
115            error_msg = 'permission invalid.'
116            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
117
118        share_type = request.data.get('share_type', None)
119        if not share_type:
120            error_msg = 'share_type invalid.'
121            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
122
123        if share_type not in ('personal', 'group', 'public'):
124            error_msg = "share_type can only be 'personal' or 'group' or 'public'."
125            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
126
127        # recourse check
128        repo = seafile_api.get_repo(repo_id)
129        if not repo:
130            error_msg = 'Library %s not found.' % repo_id
131            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
132
133        # permission check
134        username = request.user.username
135        if is_org_context(request):
136            repo_owner = seafile_api.get_org_repo_owner(repo_id)
137        else:
138            repo_owner = seafile_api.get_repo_owner(repo_id)
139
140        if username != repo_owner:
141            error_msg = 'Permission denied.'
142            return api_error(status.HTTP_403_FORBIDDEN, error_msg)
143
144        # update share permission
145        if share_type == 'personal':
146            shared_to = request.data.get('user', None)
147            if not shared_to or not is_valid_username(shared_to):
148                error_msg = 'user invalid.'
149                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
150
151            try:
152                if is_org_context(request):
153                    org_id = request.user.org.org_id
154                    update_user_dir_permission(repo_id, '/', repo_owner, shared_to, permission, org_id)
155                else:
156                    update_user_dir_permission(repo_id, '/', repo_owner, shared_to, permission)
157            except Exception as e:
158                logger.error(e)
159                error_msg = 'Internal Server Error'
160                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
161
162            send_perm_audit_msg('modify-repo-perm', username,
163                shared_to, repo_id, '/', permission)
164
165        if share_type == 'group':
166            group_id = request.data.get('group_id', None)
167            if not group_id:
168                error_msg = 'group_id invalid.'
169                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
170
171            try:
172                group_id = int(group_id)
173            except ValueError:
174                error_msg = 'group_id must be integer.'
175                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
176
177            group = ccnet_api.get_group(group_id)
178            if not group:
179                error_msg = 'Group %s not found.' % group_id
180                return api_error(status.HTTP_404_NOT_FOUND, error_msg)
181
182            try:
183                if is_org_context(request):
184                    org_id = request.user.org.org_id
185                    update_group_dir_permission(repo_id, '/', repo_owner, group_id, permission, org_id)
186                else:
187                    update_group_dir_permission(repo_id, '/', repo_owner, group_id, permission)
188            except Exception as e:
189                logger.error(e)
190                error_msg = 'Internal Server Error'
191                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
192
193            send_perm_audit_msg('modify-repo-perm', username,
194                    group_id, repo_id, '/', permission)
195
196        if share_type == 'public':
197
198            try:
199                if is_org_context(request):
200                    org_id = request.user.org.org_id
201                    seafile_api.set_org_inner_pub_repo(org_id, repo_id, permission)
202                else:
203                    if not request.user.permissions.can_add_public_repo():
204                        error_msg = 'Permission denied.'
205                        return api_error(status.HTTP_403_FORBIDDEN, error_msg)
206
207                    seafile_api.add_inner_pub_repo(repo_id, permission)
208
209            except Exception as e:
210                logger.error(e)
211                error_msg = 'Internal Server Error'
212                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
213
214            send_perm_audit_msg('modify-repo-perm', username,
215                    'all', repo_id, '/', permission)
216
217        return Response({'success': True})
218
219    def delete(self, request, repo_id, format=None):
220        """ Unshare a repo.
221
222        Permission checking:
223        1. Only repo owner and system admin can unshare a publib library.
224        """
225
226        # argument check
227        share_type = request.GET.get('share_type', None)
228        if not share_type:
229            error_msg = 'share_type invalid.'
230            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
231
232        if share_type not in ('personal', 'group', 'public'):
233            error_msg = "share_type can only be 'personal' or 'group' or 'public'."
234            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
235
236        # resource check
237        repo = seafile_api.get_repo(repo_id)
238        if not repo:
239            return api_error(status.HTTP_404_NOT_FOUND, 'Library %s not found.' % repo_id)
240
241        # permission check
242        username = request.user.username
243        if is_org_context(request):
244            repo_owner = seafile_api.get_org_repo_owner(repo_id)
245        else:
246            repo_owner = seafile_api.get_repo_owner(repo_id)
247
248        if not request.user.is_staff and not username == repo_owner:
249            error_msg = 'Permission denied.'
250            return api_error(status.HTTP_403_FORBIDDEN, error_msg)
251
252        # delete share
253        org_id = None
254        is_org = False
255        if is_org_context(request):
256            org_id = request.user.org.org_id
257            is_org = True
258
259        if share_type == 'personal':
260            user = request.GET.get('user', None)
261            if not user or not is_valid_username(user):
262                error_msg = 'user invalid.'
263                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
264
265            permission = check_user_share_out_permission(repo_id, '/', user, is_org)
266
267            try:
268                if org_id:
269                    seafile_api.org_remove_share(org_id, repo_id,
270                                                 username, user)
271                else:
272                    seafile_api.remove_share(repo_id, username, user)
273            except Exception as e:
274                logger.error(e)
275                error_msg = 'Internal Server Error'
276                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
277
278            send_perm_audit_msg('delete-repo-perm', username, user,
279                    repo_id, '/', permission)
280
281        if share_type == 'group':
282            group_id = request.GET.get('group_id', None)
283            if not group_id:
284                error_msg = 'group_id invalid.'
285                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
286
287            try:
288                group_id = int(group_id)
289            except ValueError:
290                error_msg = 'group_id must be integer.'
291                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
292
293            permission = check_group_share_out_permission(repo_id, '/', group_id, is_org)
294
295
296            try:
297                if is_org:
298                    seaserv.del_org_group_repo(repo_id, org_id, group_id)
299                else:
300                    seafile_api.unset_group_repo(repo_id, group_id, username)
301            except Exception as e:
302                logger.error(e)
303                error_msg = 'Internal Server Error'
304                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
305
306            send_perm_audit_msg('delete-repo-perm', username, group_id,
307                                repo_id, '/', permission)
308
309        if share_type == 'public':
310            pub_repos = []
311            if org_id:
312                pub_repos = seafile_api.list_org_inner_pub_repos(org_id)
313
314            if not request.cloud_mode:
315                pub_repos = seafile_api.get_inner_pub_repo_list()
316
317            try:
318                if org_id:
319                    seaserv.seafserv_threaded_rpc.unset_org_inner_pub_repo(org_id, repo_id)
320                else:
321                    seafile_api.remove_inner_pub_repo(repo_id)
322            except Exception as e:
323                logger.error(e)
324                error_msg = 'Internal Server Error'
325                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
326
327            permission = ''
328            for repo in pub_repos:
329                if repo.repo_id == repo_id:
330                    permission = repo.permission
331                    break
332
333            if permission:
334                send_perm_audit_msg('delete-repo-perm', username, 'all', repo_id, '/', permission)
335
336        return Response({'success': True})
337