1# Copyright (c) 2012-2016 Seafile Ltd.
2import os
3import logging
4from types import FunctionType
5from constance import config
6
7from rest_framework import status
8from rest_framework.authentication import SessionAuthentication
9from rest_framework.permissions import IsAdminUser
10from rest_framework.response import Response
11from rest_framework.views import APIView
12
13from django.db.models import Q
14from django.core.cache import cache
15from django.utils.translation import ugettext as _
16from django.utils.timezone import make_naive, is_aware
17
18from seaserv import seafile_api, ccnet_api
19
20from seahub.api2.authentication import TokenAuthentication
21from seahub.api2.throttling import UserRateThrottle
22from seahub.api2.utils import api_error, to_python_boolean
23from seahub.api2.models import TokenV2
24
25import seahub.settings as settings
26from seahub.settings import SEND_EMAIL_ON_ADDING_SYSTEM_MEMBER, INIT_PASSWD, \
27    SEND_EMAIL_ON_RESETTING_USER_PASSWD
28from seahub.base.templatetags.seahub_tags import email2nickname, email2contact_email
29from seahub.base.accounts import User
30from seahub.base.models import UserLastLogin
31from seahub.two_factor.models import default_device
32from seahub.profile.models import Profile
33from seahub.profile.settings import CONTACT_CACHE_TIMEOUT, CONTACT_CACHE_PREFIX, \
34    NICKNAME_CACHE_PREFIX, NICKNAME_CACHE_TIMEOUT
35from seahub.utils import is_valid_username2, is_org_context, \
36        is_pro_version, normalize_cache_key, is_valid_email, \
37        IS_EMAIL_CONFIGURED, send_html_email, get_site_name, \
38        gen_shared_link, gen_shared_upload_link
39
40from seahub.utils.file_size import get_file_size_unit
41from seahub.utils.timeutils import timestamp_to_isoformat_timestr, \
42        datetime_to_isoformat_timestr
43from seahub.utils.user_permissions import get_user_role
44from seahub.utils.repo import normalize_repo_status_code
45from seahub.constants import DEFAULT_ADMIN
46from seahub.role_permissions.models import AdminRole
47from seahub.role_permissions.utils import get_available_roles
48from seahub.utils.licenseparse import user_number_over_limit
49from seahub.institutions.models import Institution
50from seahub.avatar.templatetags.avatar_tags import api_avatar_url
51from seahub.admin_log.signals import admin_operation
52from seahub.admin_log.models import USER_DELETE, USER_ADD
53from seahub.api2.endpoints.group_owned_libraries import get_group_id_by_repo_owner
54from seahub.group.utils import group_id_to_name
55from seahub.institutions.models import InstitutionAdmin
56
57from seahub.options.models import UserOptions
58from seahub.share.models import FileShare, UploadLinkShare
59
60logger = logging.getLogger(__name__)
61json_content_type = 'application/json; charset=utf-8'
62
63
64def get_user_last_access_time(email, last_login_time):
65
66    device_last_access = ''
67    devices = TokenV2.objects.filter(user=email).order_by('-last_accessed')
68    if devices:
69        device_last_access = devices[0].last_accessed
70
71    # before make_naive
72    # 2021-04-09 05:32:30+00:00
73    # tzinfo: UTC
74
75    # after make_naive
76    # 2021-04-09 13:32:30
77    # tzinfo: None
78    last_access_time_list = []
79    if last_login_time:
80        if is_aware(last_login_time):
81            last_login_time = make_naive(last_login_time)
82        last_access_time_list.append(last_login_time)
83
84    if device_last_access:
85        if is_aware(device_last_access):
86            device_last_access = make_naive(device_last_access)
87        last_access_time_list.append(device_last_access)
88
89    if not last_access_time_list:
90        return ''
91    else:
92        return datetime_to_isoformat_timestr(sorted(last_access_time_list)[-1])
93
94
95def get_user_upload_link_info(uls):
96    data = {}
97
98    repo_id = uls.repo_id
99    try:
100        repo = seafile_api.get_repo(repo_id)
101    except Exception as e:
102        logger.error(e)
103        repo = None
104
105    path = uls.path
106    if path:
107        obj_name = '/' if path == '/' else os.path.basename(path.rstrip('/'))
108    else:
109        obj_name = ''
110
111    data['repo_name'] = repo.repo_name if repo else ''
112    data['path'] = path
113    data['token'] = uls.token
114    data['link'] = gen_shared_upload_link(uls.token)
115    data['obj_name'] = obj_name
116    data['view_cnt'] = uls.view_cnt
117
118    return data
119
120
121def get_user_share_link_info(fileshare):
122    data = {}
123
124    repo_id = fileshare.repo_id
125    try:
126        repo = seafile_api.get_repo(repo_id)
127    except Exception as e:
128        logger.error(e)
129        repo = None
130
131    path = fileshare.path
132    if path:
133        obj_name = '/' if path == '/' else os.path.basename(path.rstrip('/'))
134    else:
135        obj_name = ''
136
137    data['repo_name'] = repo.repo_name if repo else ''
138    data['token'] = fileshare.token
139    data['link'] = gen_shared_link(fileshare.token, fileshare.s_type)
140
141    data['path'] = path
142    data['obj_name'] = obj_name
143    data['is_dir'] = True if fileshare.s_type == 'd' else False
144
145    data['view_cnt'] = fileshare.view_cnt
146
147    if fileshare.s_type == 'f':
148        obj_id = seafile_api.get_file_id_by_path(repo_id, path)
149        data['size'] = seafile_api.get_file_size(repo.store_id,
150                repo.version, obj_id)
151    else:
152        data['size'] = ''
153
154    return data
155
156
157def create_user_info(request, email, role, nickname, contact_email, quota_total_mb):
158    # update additional user info
159
160    if is_pro_version() and role:
161        User.objects.update_role(email, role)
162
163    if nickname is not None:
164        Profile.objects.add_or_update(email, nickname)
165        key = normalize_cache_key(nickname, NICKNAME_CACHE_PREFIX)
166        cache.set(key, nickname, NICKNAME_CACHE_TIMEOUT)
167
168    if contact_email is not None:
169        Profile.objects.add_or_update(email, contact_email=contact_email)
170        key = normalize_cache_key(email, CONTACT_CACHE_PREFIX)
171        cache.set(key, contact_email, CONTACT_CACHE_TIMEOUT)
172
173    if quota_total_mb:
174        quota_total = int(quota_total_mb) * get_file_size_unit('MB')
175        if is_org_context(request):
176            org_id = request.user.org.org_id
177            seafile_api.set_org_user_quota(org_id, email, quota_total)
178        else:
179            seafile_api.set_user_quota(email, quota_total)
180
181
182def update_user_info(request, user, password, is_active, is_staff, role,
183                     nickname, login_id, contact_email, reference_id, quota_total_mb, institution_name):
184
185    # update basic user info
186    if is_active is not None:
187        user.is_active = is_active
188
189    if password:
190        user.set_password(password)
191
192    if is_staff is not None:
193        user.is_staff = is_staff
194
195    # update user
196    user.save()
197
198    email = user.username
199
200    # update additional user info
201    if is_pro_version() and role:
202        User.objects.update_role(email, role)
203
204    if nickname is not None:
205        Profile.objects.add_or_update(email, nickname)
206        key = normalize_cache_key(nickname, NICKNAME_CACHE_PREFIX)
207        cache.set(key, nickname, NICKNAME_CACHE_TIMEOUT)
208
209    if login_id is not None:
210        Profile.objects.add_or_update(email, login_id=login_id)
211
212    if contact_email is not None:
213        Profile.objects.add_or_update(email, contact_email=contact_email)
214        key = normalize_cache_key(email, CONTACT_CACHE_PREFIX)
215        cache.set(key, contact_email, CONTACT_CACHE_TIMEOUT)
216
217    if reference_id is not None:
218        if reference_id.strip():
219            ccnet_api.set_reference_id(email, reference_id.strip())
220        else:
221            # remove reference id
222            ccnet_api.set_reference_id(email, None)
223
224    if institution_name is not None:
225        Profile.objects.add_or_update(email, institution=institution_name)
226        if institution_name == '':
227            InstitutionAdmin.objects.filter(user=email).delete()
228
229    if quota_total_mb is not None:
230        quota_total = int(quota_total_mb) * get_file_size_unit('MB')
231        orgs = ccnet_api.get_orgs_by_user(email)
232        try:
233            if orgs:
234                org_id = orgs[0].org_id
235                seafile_api.set_org_user_quota(org_id, email, quota_total)
236            else:
237                seafile_api.set_user_quota(email, quota_total)
238        except Exception as e:
239            logger.error(e)
240            seafile_api.set_user_quota(email, -1)
241
242
243def get_user_info(email):
244
245    user = User.objects.get(email=email)
246    profile = Profile.objects.get_profile_by_user(email)
247
248    info = {}
249    info['email'] = email
250    info['name'] = email2nickname(email)
251    info['contact_email'] = profile.contact_email if profile and profile.contact_email else ''
252    info['login_id'] = profile.login_id if profile and profile.login_id else ''
253
254    info['is_staff'] = user.is_staff
255    info['is_active'] = user.is_active
256    info['reference_id'] = user.reference_id if user.reference_id else ''
257
258    orgs = ccnet_api.get_orgs_by_user(email)
259    try:
260        if orgs:
261            org_id = orgs[0].org_id
262            info['org_id'] = org_id
263            info['org_name'] = orgs[0].org_name
264            info['quota_usage'] = seafile_api.get_org_user_quota_usage(org_id, user.email)
265            info['quota_total'] = seafile_api.get_org_user_quota(org_id, user.email)
266        else:
267            info['quota_usage'] = seafile_api.get_user_self_usage(user.email)
268            info['quota_total'] = seafile_api.get_user_quota(user.email)
269    except Exception as e:
270        logger.error(e)
271        info['quota_usage'] = -1
272        info['quota_total'] = -1
273
274    info['create_time'] = timestamp_to_isoformat_timestr(user.ctime)
275
276    info['has_default_device'] = True if default_device(user) else False
277    info['is_force_2fa'] = UserOptions.objects.is_force_2fa(email)
278
279    if getattr(settings, 'MULTI_INSTITUTION', False):
280        info['institution'] = profile.institution if profile else ''
281
282    info['role'] = get_user_role(user)
283
284    return info
285
286
287class AdminAdminUsers(APIView):
288
289    authentication_classes = (TokenAuthentication, SessionAuthentication)
290    permission_classes = (IsAdminUser, )
291    throttle_classes = (UserRateThrottle, )
292
293    def get(self, request):
294        """List all admins from database and ldap imported
295        """
296
297        if not request.user.admin_permissions.can_manage_user():
298            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
299
300        try:
301            admin_users = ccnet_api.get_superusers()
302        except Exception as e:
303            logger.error(e)
304            error_msg = 'Internal Server Error'
305            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
306
307        admin_users_info = []
308        for user in admin_users:
309            user_info = {}
310            profile = Profile.objects.get_profile_by_user(user.email)
311            user_info['email'] = user.email
312            user_info['name'] = email2nickname(user.email)
313            user_info['contact_email'] = email2contact_email(user.email)
314            user_info['login_id'] = profile.login_id if profile and profile.login_id else ''
315
316            user_info['is_staff'] = user.is_staff
317            user_info['is_active'] = user.is_active
318
319            orgs = ccnet_api.get_orgs_by_user(user.email)
320            try:
321                if orgs:
322                    org_id = orgs[0].org_id
323                    user_info['org_id'] = org_id
324                    user_info['org_name'] = orgs[0].org_name
325                    user_info['quota_usage'] = seafile_api.get_org_user_quota_usage(org_id, user.email)
326                    user_info['quota_total'] = seafile_api.get_org_user_quota(org_id, user.email)
327                else:
328                    user_info['quota_usage'] = seafile_api.get_user_self_usage(user.email)
329                    user_info['quota_total'] = seafile_api.get_user_quota(user.email)
330            except Exception as e:
331                logger.error(e)
332                user_info['quota_usage'] = -1
333                user_info['quota_total'] = -1
334
335            user_info['create_time'] = timestamp_to_isoformat_timestr(user.ctime)
336
337            last_login_obj = UserLastLogin.objects.get_by_username(user.email)
338            if last_login_obj:
339                user_info['last_login'] = datetime_to_isoformat_timestr(last_login_obj.last_login)
340                user_info['last_access_time'] = get_user_last_access_time(user.email,
341                                                                          last_login_obj.last_login)
342            else:
343                user_info['last_login'] = ''
344                user_info['last_access_time'] = get_user_last_access_time(user.email, '')
345
346            try:
347                admin_role = AdminRole.objects.get_admin_role(user.email)
348                user_info['admin_role'] = admin_role.role
349            except AdminRole.DoesNotExist:
350                user_info['admin_role'] = DEFAULT_ADMIN
351            admin_users_info.append(user_info)
352
353        result = {
354            'admin_user_list': admin_users_info,
355        }
356        return Response(result)
357
358
359class AdminUsers(APIView):
360
361    authentication_classes = (TokenAuthentication, SessionAuthentication)
362    permission_classes = (IsAdminUser, )
363    throttle_classes = (UserRateThrottle, )
364
365    def get_info_of_users_order_by_quota_usage(self, source, direction,
366            page, per_page):
367
368        # get user's quota usage info
369        user_usage_dict = {}
370        users_with_usage = seafile_api.list_user_quota_usage()
371        for user in users_with_usage:
372            email = user.user
373            if email not in user_usage_dict:
374                user_usage_dict[email] = user.usage
375
376        # get all users and map quota usage to user
377        if source == 'db':
378            users = ccnet_api.get_emailusers('DB', -1, -1)
379        else:
380            users = ccnet_api.get_emailusers('LDAPImport', -1, -1)
381
382        for user in users:
383            email = user.email
384            user.quota_usage = user_usage_dict.get(email, -1)
385
386        # sort
387        users.sort(key=lambda item: item.quota_usage,
388                reverse = direction == 'desc')
389
390        data = []
391        MULTI_INSTITUTION = getattr(settings, 'MULTI_INSTITUTION', False)
392        for user in users[(page-1)*per_page: page*per_page]:
393
394            info = {}
395            info['email'] = user.email
396            info['name'] = email2nickname(user.email)
397            info['contact_email'] = email2contact_email(user.email)
398
399            profile = Profile.objects.get_profile_by_user(user.email)
400            info['login_id'] = profile.login_id if profile and profile.login_id else ''
401
402            info['is_staff'] = user.is_staff
403            info['is_active'] = user.is_active
404            info['create_time'] = timestamp_to_isoformat_timestr(user.ctime)
405
406            info['quota_usage'] = user.quota_usage
407            info['quota_total'] = seafile_api.get_user_quota(user.email)
408
409            last_login_obj = UserLastLogin.objects.get_by_username(user.email)
410            if last_login_obj:
411                info['last_login'] = datetime_to_isoformat_timestr(last_login_obj.last_login)
412                info['last_access_time'] = get_user_last_access_time(user.email,
413                                                                     last_login_obj.last_login)
414            else:
415                info['last_login'] = ''
416                info['last_access_time'] = get_user_last_access_time(user.email, '')
417
418            info['role'] = get_user_role(user)
419
420            if MULTI_INSTITUTION:
421                info['institution'] = profile.institution if profile else ''
422
423            data.append(info)
424
425        return data
426
427    def get(self, request):
428        """List all users in DB or LDAPImport
429
430        Permission checking:
431        1. only admin can perform this action.
432        """
433
434        if not request.user.admin_permissions.can_manage_user():
435            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
436
437        # parameter check
438        try:
439            page = int(request.GET.get('page', '1'))
440            per_page = int(request.GET.get('per_page', '25'))
441        except ValueError:
442            page = 1
443            per_page = 25
444
445        start = (page - 1) * per_page
446
447        source = request.GET.get('source', 'DB').lower().strip()
448        if source not in ['db', 'ldapimport']:
449            # source: 'DB' or 'LDAPImport', default is 'DB'
450            error_msg = 'source %s invalid.' % source
451            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
452
453        order_by = request.GET.get('order_by', '').lower().strip()
454        if order_by:
455            if order_by not in ('quota_usage'):
456                error_msg = 'order_by invalid.'
457                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
458
459            direction = request.GET.get('direction', 'desc').lower().strip()
460            if direction not in ('asc', 'desc'):
461                error_msg = 'direction invalid.'
462                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
463
464        if source == 'db':
465
466            total_count = ccnet_api.count_emailusers('DB') + \
467                          ccnet_api.count_inactive_emailusers('DB')
468            if order_by:
469
470                if total_count > 500 and \
471                        not getattr(settings, 'ALWAYS_SORT_USERS_BY_QUOTA_USAGE', False):
472                    error_msg = _("There are more than 500 users, and sort is not offered.")
473                    return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
474
475                try:
476                    data = self.get_info_of_users_order_by_quota_usage(source,
477                                                                       direction,
478                                                                       page,
479                                                                       per_page)
480                except Exception as e:
481                    logger.error(e)
482                    error_msg = 'Internal Server Error'
483                    return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
484
485                result = {'data': data, 'total_count': total_count}
486                return Response(result)
487            else:
488                users = ccnet_api.get_emailusers('DB', start, per_page)
489
490        elif source == 'ldapimport':
491
492            # api param is 'LDAP', but actually get count of 'LDAPImport' users
493            total_count = ccnet_api.count_emailusers('LDAP') + \
494                          ccnet_api.count_inactive_emailusers('LDAP')
495            if order_by:
496
497                if total_count > 500 and \
498                        not getattr(settings, 'ALWAYS_SORT_USERS_BY_QUOTA_USAGE', False):
499                    error_msg = _("There are more than 500 users, and sort is not offered.")
500                    return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
501
502                try:
503                    data = self.get_info_of_users_order_by_quota_usage(source,
504                                                                       direction,
505                                                                       page,
506                                                                       per_page)
507                except Exception as e:
508                    logger.error(e)
509                    error_msg = 'Internal Server Error'
510                    return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
511
512                result = {'data': data, 'total_count': total_count}
513                return Response(result)
514            else:
515                users = ccnet_api.get_emailusers('LDAPImport', start, per_page)
516
517        data = []
518        for user in users:
519            profile = Profile.objects.get_profile_by_user(user.email)
520
521            info = {}
522            info['email'] = user.email
523            info['name'] = email2nickname(user.email)
524            info['contact_email'] = email2contact_email(user.email)
525            info['login_id'] = profile.login_id if profile and profile.login_id else ''
526
527            info['is_staff'] = user.is_staff
528            info['is_active'] = user.is_active
529
530            orgs = ccnet_api.get_orgs_by_user(user.email)
531            try:
532                if orgs:
533                    org_id = orgs[0].org_id
534                    info['org_id'] = org_id
535                    info['org_name'] = orgs[0].org_name
536                    info['quota_usage'] = seafile_api.get_org_user_quota_usage(org_id, user.email)
537                    info['quota_total'] = seafile_api.get_org_user_quota(org_id, user.email)
538                else:
539                    info['quota_usage'] = seafile_api.get_user_self_usage(user.email)
540                    info['quota_total'] = seafile_api.get_user_quota(user.email)
541            except Exception as e:
542                logger.error(e)
543                info['quota_usage'] = -1
544                info['quota_total'] = -1
545
546            info['role'] = get_user_role(user)
547
548            info['create_time'] = timestamp_to_isoformat_timestr(user.ctime)
549
550            last_login_obj = UserLastLogin.objects.get_by_username(user.email)
551            if last_login_obj:
552                info['last_login'] = datetime_to_isoformat_timestr(last_login_obj.last_login)
553                info['last_access_time'] = get_user_last_access_time(user.email,
554                                                                     last_login_obj.last_login)
555            else:
556                info['last_login'] = ''
557                info['last_access_time'] = get_user_last_access_time(user.email, '')
558
559            if getattr(settings, 'MULTI_INSTITUTION', False):
560                info['institution'] = profile.institution if profile else ''
561
562            data.append(info)
563
564        result = {'data': data, 'total_count': total_count}
565        return Response(result)
566
567    def post(self, request):
568
569        if not request.user.admin_permissions.can_manage_user():
570            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
571
572        if user_number_over_limit():
573            error_msg = _("The number of users exceeds the limit.")
574            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
575
576        email = request.data.get('email', None)
577        if not email or not is_valid_email(email):
578            error_msg = 'email invalid.'
579            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
580
581        # basic user info check
582        is_staff = request.data.get("is_staff", 'False')
583        try:
584            is_staff = to_python_boolean(is_staff)
585        except ValueError:
586            error_msg = 'is_staff invalid.'
587            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
588
589        is_active = request.data.get("is_active", 'True')
590        try:
591            is_active = to_python_boolean(is_active)
592        except ValueError:
593            error_msg = 'is_active invalid.'
594            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
595
596        # additional user info check
597        role = ''
598        if is_pro_version():
599            role = request.data.get("role", None)
600        if role:
601            available_roles = get_available_roles()
602            if role not in available_roles:
603                error_msg = 'role must be in %s.' % str(available_roles)
604                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
605
606        name = request.data.get("name", None)
607        if name:
608            if len(name) > 64:
609                error_msg = 'Name is too long (maximum is 64 characters).'
610                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
611
612            if "/" in name:
613                error_msg = "Name should not include '/'."
614                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
615
616        contact_email = request.data.get('contact_email', None)
617        if contact_email and not is_valid_email(contact_email):
618            error_msg = 'contact_email invalid.'
619            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
620
621        quota_total_mb = request.data.get("quota_total", None)
622        if quota_total_mb:
623            try:
624                quota_total_mb = int(quota_total_mb)
625            except ValueError:
626                error_msg = "Must be an integer that is greater than or equal to 0."
627                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
628
629            if quota_total_mb < 0:
630                error_msg = "Space quota is too low (minimum value is 0)."
631                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
632
633            if is_org_context(request):
634                org_id = request.user.org.org_id
635                org_quota_mb = seafile_api.get_org_quota(org_id) / get_file_size_unit('MB')
636
637                if quota_total_mb > org_quota_mb:
638                    error_msg = 'Failed to set quota: maximum quota is %d MB' % org_quota_mb
639                    return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
640
641        try:
642            User.objects.get(email=email)
643            user_exist = True
644        except User.DoesNotExist:
645            user_exist = False
646
647        if user_exist:
648            error_msg = "User %s already exists." % email
649            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
650
651        password = request.data.get('password', None)
652        if not password:
653            error_msg = 'password required.'
654            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
655
656        # create user
657        try:
658            user_obj = User.objects.create_user(email, password, is_staff, is_active)
659            create_user_info(request, email=user_obj.username, role=role,
660                             nickname=name, contact_email=contact_email,
661                             quota_total_mb=quota_total_mb)
662        except Exception as e:
663            logger.error(e)
664            error_msg = 'Internal Server Error'
665            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
666
667        add_user_tip = _('Successfully added user %(user)s.') % {'user': email}
668        if IS_EMAIL_CONFIGURED and SEND_EMAIL_ON_ADDING_SYSTEM_MEMBER:
669            c = {'user': request.user.username, 'email': email, 'password': password}
670            try:
671                send_html_email(_('You are invited to join %s') % get_site_name(),
672                                'sysadmin/user_add_email.html',
673                                c,
674                                None,
675                                [email2contact_email(email)])
676
677                add_user_tip = _('Successfully added user %(user)s. An email notification has been sent.') % {'user': email}
678            except Exception as e:
679                logger.error(str(e))
680                add_user_tip = _('Successfully added user %(user)s. But email notification can not be sent, because Email service is not properly configured.') % {'user': email}
681
682        user_info = get_user_info(email)
683        user_info['add_user_tip'] = add_user_tip
684
685        # send admin operation log signal
686        admin_op_detail = {
687            "email": email,
688        }
689        admin_operation.send(sender=None, admin_name=request.user.username,
690                             operation=USER_ADD, detail=admin_op_detail)
691
692        if config.FORCE_PASSWORD_CHANGE:
693            UserOptions.objects.set_force_passwd_change(email)
694
695        return Response(user_info)
696
697
698class AdminLDAPUsers(APIView):
699
700    authentication_classes = (TokenAuthentication, SessionAuthentication)
701    permission_classes = (IsAdminUser, )
702    throttle_classes = (UserRateThrottle, )
703
704    def get(self, request):
705        """List all users from LDAP server
706
707        Permission checking:
708        1. only admin can perform this action.
709        """
710
711        if not request.user.admin_permissions.can_manage_user():
712            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
713
714        try:
715            page = int(request.GET.get('page', '1'))
716            per_page = int(request.GET.get('per_page', '25'))
717        except ValueError:
718            page = 1
719            per_page = 25
720
721        start = (page - 1) * per_page
722        end = page * per_page + 1
723        users = ccnet_api.get_emailusers('LDAP', start, end)
724
725        if len(users) == end - start:
726            users = users[:per_page]
727            has_next_page = True
728        else:
729            has_next_page = False
730
731        data = []
732        for user in users:
733            info = {}
734            info['email'] = user.email
735            info['quota_total'] = seafile_api.get_user_quota(user.email)
736            info['quota_usage'] = seafile_api.get_user_self_usage(user.email)
737            info['create_time'] = timestamp_to_isoformat_timestr(user.ctime)
738
739            last_login_obj = UserLastLogin.objects.get_by_username(user.email)
740            if last_login_obj:
741                info['last_login'] = datetime_to_isoformat_timestr(last_login_obj.last_login)
742                info['last_access_time'] = get_user_last_access_time(user.email,
743                                                                     last_login_obj.last_login)
744            else:
745                info['last_login'] = ''
746                info['last_access_time'] = get_user_last_access_time(user.email, '')
747
748            data.append(info)
749
750        result = {'ldap_user_list': data, 'has_next_page': has_next_page}
751        return Response(result)
752
753
754class AdminSearchUser(APIView):
755
756    authentication_classes = (TokenAuthentication, SessionAuthentication)
757    permission_classes = (IsAdminUser, )
758    throttle_classes = (UserRateThrottle, )
759
760    def get(self, request):
761        """Search user from DB, LDAPImport and Profile
762
763        Permission checking:
764        1. only admin can perform this action.
765        """
766
767        if not request.user.admin_permissions.can_manage_user():
768            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
769
770        query_str = request.GET.get('query', '').lower()
771        if not query_str:
772            error_msg = 'query invalid.'
773            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
774
775        users = []
776
777        page = request.GET.get('page', '')
778        per_page = request.GET.get('per_page', '')
779
780        if not page or not per_page:
781
782            # search user from ccnet db
783            users += ccnet_api.search_emailusers('DB', query_str, 0, 10)
784
785            # search user from ccnet ldapimport
786            users += ccnet_api.search_emailusers('LDAP', query_str, 0, 10)
787
788            ccnet_user_emails = [u.email for u in users]
789
790            # get institution for user from ccnet
791            if getattr(settings, 'MULTI_INSTITUTION', False):
792                user_institution_dict = {}
793                profiles = Profile.objects.filter(user__in=ccnet_user_emails)
794                for profile in profiles:
795                    email = profile.user
796                    if email not in user_institution_dict:
797                        user_institution_dict[email] = profile.institution
798
799                for user in users:
800                    user.institution = user_institution_dict.get(user.email, '')
801
802            # search user from profile
803            searched_profile = Profile.objects.filter((Q(nickname__icontains=query_str)) | \
804                                                       Q(contact_email__icontains=query_str))[:10]
805
806            for profile in searched_profile:
807                email = profile.user
808                institution = profile.institution
809
810                # remove duplicate emails
811                if email not in ccnet_user_emails:
812                    try:
813                        # get is_staff and is_active info
814                        user = User.objects.get(email=email)
815                        user.institution = institution
816                        users.append(user)
817                    except User.DoesNotExist:
818                        continue
819
820            page_info = {
821                'has_next_page': '',
822                'current_page': ''
823            }
824
825        else:
826
827            try:
828                page = int(page)
829                per_page = int(per_page)
830            except ValueError:
831                page = 1
832                per_page = 25
833
834            ccnet_users = []
835            ccnet_db_users = ccnet_api.search_emailusers('DB', query_str, 0, page * per_page)
836            ccnet_ldap_import_users = []
837
838            if len(ccnet_db_users) == page * per_page:
839
840                # users from ccnet db is enough
841                ccnet_users = ccnet_db_users[-per_page:]
842
843            elif len(ccnet_db_users) < page * per_page:
844
845                ccnet_ldap_import_users = ccnet_api.search_emailusers('LDAP',
846                                                                      query_str,
847                                                                      0,
848                                                                      page*per_page - len(ccnet_db_users))
849
850                if int(len(ccnet_db_users)/per_page) == page-1:
851                    # need ccnet_db_users + ccnet_ldap_import_users
852                    ccnet_users = ccnet_db_users[(page-1)*per_page-len(ccnet_db_users):] + ccnet_ldap_import_users
853
854                if int(len(ccnet_db_users)/per_page) < page-1:
855                    # users only from ccnet_ldap_import_users
856                    ccnet_users = ccnet_ldap_import_users[-per_page:]
857
858            # search user from profile
859            profile_users = []
860            all_ccnet_users = ccnet_db_users + ccnet_ldap_import_users
861            all_profile_users = []
862
863            if len(all_ccnet_users) == page * per_page:
864
865                # users from ccnet is enough
866                users = ccnet_users
867
868            if len(all_ccnet_users) < page * per_page:
869                all_profile_users = Profile.objects.filter((Q(nickname__icontains=query_str)) | \
870                                                           Q(contact_email__icontains=query_str)) \
871                                                          [0:page*per_page-len(all_ccnet_users)]
872
873                if int(len(all_ccnet_users)/per_page) == page-1:
874                    # need ccnet users + profile users
875                    tmp_users = []
876                    for profile_user in all_profile_users:
877                        try:
878                            user = User.objects.get(email=profile_user.user)
879                            tmp_users.append(user)
880                        except User.DoesNotExist:
881                            continue
882
883                    users = ccnet_users + tmp_users
884
885                if int(len(all_ccnet_users)/per_page) < page-1:
886                    # only need profile users
887                    for profile_user in list(all_profile_users)[-per_page:]:
888                        try:
889                            user = User.objects.get(email=profile_user.user)
890                            users.append(user)
891                        except User.DoesNotExist:
892                            continue
893
894            if len(all_ccnet_users) + len(all_profile_users) >= page * per_page:
895                has_next_page = True
896            else:
897                has_next_page = False
898
899            page_info = {
900                'has_next_page': has_next_page,
901                'current_page': page
902            }
903
904            # get institution for user from ccnet
905            if getattr(settings, 'MULTI_INSTITUTION', False):
906                for user in users:
907                    if not hasattr(user, 'institution'):
908                        profile = Profile.objects.filter(user=user.email)
909                        user.institution = profile.institution
910
911        data = []
912        has_appended = []
913
914        for user in users:
915
916            if user.email in has_appended:
917                continue
918            else:
919                has_appended.append(user.email)
920
921            info = {}
922            info['email'] = user.email
923            info['name'] = email2nickname(user.email)
924            info['contact_email'] = email2contact_email(user.email)
925
926            info['is_staff'] = user.is_staff
927            info['is_active'] = user.is_active
928
929            info['source'] = user.source.lower()
930
931            orgs = ccnet_api.get_orgs_by_user(user.email)
932            if orgs:
933                org_id = orgs[0].org_id
934                info['org_id'] = org_id
935                info['org_name'] = orgs[0].org_name
936                info['quota_usage'] = seafile_api.get_org_user_quota_usage(org_id, user.email)
937                info['quota_total'] = seafile_api.get_org_user_quota(org_id, user.email)
938            else:
939                info['quota_usage'] = seafile_api.get_user_self_usage(user.email)
940                info['quota_total'] = seafile_api.get_user_quota(user.email)
941
942            info['create_time'] = timestamp_to_isoformat_timestr(user.ctime)
943
944            last_login_obj = UserLastLogin.objects.get_by_username(user.email)
945            if last_login_obj:
946                info['last_login'] = datetime_to_isoformat_timestr(last_login_obj.last_login)
947                info['last_access_time'] = get_user_last_access_time(user.email,
948                                                                     last_login_obj.last_login)
949            else:
950                info['last_login'] = ''
951                info['last_access_time'] = get_user_last_access_time(user.email, '')
952
953            info['role'] = get_user_role(user)
954
955            if getattr(settings, 'MULTI_INSTITUTION', False):
956                info['institution'] = user.institution
957
958            data.append(info)
959
960        result = {
961            'user_list': data,
962            'page_info': page_info,
963        }
964        return Response(result)
965
966
967class AdminUser(APIView):
968
969    authentication_classes = (TokenAuthentication, SessionAuthentication)
970    permission_classes = (IsAdminUser, )
971    throttle_classes = (UserRateThrottle, )
972
973    def get(self, request, email):
974
975        if not (request.user.admin_permissions.can_manage_user() or \
976            request.user.admin_permissions.can_update_user()):
977            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
978
979        avatar_size = request.data.get('avatar_size', 64)
980        try:
981            avatar_size = int(avatar_size)
982        except Exception as e:
983            logger.error(e)
984            error_msg = 'avatar_size invalid.'
985            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
986
987        try:
988            User.objects.get(email=email)
989        except User.DoesNotExist:
990            error_msg = 'User %s not found.' % email
991            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
992
993        user_info = get_user_info(email)
994        user_info['avatar_url'], _, _ = api_avatar_url(email, avatar_size)
995
996        return Response(user_info)
997
998    def put(self, request, email):
999
1000        if not (request.user.admin_permissions.can_manage_user() or \
1001            request.user.admin_permissions.can_update_user()):
1002            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
1003
1004        # basic user info check
1005        is_staff = request.data.get("is_staff", None)
1006        if is_staff:
1007            try:
1008                is_staff = to_python_boolean(is_staff)
1009            except ValueError:
1010                error_msg = 'is_staff invalid.'
1011                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1012
1013        is_active = request.data.get("is_active", None)
1014        if is_active:
1015            try:
1016                is_active = to_python_boolean(is_active)
1017            except ValueError:
1018                error_msg = 'is_active invalid.'
1019                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1020
1021        # additional user info check
1022        role = request.data.get("role", None)
1023        if role:
1024            available_roles = get_available_roles()
1025            if role not in available_roles:
1026                error_msg = 'role must be in %s.' % str(available_roles)
1027                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1028
1029        name = request.data.get("name", None)
1030        if name:
1031            if len(name) > 64:
1032                error_msg = 'Name is too long (maximum is 64 characters).'
1033                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1034
1035            if "/" in name:
1036                error_msg = "Name should not include '/'."
1037                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1038
1039        # argument check for login_id
1040        login_id = request.data.get("login_id", None)
1041        if login_id is not None:
1042            login_id = login_id.strip()
1043            username_by_login_id = Profile.objects.get_username_by_login_id(login_id)
1044            if username_by_login_id is not None:
1045                return api_error(status.HTTP_400_BAD_REQUEST,
1046                                 _("Login id %s already exists." % login_id))
1047
1048        contact_email = request.data.get("contact_email", None)
1049        if contact_email is not None and contact_email.strip() != '':
1050            if not is_valid_email(contact_email):
1051                error_msg = 'Contact email invalid.'
1052                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1053
1054        password = request.data.get("password")
1055
1056        reference_id = request.data.get("reference_id", None)
1057        if reference_id:
1058            if ' ' in reference_id:
1059                return api_error(status.HTTP_400_BAD_REQUEST, 'Reference ID can not contain spaces.')
1060            primary_id = ccnet_api.get_primary_id(reference_id)
1061            if primary_id:
1062                return api_error(status.HTTP_400_BAD_REQUEST, 'Reference ID %s already exists.' % reference_id)
1063
1064        quota_total_mb = request.data.get("quota_total", None)
1065        if quota_total_mb:
1066            try:
1067                quota_total_mb = int(quota_total_mb)
1068            except ValueError:
1069                error_msg = "Must be an integer that is greater than or equal to 0."
1070                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1071
1072            if quota_total_mb < 0:
1073                error_msg = "Space quota is too low (minimum value is 0)."
1074                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1075
1076            if is_org_context(request):
1077                org_id = request.user.org.org_id
1078                org_quota_mb = seafile_api.get_org_quota(org_id) / get_file_size_unit('MB')
1079
1080                if quota_total_mb > org_quota_mb:
1081                    error_msg = 'Failed to set quota: maximum quota is %d MB' % org_quota_mb
1082                    return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1083
1084        institution = request.data.get("institution", None)
1085        if institution:
1086            try:
1087                Institution.objects.get(name=institution)
1088            except Institution.DoesNotExist:
1089                error_msg = 'Institution %s does not exist' % institution
1090                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1091
1092        # query user info
1093        try:
1094            user_obj = User.objects.get(email=email)
1095        except User.DoesNotExist:
1096            error_msg = 'User %s not found.' % email
1097            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
1098
1099        try:
1100            update_user_info(request, user=user_obj, password=password, is_active=is_active, is_staff=is_staff,
1101                             role=role, nickname=name, login_id=login_id, contact_email=contact_email,
1102                             reference_id=reference_id, quota_total_mb=quota_total_mb, institution_name=institution)
1103        except Exception as e:
1104            logger.error(e)
1105            error_msg = 'Internal Server Error'
1106            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
1107
1108        # update user
1109        try:
1110            user_obj.save()
1111        except Exception as e:
1112            logger.error(e)
1113            error_msg = 'Internal Server Error'
1114            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
1115
1116        update_status_tip = ''
1117        if is_active is not None:
1118            update_status_tip = _('Edit succeeded')
1119            if user_obj.is_active and IS_EMAIL_CONFIGURED:
1120                try:
1121                    send_html_email(_(u'Your account on %s is activated') % get_site_name(),
1122                                    'sysadmin/user_activation_email.html',
1123                                    {'username': user_obj.email},
1124                                    None,
1125                                    [email2contact_email(user_obj.email)])
1126                    update_status_tip = _('Edit succeeded, an email has been sent.')
1127                except Exception as e:
1128                    logger.error(e)
1129                    update_status_tip = _('Edit succeeded, but failed to send email, please check your email configuration.')
1130
1131        user_info = get_user_info(email)
1132        user_info['update_status_tip'] = update_status_tip
1133
1134        return Response(user_info)
1135
1136    def delete(self, request, email):
1137
1138        if not request.user.admin_permissions.can_manage_user():
1139            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
1140
1141        try:
1142            User.objects.get(email=email)
1143        except User.DoesNotExist:
1144            error_msg = 'User %s not found.' % email
1145            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
1146
1147        # delete user
1148        try:
1149            User.objects.get(email=email).delete()
1150        except Exception as e:
1151            logger.error(e)
1152            error_msg = 'Internal Server Error'
1153            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
1154
1155        # send admin operation log signal
1156        admin_op_detail = {
1157            "email": email,
1158        }
1159        admin_operation.send(sender=None, admin_name=request.user.username,
1160                             operation=USER_DELETE, detail=admin_op_detail)
1161
1162        return Response({'success': True})
1163
1164
1165class AdminUserResetPassword(APIView):
1166
1167    authentication_classes = (TokenAuthentication, SessionAuthentication)
1168    permission_classes = (IsAdminUser, )
1169    throttle_classes = (UserRateThrottle, )
1170
1171    def put(self, request, email):
1172        """Reset password for user
1173
1174        Permission checking:
1175        1. only admin can perform this action.
1176        """
1177
1178        if not request.user.admin_permissions.can_manage_user():
1179            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
1180
1181        if not is_valid_username2(email):
1182            error_msg = 'email invalid'
1183            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1184
1185        try:
1186            user = User.objects.get(email=email)
1187        except User.DoesNotExist as e:
1188            logger.error(e)
1189            error_msg = 'email invalid.'
1190            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1191
1192        if isinstance(INIT_PASSWD, FunctionType):
1193            new_password = INIT_PASSWD()
1194        else:
1195            new_password = INIT_PASSWD
1196        user.set_password(new_password)
1197        user.save()
1198
1199        if config.FORCE_PASSWORD_CHANGE:
1200            UserOptions.objects.set_force_passwd_change(user.username)
1201
1202        if IS_EMAIL_CONFIGURED:
1203            if SEND_EMAIL_ON_RESETTING_USER_PASSWD:
1204                c = {'email': email, 'password': new_password}
1205                contact_email = Profile.objects.get_contact_email_by_user(email)
1206                try:
1207                    send_html_email(_(u'Password has been reset on %s') % get_site_name(),
1208                                    'sysadmin/user_reset_email.html', c, None, [contact_email])
1209                    reset_tip = _('Successfully reset password to %(passwd)s, an email has been sent to %(user)s.') % \
1210                        {'passwd': new_password, 'user': contact_email}
1211                except Exception as e:
1212                    logger.warning(e)
1213                    reset_tip = _('Successfully reset password to %(passwd)s, but failed to send email to %(user)s, please check your email configuration.') % \
1214                        {'passwd': new_password, 'user': email}
1215            else:
1216                reset_tip = _('Successfully reset password to %(passwd)s for user %(user)s.') % \
1217                    {'passwd': new_password, 'user': email}
1218        else:
1219            reset_tip = _('Successfully reset password to %(passwd)s for user %(user)s. But email notification can not be sent, because Email service is not properly configured.') % \
1220                {'passwd': new_password, 'user': email}
1221
1222        return Response({'new_password': new_password, 'reset_tip': reset_tip})
1223
1224
1225class AdminUserGroups(APIView):
1226
1227    authentication_classes = (TokenAuthentication, SessionAuthentication)
1228    permission_classes = (IsAdminUser, )
1229    throttle_classes = (UserRateThrottle, )
1230
1231    def get(self, request, email):
1232        """ return all groups user joined
1233
1234        Permission checking:
1235        1. Admin user;
1236        """
1237
1238        if not request.user.admin_permissions.can_manage_user():
1239            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
1240
1241        try:
1242            User.objects.get(email=email)
1243        except User.DoesNotExist as e:
1244            logger.error(e)
1245            error_msg = 'User %s not found.' % email
1246            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
1247
1248        groups_info = []
1249        try:
1250            groups = ccnet_api.get_groups(email)
1251        except Exception as e:
1252            logger.error(e)
1253            error_msg = 'Internal Server Error'
1254            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
1255
1256        # Use dict to reduce memcache fetch cost in large for-loop.
1257        nickname_dict = {}
1258        creator_name_set = set([g.creator_name for g in groups])
1259        for e in creator_name_set:
1260            if e not in nickname_dict:
1261                nickname_dict[e] = email2nickname(e)
1262
1263        for group in groups:
1264            isoformat_timestr = timestamp_to_isoformat_timestr(group.timestamp)
1265            group_info = {
1266                "id": group.id,
1267                "name": group.group_name,
1268                "owner_email": group.creator_name,
1269                "owner_name": nickname_dict.get(group.creator_name, ''),
1270                "created_at": isoformat_timestr,
1271                "parent_group_id": group.parent_group_id if is_pro_version() else 0
1272            }
1273            groups_info.append(group_info)
1274
1275            try:
1276                is_group_staff = ccnet_api.check_group_staff(group.id, email)
1277            except Exception as e:
1278                logger.error(e)
1279                error_msg = 'Internal Server Error'
1280                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
1281
1282            if email == group.creator_name:
1283                group_info['role'] = 'Owner'
1284            elif is_group_staff:
1285                group_info['role'] = 'Admin'
1286            else:
1287                group_info['role'] = 'Member'
1288        return Response({'group_list': groups_info})
1289
1290
1291class AdminUserShareLinks(APIView):
1292    authentication_classes = (TokenAuthentication, SessionAuthentication)
1293    permission_classes = (IsAdminUser,)
1294    throttle_classes = (UserRateThrottle,)
1295
1296    def get(self, request, email):
1297        """ Get all shared download links of a user.
1298
1299        Permission checking:
1300        1. only admin can perform this action.
1301        """
1302
1303        if not request.user.admin_permissions.can_manage_user():
1304            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
1305
1306        try:
1307            User.objects.get(email=email)
1308        except User.DoesNotExist as e:
1309            logger.error(e)
1310            error_msg = 'User %s not found.' % email
1311            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
1312
1313        share_links = FileShare.objects.filter(username=email)
1314
1315        links_info = []
1316        for fs in share_links:
1317            link_info = get_user_share_link_info(fs)
1318            links_info.append(link_info)
1319
1320        return Response({'share_link_list': links_info})
1321
1322
1323class AdminUserUploadLinks(APIView):
1324    authentication_classes = (TokenAuthentication, SessionAuthentication)
1325    permission_classes = (IsAdminUser,)
1326    throttle_classes = (UserRateThrottle,)
1327
1328    def get(self, request, email):
1329        """ Get all shared upload links of a user.
1330
1331        Permission checking:
1332        1. only admin can perform this action.
1333        """
1334
1335        if not request.user.admin_permissions.can_manage_user():
1336            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
1337
1338        try:
1339            User.objects.get(email=email)
1340        except User.DoesNotExist as e:
1341            logger.error(e)
1342            error_msg = 'User %s not found.' % email
1343            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
1344
1345        upload_links = UploadLinkShare.objects.filter(username=email)
1346
1347        links_info = []
1348        for fs in upload_links:
1349            link_info = get_user_upload_link_info(fs)
1350            links_info.append(link_info)
1351
1352        return Response({'upload_link_list': links_info})
1353
1354
1355class AdminUserBeSharedRepos(APIView):
1356
1357    authentication_classes = (TokenAuthentication, SessionAuthentication)
1358    throttle_classes = (UserRateThrottle,)
1359    permission_classes = (IsAdminUser,)
1360
1361    def get(self, request, email):
1362        """ List 'all' libraries shared to a user
1363
1364        Permission checking:
1365        1. only admin can perform this action.
1366        """
1367
1368        if not request.user.admin_permissions.can_manage_user():
1369            return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
1370
1371        try:
1372            User.objects.get(email=email)
1373        except User.DoesNotExist as e:
1374            logger.error(e)
1375            error_msg = 'User %s not found.' % email
1376            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
1377
1378        try:
1379            beshared_repos = seafile_api.get_share_in_repo_list(email, -1, -1)
1380        except Exception as e:
1381            logger.error(e)
1382            error_msg = 'Internal Server Error'
1383            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
1384
1385        # Use dict to reduce memcache fetch cost in large for-loop.
1386        nickname_dict = {}
1387        owner_set = set([x.user for x in beshared_repos])
1388        for email in owner_set:
1389            if email not in nickname_dict:
1390                if '@seafile_group' in email:
1391                    group_id = get_group_id_by_repo_owner(email)
1392                    group_name = group_id_to_name(group_id)
1393                    nickname_dict[email] = group_name
1394                else:
1395                    nickname_dict[email] = email2nickname(email)
1396
1397        repos_info = []
1398        for repo in beshared_repos:
1399            repo_info = {}
1400            repo_info['id'] = repo.repo_id
1401            repo_info['name'] = repo.repo_name
1402            repo_info['owner_email'] = repo.user
1403            repo_info['owner_name'] = nickname_dict.get(repo.user, '')
1404            repo_info['size'] = repo.size
1405            repo_info['encrypted'] = repo.encrypted
1406            repo_info['file_count'] = repo.file_count
1407            repo_info['status'] = normalize_repo_status_code(repo.status)
1408            repo_info['last_modify'] = timestamp_to_isoformat_timestr(repo.last_modify)
1409
1410            repos_info.append(repo_info)
1411
1412        return Response({'repo_list': repos_info})
1413
1414
1415class AdminUpdateUserCcnetEmail(APIView):
1416
1417    authentication_classes = (TokenAuthentication, SessionAuthentication)
1418    permission_classes = (IsAdminUser, )
1419    throttle_classes = (UserRateThrottle, )
1420
1421    def put(self, request):
1422        """update ccnet email
1423
1424        Permission checking:
1425        1. only admin can perform this action.
1426        """
1427
1428        # argument check
1429        old_ccnet_email = request.data.get("old_email", None)
1430        if not old_ccnet_email:
1431            error_msg = 'old_email invalid.'
1432            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1433
1434        new_ccnet_email = request.data.get("new_email", None)
1435        if not new_ccnet_email:
1436            error_msg = 'new_email invalid.'
1437            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1438
1439        new_ccnet_email = new_ccnet_email.strip()
1440        if not is_valid_email(new_ccnet_email):
1441            error_msg = 'new_email invalid.'
1442            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1443
1444        # resource check
1445        if not ccnet_api.get_emailuser(old_ccnet_email):
1446            error_msg = 'User %s not found.' % old_ccnet_email
1447            return api_error(status.HTTP_404_NOT_FOUND, error_msg)
1448
1449        if ccnet_api.get_emailuser(new_ccnet_email):
1450            error_msg = "User %s already exists." % new_ccnet_email
1451            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
1452
1453        # update
1454        try:
1455            ccnet_api.update_emailuser_id(old_ccnet_email, new_ccnet_email)
1456            logger.debug('the ccnet database was successfully updated')
1457        except Exception as e:
1458            logger.error(e)
1459            error_msg = 'Internal Server Error'
1460            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
1461
1462        try:
1463            from seahub.api2.models import Token
1464            token_list = Token.objects.filter(user=old_ccnet_email)
1465            for token in token_list:
1466                token.user = new_ccnet_email
1467                token.save()
1468            logger.debug('the api2_token table in seahub database was successfully updated')
1469        except Exception as e:
1470            logger.error(e)
1471            error_msg = 'Internal Server Error'
1472            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
1473
1474        try:
1475            from seahub.api2.models import TokenV2
1476            tokenv2_list = TokenV2.objects.filter(user=old_ccnet_email)
1477            for tokenv2 in tokenv2_list:
1478                tokenv2.user = new_ccnet_email
1479                tokenv2.save()
1480            logger.debug('the api2_tokenv2 table in seahub database was successfully updated')
1481        except Exception as e:
1482            logger.error(e)
1483            error_msg = 'Internal Server Error'
1484            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
1485
1486        try:
1487            from seahub.admin_log.models import AdminLog
1488            adminlog_list = AdminLog.objects.filter(email=old_ccnet_email)
1489            for adminlog in adminlog_list:
1490                adminlog.email = new_ccnet_email
1491                adminlog.save()
1492            logger.debug('the admin_log_adminlog table in seahub database was successfully updated')
1493        except Exception as e:
1494            logger.error(e)
1495
1496        try:
1497            from seahub.avatar.models import Avatar
1498            avatar_list = Avatar.objects.filter(emailuser=old_ccnet_email)
1499            for avatar in avatar_list:
1500                avatar.emailuser = new_ccnet_email
1501                avatar.save()
1502            logger.debug('the avatar_avatar table in seahub database was successfully updated')
1503        except Exception as e:
1504            logger.error(e)
1505
1506        try:
1507            from seahub.base.models import ClientLoginToken
1508            clientlogintoken_list = ClientLoginToken.objects.filter(username=old_ccnet_email)
1509            for clientlogintoken in clientlogintoken_list:
1510                clientlogintoken.username = new_ccnet_email
1511                clientlogintoken.save()
1512            logger.debug('the base_clientlogintoken table in seahub database was successfully updated')
1513        except Exception as e:
1514            logger.error(e)
1515
1516        try:
1517            from seahub.base.models import DeviceToken
1518            devicetoken_list = DeviceToken.objects.filter(user=old_ccnet_email)
1519            for devicetoken in devicetoken_list:
1520                devicetoken.user = new_ccnet_email
1521                devicetoken.save()
1522            logger.debug('the base_devicetoken table in seahub database was successfully updated')
1523        except Exception as e:
1524            logger.error(e)
1525
1526        try:
1527            from seahub.base.models import FileComment
1528            filecomment_list = FileComment.objects.filter(author=old_ccnet_email)
1529            for filecomment in filecomment_list:
1530                filecomment.author = new_ccnet_email
1531                filecomment.save()
1532            logger.debug('the base_filecomment table in seahub database was successfully updated')
1533        except Exception as e:
1534            logger.error(e)
1535
1536        try:
1537            from seahub.base.models import UserLastLogin
1538            userlastlogin_list = UserLastLogin.objects.filter(username=old_ccnet_email)
1539            for userlastlogin in userlastlogin_list:
1540                userlastlogin.username = new_ccnet_email
1541                userlastlogin.save()
1542            logger.debug('the base_userlastlogin table in seahub database was successfully updated')
1543        except Exception as e:
1544            logger.error(e)
1545
1546        try:
1547            from seahub.base.models import UserStarredFiles
1548            userstarredfiles_list = UserStarredFiles.objects.filter(email=old_ccnet_email)
1549            for userstarredfiles in userstarredfiles_list:
1550                userstarredfiles.email = new_ccnet_email
1551                userstarredfiles.save()
1552            logger.debug('the base_userstarredfiles table in seahub database was successfully updated')
1553        except Exception as e:
1554            logger.error(e)
1555
1556        try:
1557            from seahub.drafts.models import Draft
1558            draft_list = Draft.objects.filter(username=old_ccnet_email)
1559            for draft in draft_list:
1560                draft.username = new_ccnet_email
1561                draft.save()
1562            logger.debug('the drafts_draft table in seahub database was successfully updated')
1563        except Exception as e:
1564            logger.error(e)
1565
1566        try:
1567            from seahub.drafts.models import DraftReviewer
1568            draftreviewer_list = DraftReviewer.objects.filter(reviewer=old_ccnet_email)
1569            for draftreviewer in draftreviewer_list:
1570                draftreviewer.reviewer = new_ccnet_email
1571                draftreviewer.save()
1572            logger.debug('the drafts_draftreviewer table in seahub database was successfully updated')
1573        except Exception as e:
1574            logger.error(e)
1575
1576        try:
1577            from seahub.file_participants.models import FileParticipant
1578            fileparticipant_list = FileParticipant.objects.filter(username=old_ccnet_email)
1579            for fileparticipant in fileparticipant_list:
1580                fileparticipant.username = new_ccnet_email
1581                fileparticipant.save()
1582            logger.debug('the file_participants_fileparticipant table in seahub database was successfully updated')
1583        except Exception as e:
1584            logger.error(e)
1585
1586        try:
1587            from seahub.institutions.models import InstitutionAdmin
1588            institutionadmin_list = InstitutionAdmin.objects.filter(user=old_ccnet_email)
1589            for institutionadmin in institutionadmin_list:
1590                institutionadmin.user = new_ccnet_email
1591                institutionadmin.save()
1592            logger.debug('the institutions_institutionadmin table in seahub database was successfully updated')
1593        except Exception as e:
1594            logger.error(e)
1595
1596        try:
1597            from seahub.invitations.models import Invitation
1598            invitation_list = Invitation.objects.filter(inviter=old_ccnet_email)
1599            for invitation in invitation_list:
1600                invitation.inviter = new_ccnet_email
1601                invitation.save()
1602            logger.debug('the invitations_invitation table in seahub database was successfully updated')
1603        except Exception as e:
1604            logger.error(e)
1605
1606        try:
1607            from seahub.notifications.models import UserNotification
1608            usernotification_list = UserNotification.objects.filter(to_user=old_ccnet_email)
1609            for usernotification in usernotification_list:
1610                usernotification.to_user = new_ccnet_email
1611                usernotification.save()
1612            logger.debug('the notifications_usernotification table in seahub database was successfully updated')
1613        except Exception as e:
1614            logger.error(e)
1615
1616        try:
1617            from seahub.options.models import UserOptions
1618            useroptions_list = UserOptions.objects.filter(email=old_ccnet_email)
1619            for useroptions in useroptions_list:
1620                useroptions.email = new_ccnet_email
1621                useroptions.save()
1622            logger.debug('the options_useroptions table in seahub database was successfully updated')
1623        except Exception as e:
1624            logger.error(e)
1625
1626        try:
1627            from seahub.profile.models import DetailedProfile
1628            detailedprofile_list = DetailedProfile.objects.filter(user=old_ccnet_email)
1629            for detailedprofile in detailedprofile_list:
1630                detailedprofile.user = new_ccnet_email
1631                detailedprofile.save()
1632            logger.debug('the profile_detailedprofile table in seahub database was successfully updated')
1633        except Exception as e:
1634            logger.error(e)
1635
1636        try:
1637            from seahub.profile.models import Profile
1638            profile_list = Profile.objects.filter(user=old_ccnet_email)
1639            for profile in profile_list:
1640                profile.user = new_ccnet_email
1641                profile.save()
1642            logger.debug('the profile_profile table in seahub database was successfully updated')
1643        except Exception as e:
1644            logger.error(e)
1645
1646        try:
1647            from seahub.role_permissions.models import AdminRole
1648            adminrole_list = AdminRole.objects.filter(email=old_ccnet_email)
1649            for adminrole in adminrole_list:
1650                adminrole.email = new_ccnet_email
1651                adminrole.save()
1652            logger.debug('the role_permissions_adminrole table in seahub database was successfully updated')
1653        except Exception as e:
1654            logger.error(e)
1655
1656        try:
1657            from seahub.share.models import AnonymousShare
1658            anonymousshare_list = AnonymousShare.objects.filter(repo_owner=old_ccnet_email)
1659            for anonymousshare in anonymousshare_list:
1660                anonymousshare.repo_owner = new_ccnet_email
1661                anonymousshare.save()
1662            logger.debug('the share_anonymousshare table in seahub database was successfully updated')
1663        except Exception as e:
1664            logger.error(e)
1665
1666        try:
1667            from seahub.share.models import FileShare
1668            fileshare_list = FileShare.objects.filter(username=old_ccnet_email)
1669            for fileshare in fileshare_list:
1670                fileshare.username = new_ccnet_email
1671                fileshare.save()
1672            logger.debug('the share_fileshare table in seahub database was successfully updated')
1673        except Exception as e:
1674            logger.error(e)
1675
1676        try:
1677            from seahub.share.models import UploadLinkShare
1678            uploadlinkshare_list = UploadLinkShare.objects.filter(username=old_ccnet_email)
1679            for uploadlinkshare in uploadlinkshare_list:
1680                uploadlinkshare.username = new_ccnet_email
1681                uploadlinkshare.save()
1682            logger.debug('the share_uploadlinkshare table in seahub database was successfully updated')
1683        except Exception as e:
1684            logger.error(e)
1685
1686        try:
1687            from seahub.auth.models import SocialAuthUser
1688            socialauthuser_list = SocialAuthUser.objects.filter(username=old_ccnet_email)
1689            for socialauthuser in socialauthuser_list:
1690                socialauthuser.username = new_ccnet_email
1691                socialauthuser.save()
1692            logger.debug('the social_auth_usersocialauth table in seahub database was successfully updated')
1693        except Exception as e:
1694            logger.error(e)
1695
1696        try:
1697            from seahub_extra.sysadmin_extra.models import UserLoginLog
1698            userlastlogin_list = UserLoginLog.objects.filter(username=old_ccnet_email)
1699            for userlastlogin in userlastlogin_list:
1700                userlastlogin.username = new_ccnet_email
1701                userlastlogin.save()
1702            logger.debug('the sysadmin_extra_userloginlog table in seahub database was successfully updated')
1703        except Exception as e:
1704            logger.error(e)
1705
1706        try:
1707            from seahub.tags.models import FileTag
1708            filetag_list = FileTag.objects.filter(username=old_ccnet_email)
1709            for filetag in filetag_list:
1710                filetag.username = new_ccnet_email
1711                filetag.save()
1712            logger.debug('the tags_filetag table in seahub database was successfully updated')
1713        except Exception as e:
1714            logger.error(e)
1715
1716        try:
1717            from termsandconditions.models import UserTermsAndConditions
1718            usertermsandconditions_list = UserTermsAndConditions.objects.filter(username=old_ccnet_email)
1719            for usertermsandconditions in usertermsandconditions_list:
1720                usertermsandconditions.username = new_ccnet_email
1721                usertermsandconditions.save()
1722            logger.debug('the termsandconditions_usertermsandconditions table in seahub database was successfully updated')
1723        except Exception as e:
1724            logger.error(e)
1725
1726        try:
1727            from seahub.wiki.models import Wiki
1728            wiki_list = Wiki.objects.filter(username=old_ccnet_email)
1729            for wiki in wiki_list:
1730                wiki.username = new_ccnet_email
1731                wiki.save()
1732            logger.debug('the wiki_wiki table in seahub database was successfully updated')
1733        except Exception as e:
1734            logger.error(e)
1735
1736        try:
1737            from seahub.ocm.models import OCMShare
1738            ocmshare_list = OCMShare.objects.filter(from_user=old_ccnet_email)
1739            for ocmshare in ocmshare_list:
1740                ocmshare.from_user = new_ccnet_email
1741                ocmshare.save()
1742            logger.debug('the ocm_share table in seahub database was successfully updated')
1743        except Exception as e:
1744            logger.error(e)
1745
1746        try:
1747            from seahub.ocm.models import OCMShareReceived
1748            ocmsharereceived_list = OCMShareReceived.objects.filter(to_user=old_ccnet_email)
1749            for ocmsharereceived in ocmsharereceived_list:
1750                ocmsharereceived.to_user = new_ccnet_email
1751                ocmsharereceived.save()
1752            logger.debug('the ocm_share_received table in seahub database was successfully updated')
1753        except Exception as e:
1754            logger.error(e)
1755
1756        return Response({'success': True})
1757