mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-07 09:51:26 +00:00
459 lines
18 KiB
Python
459 lines
18 KiB
Python
# Copyright (c) 2012-2019 Seafile Ltd.
|
|
|
|
# encoding: utf-8
|
|
|
|
import logging
|
|
import requests
|
|
import json
|
|
|
|
from seaserv import seafile_api, ccnet_api
|
|
from rest_framework.authentication import SessionAuthentication
|
|
from rest_framework.permissions import IsAdminUser
|
|
from rest_framework.response import Response
|
|
from rest_framework.views import APIView
|
|
from rest_framework import status
|
|
from seahub.api2.authentication import TokenAuthentication
|
|
from seahub.api2.throttling import UserRateThrottle
|
|
from seahub.api2.utils import api_error
|
|
from seahub.api2.permissions import IsProVersion
|
|
|
|
from seahub.work_weixin.utils import handler_work_weixin_api_response, \
|
|
get_work_weixin_access_token, admin_work_weixin_departments_check, \
|
|
update_work_weixin_user_info
|
|
from seahub.work_weixin.settings import WORK_WEIXIN_DEPARTMENTS_URL, \
|
|
WORK_WEIXIN_DEPARTMENT_MEMBERS_URL, WORK_WEIXIN_PROVIDER, WORK_WEIXIN_UID_PREFIX
|
|
from seahub.base.accounts import User
|
|
from seahub.utils.auth import gen_user_virtual_id
|
|
from seahub.auth.models import SocialAuthUser
|
|
from seahub.group.utils import validate_group_name
|
|
from seahub.auth.models import ExternalDepartment
|
|
|
|
logger = logging.getLogger(__name__)
|
|
WORK_WEIXIN_DEPARTMENT_FIELD = 'department'
|
|
WORK_WEIXIN_DEPARTMENT_MEMBERS_FIELD = 'userlist'
|
|
DEPARTMENT_OWNER = 'system admin'
|
|
|
|
|
|
# # uid = corpid + '_' + userid
|
|
# get departments: https://work.weixin.qq.com/api/doc#90000/90135/90208
|
|
# get members: https://work.weixin.qq.com/api/doc#90000/90135/90200
|
|
|
|
|
|
class AdminWorkWeixinDepartments(APIView):
|
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
|
throttle_classes = (UserRateThrottle,)
|
|
permission_classes = (IsAdminUser,)
|
|
|
|
def get(self, request):
|
|
if not admin_work_weixin_departments_check():
|
|
error_msg = 'Feature is not enabled.'
|
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
|
|
|
if not request.user.admin_permissions.can_manage_user():
|
|
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
|
|
|
|
access_token = get_work_weixin_access_token()
|
|
if not access_token:
|
|
logger.error('can not get work weixin access_token')
|
|
error_msg = '获取企业微信组织架构失败'
|
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
|
|
|
data = {
|
|
'access_token': access_token,
|
|
}
|
|
department_id = request.GET.get('department_id', None)
|
|
if department_id:
|
|
data['id'] = department_id
|
|
|
|
api_response = requests.get(WORK_WEIXIN_DEPARTMENTS_URL, params=data)
|
|
api_response_dic = handler_work_weixin_api_response(api_response)
|
|
if not api_response_dic:
|
|
logger.error('can not get work weixin departments response')
|
|
error_msg = '获取企业微信组织架构失败'
|
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
|
|
|
if WORK_WEIXIN_DEPARTMENT_FIELD not in api_response_dic:
|
|
logger.error(json.dumps(api_response_dic))
|
|
logger.error('can not get department list in work weixin departments response')
|
|
error_msg = '获取企业微信组织架构失败'
|
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
|
|
|
return Response(api_response_dic)
|
|
|
|
|
|
class AdminWorkWeixinDepartmentMembers(APIView):
|
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
|
throttle_classes = (UserRateThrottle,)
|
|
permission_classes = (IsAdminUser,)
|
|
|
|
def get(self, request, department_id):
|
|
if not admin_work_weixin_departments_check():
|
|
error_msg = 'Feature is not enabled.'
|
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
|
|
|
if not request.user.admin_permissions.can_manage_user():
|
|
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
|
|
|
|
access_token = get_work_weixin_access_token()
|
|
if not access_token:
|
|
logger.error('can not get work weixin access_token')
|
|
error_msg = '获取企业微信组织架构成员失败'
|
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
|
|
|
data = {
|
|
'access_token': access_token,
|
|
'department_id': department_id,
|
|
}
|
|
fetch_child = request.GET.get('fetch_child', None)
|
|
if fetch_child:
|
|
if fetch_child not in ('true', 'false'):
|
|
error_msg = 'fetch_child invalid.'
|
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
|
data['fetch_child'] = 1 if fetch_child == 'true' else 0
|
|
|
|
api_response = requests.get(WORK_WEIXIN_DEPARTMENT_MEMBERS_URL, params=data)
|
|
api_response_dic = handler_work_weixin_api_response(api_response)
|
|
if not api_response_dic:
|
|
logger.error('can not get work weixin department members response')
|
|
error_msg = '获取企业微信组织架构成员失败'
|
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
|
|
|
if WORK_WEIXIN_DEPARTMENT_MEMBERS_FIELD not in api_response_dic:
|
|
logger.error(json.dumps(api_response_dic))
|
|
logger.error('can not get userlist in work weixin department members response')
|
|
error_msg = '获取企业微信组织架构成员失败'
|
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
|
|
|
api_user_list = api_response_dic[WORK_WEIXIN_DEPARTMENT_MEMBERS_FIELD]
|
|
# todo filter ccnet User database
|
|
social_auth_queryset = SocialAuthUser.objects.filter(
|
|
provider=WORK_WEIXIN_PROVIDER, uid__contains=WORK_WEIXIN_UID_PREFIX)
|
|
for api_user in api_user_list:
|
|
uid = WORK_WEIXIN_UID_PREFIX + api_user.get('userid', '')
|
|
api_user['contact_email'] = api_user['email']
|
|
# # determine the user exists
|
|
if social_auth_queryset.filter(uid=uid).exists():
|
|
api_user['email'] = social_auth_queryset.get(uid=uid).username
|
|
else:
|
|
api_user['email'] = ''
|
|
|
|
return Response(api_response_dic)
|
|
|
|
|
|
def _handler_work_weixin_user_data(api_user, social_auth_queryset):
|
|
user_id = api_user.get('userid', '')
|
|
uid = WORK_WEIXIN_UID_PREFIX + user_id
|
|
name = api_user.get('name', None)
|
|
error_data = None
|
|
if not uid:
|
|
error_data = {
|
|
'userid': None,
|
|
'name': None,
|
|
'error_msg': 'userid invalid.',
|
|
}
|
|
elif social_auth_queryset.filter(uid=uid).exists():
|
|
error_data = {
|
|
'userid': user_id,
|
|
'name': name,
|
|
'error_msg': '用户已存在',
|
|
}
|
|
|
|
return error_data
|
|
|
|
|
|
def _import_user_from_work_weixin(email, api_user):
|
|
api_user['username'] = email
|
|
uid = WORK_WEIXIN_UID_PREFIX + api_user.get('userid')
|
|
try:
|
|
User.objects.create_user(email)
|
|
SocialAuthUser.objects.add(email, WORK_WEIXIN_PROVIDER, uid)
|
|
update_work_weixin_user_info(api_user)
|
|
except Exception as e:
|
|
logger.error(e)
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
class AdminWorkWeixinUsersBatch(APIView):
|
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
|
permission_classes = (IsAdminUser,)
|
|
throttle_classes = (UserRateThrottle,)
|
|
|
|
def post(self, request):
|
|
if not admin_work_weixin_departments_check():
|
|
error_msg = 'Feature is not enabled.'
|
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
|
|
|
if not request.user.admin_permissions.can_manage_user():
|
|
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
|
|
|
|
api_user_list = request.data.get(WORK_WEIXIN_DEPARTMENT_MEMBERS_FIELD, None)
|
|
if not api_user_list or not isinstance(api_user_list, list):
|
|
error_msg = 'userlist invalid.'
|
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
|
|
|
success = []
|
|
failed = []
|
|
social_auth_queryset = SocialAuthUser.objects.filter(
|
|
provider=WORK_WEIXIN_PROVIDER, uid__contains=WORK_WEIXIN_UID_PREFIX)
|
|
|
|
for api_user in api_user_list:
|
|
|
|
error_data = _handler_work_weixin_user_data(api_user, social_auth_queryset)
|
|
if not error_data:
|
|
email = gen_user_virtual_id()
|
|
if _import_user_from_work_weixin(email, api_user):
|
|
success.append({
|
|
'userid': api_user.get('userid'),
|
|
'name': api_user.get('name'),
|
|
'email': email,
|
|
})
|
|
else:
|
|
failed.append({
|
|
'userid': api_user.get('userid'),
|
|
'name': api_user.get('name'),
|
|
'error_msg': '导入失败'
|
|
})
|
|
else:
|
|
failed.append(error_data)
|
|
|
|
return Response({'success': success, 'failed': failed})
|
|
|
|
|
|
class AdminWorkWeixinDepartmentsImport(APIView):
|
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
|
throttle_classes = (UserRateThrottle,)
|
|
permission_classes = (IsAdminUser,)
|
|
|
|
def _list_departments_from_work_weixin(self, access_token, department_id):
|
|
# https://work.weixin.qq.com/api/doc/90000/90135/90208
|
|
data = {
|
|
'access_token': access_token,
|
|
'id': department_id,
|
|
}
|
|
api_response = requests.get(WORK_WEIXIN_DEPARTMENTS_URL, params=data)
|
|
api_response_dic = handler_work_weixin_api_response(api_response)
|
|
|
|
if not api_response_dic:
|
|
logger.error('can not get work weixin departments response')
|
|
return None
|
|
|
|
if WORK_WEIXIN_DEPARTMENT_FIELD not in api_response_dic:
|
|
logger.error(json.dumps(api_response_dic))
|
|
logger.error('can not get department list in work weixin departments response')
|
|
return None
|
|
|
|
return api_response_dic[WORK_WEIXIN_DEPARTMENT_FIELD]
|
|
|
|
def _list_department_members_from_work_weixin(self, access_token, department_id):
|
|
data = {
|
|
'access_token': access_token,
|
|
'department_id': department_id,
|
|
'fetch_child': 1,
|
|
}
|
|
api_response = requests.get(WORK_WEIXIN_DEPARTMENT_MEMBERS_URL, params=data)
|
|
api_response_dic = handler_work_weixin_api_response(api_response)
|
|
|
|
if not api_response_dic:
|
|
logger.error('can not get work weixin department members response')
|
|
return None
|
|
|
|
if WORK_WEIXIN_DEPARTMENT_MEMBERS_FIELD not in api_response_dic:
|
|
logger.error(json.dumps(api_response_dic))
|
|
logger.error('can not get userlist in work weixin department members response')
|
|
return None
|
|
|
|
return api_response_dic[WORK_WEIXIN_DEPARTMENT_MEMBERS_FIELD]
|
|
|
|
def _api_department_success_msg(self, department_obj_id, department_obj_name, group_id):
|
|
return {
|
|
'type': 'department',
|
|
'department_id': department_obj_id,
|
|
'department_name': department_obj_name,
|
|
'group_id': group_id,
|
|
}
|
|
|
|
def _api_department_failed_msg(self, department_obj_id, department_obj_name, msg):
|
|
return {
|
|
'type': 'department',
|
|
'department_id': department_obj_id,
|
|
'department_name': department_obj_name,
|
|
'msg': msg,
|
|
}
|
|
|
|
def _api_user_success_msg(self, email, api_user_name, department_obj_id, group_id):
|
|
return {
|
|
'type': 'user',
|
|
'email': email,
|
|
'api_user_name': api_user_name,
|
|
'department_id': department_obj_id,
|
|
'group_id': group_id,
|
|
}
|
|
|
|
def _api_user_failed_msg(self, email, api_user_name, department_obj_id, msg):
|
|
return {
|
|
'type': 'user',
|
|
'email': email,
|
|
'api_user_name': api_user_name,
|
|
'department_id': department_obj_id,
|
|
'msg': msg,
|
|
}
|
|
|
|
def post(self, request):
|
|
"""import department from work weixin
|
|
|
|
permission: IsProVersion
|
|
"""
|
|
|
|
if not request.user.admin_permissions.can_manage_user():
|
|
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
|
|
|
|
# argument check
|
|
department_id = request.data.get('work_weixin_department_id')
|
|
try:
|
|
department_id = int(department_id)
|
|
except Exception as e:
|
|
logger.error(e)
|
|
error_msg = 'work_weixin_department_ids invalid.'
|
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
|
|
|
# is pro version and work weixin check
|
|
if not IsProVersion or not admin_work_weixin_departments_check():
|
|
error_msg = 'Feature is not enabled.'
|
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
|
|
|
access_token = get_work_weixin_access_token()
|
|
if not access_token:
|
|
logger.error('can not get work weixin access_token')
|
|
error_msg = '获取企业微信组织架构失败'
|
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
|
|
|
# list departments from work weixin
|
|
api_department_list = self._list_departments_from_work_weixin(access_token, department_id)
|
|
if api_department_list is None:
|
|
error_msg = '获取企业微信组织架构失败'
|
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
|
api_department_list = sorted(api_department_list, key=lambda x:x['id'])
|
|
|
|
# list department members from work weixin
|
|
api_user_list = self._list_department_members_from_work_weixin(access_token, department_id)
|
|
if api_user_list is None:
|
|
error_msg = '获取企业微信组织架构成员失败'
|
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
|
|
|
# main
|
|
success = list()
|
|
failed = list()
|
|
department_map_to_group_dict = dict()
|
|
|
|
for index, department_obj in enumerate(api_department_list):
|
|
# check department argument
|
|
new_group_name = department_obj.get('name')
|
|
department_obj_id = department_obj.get('id')
|
|
parent_department_id = department_obj.get('parentid', 0)
|
|
if department_obj_id is None or not new_group_name or not validate_group_name(new_group_name):
|
|
failed_msg = self._api_department_failed_msg(
|
|
department_obj_id, new_group_name, '部门参数错误')
|
|
failed.append(failed_msg)
|
|
continue
|
|
|
|
# check parent group
|
|
if index == 0:
|
|
parent_group_id = -1
|
|
else:
|
|
parent_group_id = department_map_to_group_dict.get(parent_department_id)
|
|
|
|
if parent_group_id is None:
|
|
failed_msg = self._api_department_failed_msg(
|
|
department_obj_id, new_group_name, '父级部门不存在')
|
|
failed.append(failed_msg)
|
|
continue
|
|
|
|
# check department exist
|
|
exist_department = ExternalDepartment.objects.get_by_provider_and_outer_id(
|
|
WORK_WEIXIN_PROVIDER, department_obj_id)
|
|
if exist_department:
|
|
department_map_to_group_dict[department_obj_id] = exist_department.group_id
|
|
failed_msg = self._api_department_failed_msg(
|
|
department_obj_id, new_group_name, '部门已存在')
|
|
failed.append(failed_msg)
|
|
continue
|
|
|
|
# import department
|
|
try:
|
|
group_id = ccnet_api.create_group(
|
|
new_group_name, DEPARTMENT_OWNER, parent_group_id=parent_group_id)
|
|
|
|
seafile_api.set_group_quota(group_id, -2)
|
|
|
|
ExternalDepartment.objects.create(
|
|
group_id=group_id,
|
|
provider=WORK_WEIXIN_PROVIDER,
|
|
outer_id=department_obj_id,
|
|
outer_parent_id=parent_department_id,
|
|
)
|
|
|
|
department_map_to_group_dict[department_obj_id] = group_id
|
|
success_msg = self._api_department_success_msg(
|
|
department_obj_id, new_group_name, group_id)
|
|
success.append(success_msg)
|
|
except Exception as e:
|
|
logger.error(e)
|
|
failed_msg = self._api_department_failed_msg(
|
|
department_obj_id, new_group_name, '部门导入失败')
|
|
failed.append(failed_msg)
|
|
|
|
# todo filter ccnet User database
|
|
social_auth_queryset = SocialAuthUser.objects.filter(
|
|
provider=WORK_WEIXIN_PROVIDER, uid__contains=WORK_WEIXIN_UID_PREFIX)
|
|
|
|
# import api_user
|
|
for api_user in api_user_list:
|
|
uid = WORK_WEIXIN_UID_PREFIX + api_user.get('userid', '')
|
|
api_user['contact_email'] = api_user['email']
|
|
api_user_name = api_user.get('name')
|
|
|
|
# determine the user exists
|
|
if social_auth_queryset.filter(uid=uid).exists():
|
|
email = social_auth_queryset.get(uid=uid).username
|
|
else:
|
|
# create user
|
|
email = gen_user_virtual_id()
|
|
create_user_success = _import_user_from_work_weixin(email, api_user)
|
|
if not create_user_success:
|
|
failed_msg = self._api_user_failed_msg(
|
|
'', api_user_name, department_id, '导入用户失败')
|
|
failed.append(failed_msg)
|
|
continue
|
|
|
|
# bind user to department
|
|
api_user_department_list = api_user.get('department')
|
|
for department_obj_id in api_user_department_list:
|
|
group_id = department_map_to_group_dict.get(department_obj_id)
|
|
if group_id is None:
|
|
# the api_user also exist in the brother department which not import
|
|
continue
|
|
|
|
if ccnet_api.is_group_user(group_id, email):
|
|
failed_msg = self._api_user_failed_msg(
|
|
email, api_user_name, department_obj_id, '部门成员已存在')
|
|
failed.append(failed_msg)
|
|
continue
|
|
|
|
try:
|
|
ccnet_api.group_add_member(group_id, DEPARTMENT_OWNER, email)
|
|
success_msg = self._api_user_success_msg(
|
|
email, api_user_name, department_obj_id, group_id)
|
|
success.append(success_msg)
|
|
except Exception as e:
|
|
logger.error(e)
|
|
failed_msg = self._api_user_failed_msg(
|
|
email, api_user_name, department_id, '导入部门成员失败')
|
|
failed.append(failed_msg)
|
|
|
|
return Response({
|
|
'success': success,
|
|
'failed': failed,
|
|
})
|