338 lines
14 KiB
Python
338 lines
14 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
from urllib.parse import quote
|
|
import json
|
|
import uuid
|
|
import logging
|
|
|
|
from odoo import http
|
|
from odoo.http import request
|
|
from odoo.exceptions import AccessDenied
|
|
from werkzeug.wrappers import Response as WerkzeugResponse
|
|
|
|
from ..utils.common import WeChatOAuthException
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class Home(http.Controller):
|
|
@http.route('/WW_verify_fC7AX158Ffby8Ed1.txt', type='http', auth="public")
|
|
def WW_verify_UP42BAWNwtWK60RH(self):
|
|
return 'fC7AX158Ffby8Ed1'
|
|
|
|
@http.route('/wechat/login', type='http', auth="none", methods=['GET'])
|
|
def wechat_login(self):
|
|
base_url = request.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
|
redirect_uri = quote(f'{base_url}/wechat/login_by_code')
|
|
redirect_uri = redirect_uri.replace(' ', '%20')
|
|
appid = 'wwc955c117b738b052'
|
|
agent_id = '1000120'
|
|
url = f'https://open.weixin.qq.com/connect/oauth2/authorize?appid={appid}&redirect_uri={redirect_uri}&response_type=code&scope=snsapi_base&state=STATE&agentid={agent_id}#wechat_redirect'
|
|
return http.request.redirect(url, 301, False)
|
|
|
|
@http.route('/wechat/login_by_code', type='http', auth="none", methods=['GET'])
|
|
def wechat_login_by_code(self, code, state=None, **kw):
|
|
"""
|
|
企业微信OAuth回调接口,支持state参数重定向到目标URL
|
|
参数:
|
|
code: 企业微信OAuth授权码
|
|
state: 目标URL(编码后的),登录成功后重定向到此URL
|
|
"""
|
|
provider = request.env.ref('yuthon_wechat.wechat_corp_web_auth_oauth_provider')
|
|
(db, login, oauth_code) = request.env['res.users'].sudo().auth_oauth_wechat(provider.sudo(), {'code': code})
|
|
|
|
# 如果没有state参数,返回原始值,保持与原本方法行为一致
|
|
if not state:
|
|
return (db, login, oauth_code)
|
|
|
|
# 如果有state参数,执行登录并重定向到目标URL
|
|
try:
|
|
# 查找用户并创建session
|
|
user = request.env['res.users'].sudo().search([('login', '=', login)], limit=1)
|
|
if not user:
|
|
_logger.error(f"OAuth登录后未找到用户: {login}")
|
|
return http.request.redirect('/web/login?error=user_not_found', 302)
|
|
|
|
# 创建session
|
|
dbname = request.env.cr.dbname
|
|
try:
|
|
uid = request.session.authenticate(dbname, user.login, user.login)
|
|
except Exception:
|
|
# 如果authenticate失败,直接设置session
|
|
request.session.uid = user.id
|
|
request.session.login = user.login
|
|
request.session.db = dbname
|
|
uid = user.id
|
|
|
|
_logger.info(f"企业微信OAuth登录成功: 用户 {user.login} (ID: {user.id})")
|
|
|
|
# 解码并重定向到目标URL
|
|
try:
|
|
from urllib.parse import unquote
|
|
target_url = unquote(state)
|
|
# 确保是相对路径或同域名
|
|
if target_url.startswith('http') and 'oa.thtzjt.com' not in target_url:
|
|
# 如果不是同域名,默认重定向到/web
|
|
target_url = '/web'
|
|
_logger.info(f"OAuth登录成功,重定向到: {target_url}")
|
|
return http.request.redirect(target_url, 302)
|
|
except Exception as e:
|
|
_logger.error(f"解析state参数失败: {str(e)}")
|
|
return http.request.redirect('/web', 302)
|
|
|
|
except AccessDenied as e:
|
|
_logger.error(f"企业微信OAuth登录失败: {str(e)}")
|
|
return http.request.redirect('/web/login?error=access_denied', 302)
|
|
except Exception as e:
|
|
_logger.exception(f"企业微信OAuth回调处理异常: {str(e)}")
|
|
return http.request.redirect('/web/login?error=oauth_error', 302)
|
|
|
|
@http.route('/wechat/login_by_user_id', type='http', auth="none", methods=['GET', 'POST'], csrf=False)
|
|
def wechat_login_by_user_id(self, user_id=None, login=None, **kw):
|
|
"""
|
|
免登录接口:通过用户ID或登录名直接登录,绕过密码校验
|
|
/wechat/login_by_user_id?user_id=2&redirect_url=/web#action=3584
|
|
/wechat/login_by_user_id?user_id=2&redirect_url=/web#action=3584
|
|
https://oa.thtzjt.com/web#action=3584&cids=3-1-2-4&menu_id=9668
|
|
参数:
|
|
user_id: 用户ID (res.users的id)
|
|
login: 用户登录名 (res.users的login字段)
|
|
|
|
返回:
|
|
JSON格式的响应,包含登录结果
|
|
"""
|
|
try:
|
|
# 优先使用user_id,如果没有则使用login查找
|
|
if user_id:
|
|
try:
|
|
user_id = int(user_id)
|
|
user = request.env['res.users'].sudo().browse(user_id)
|
|
except (ValueError, TypeError):
|
|
return json.dumps({
|
|
'success': False,
|
|
'message': '用户ID格式错误'
|
|
}), 200, [('Content-Type', 'application/json')]
|
|
elif login:
|
|
user = request.env['res.users'].sudo().search([('login', '=', login)], limit=1)
|
|
else:
|
|
return json.dumps({
|
|
'success': False,
|
|
'message': '请提供user_id或login参数'
|
|
}), 200, [('Content-Type', 'application/json')]
|
|
|
|
# 验证用户是否存在且激活
|
|
if not user or not user.exists():
|
|
return json.dumps({
|
|
'success': False,
|
|
'message': '用户不存在'
|
|
}), 200, [('Content-Type', 'application/json')]
|
|
|
|
if not user.active:
|
|
return json.dumps({
|
|
'success': False,
|
|
'message': '用户已被禁用'
|
|
}), 200, [('Content-Type', 'application/json')]
|
|
|
|
# 直接设置session来绕过密码验证
|
|
dbname = request.env.cr.dbname
|
|
|
|
# 方法1: 尝试使用authenticate(如果用户密码恰好是login)
|
|
try:
|
|
uid = request.session.authenticate(dbname, user.login, user.login)
|
|
except Exception:
|
|
# 方法2: 如果authenticate失败,直接设置session
|
|
# 这是绕过密码验证的关键步骤
|
|
request.session.uid = user.id
|
|
request.session.login = user.login
|
|
request.session.db = dbname
|
|
uid = user.id
|
|
|
|
_logger.info(f"免登录接口:用户 {user.login} (ID: {user.id}) 登录成功")
|
|
|
|
# 重定向到主页或返回成功信息
|
|
redirect_url = kw.get('redirect_url', '/web')
|
|
if kw.get('return_json', 'false').lower() == 'true':
|
|
return json.dumps({
|
|
'success': True,
|
|
'message': '登录成功',
|
|
'user_id': user.id,
|
|
'login': user.login,
|
|
'name': user.name
|
|
}), 200, [('Content-Type', 'application/json')]
|
|
else:
|
|
return http.request.redirect(redirect_url, 302)
|
|
|
|
except Exception as e:
|
|
_logger.exception(f"免登录接口错误: {str(e)}")
|
|
return json.dumps({
|
|
'success': False,
|
|
'message': f'登录失败: {str(e)}'
|
|
}), 200, [('Content-Type', 'application/json')]
|
|
|
|
@http.route('/phone/wechat/login', type='http', auth="none", csrf=False, cors='*', methods=['POST'])
|
|
def phone_wechat_login(self, **kw):
|
|
"""手机端企业微信OAuth登录接口"""
|
|
try:
|
|
code = kw.get('code')
|
|
if not code:
|
|
return request.make_response(
|
|
json.dumps({
|
|
'code': 400,
|
|
'message': '缺少code参数',
|
|
'data': None
|
|
}),
|
|
headers=[('Content-Type', 'application/json')]
|
|
)
|
|
|
|
try:
|
|
provider = request.env.ref('yuthon_wechat.wechat_corp_web_auth_oauth_provider').sudo()
|
|
except Exception as e:
|
|
_logger.error(f"获取企业微信OAuth provider失败: {str(e)}")
|
|
return request.make_response(
|
|
json.dumps({
|
|
'code': 500,
|
|
'message': '企业微信配置错误',
|
|
'data': None
|
|
}),
|
|
headers=[('Content-Type', 'application/json')]
|
|
)
|
|
|
|
try:
|
|
(db, login, oauth_code) = request.env['res.users'].sudo().auth_oauth_wechat(provider, {'code': code})
|
|
except AccessDenied as e:
|
|
_logger.warning(f"企业微信登录失败: {str(e)}")
|
|
return request.make_response(
|
|
json.dumps({
|
|
'code': 403,
|
|
'message': str(e) or '未找到对应的用户账号',
|
|
'data': None
|
|
}),
|
|
headers=[('Content-Type', 'application/json')]
|
|
)
|
|
except Exception as e:
|
|
_logger.error(f"企业微信OAuth认证异常: {str(e)}", exc_info=True)
|
|
return request.make_response(
|
|
json.dumps({
|
|
'code': 500,
|
|
'message': f'企业微信认证失败: {str(e)}',
|
|
'data': None
|
|
}),
|
|
headers=[('Content-Type', 'application/json')]
|
|
)
|
|
|
|
user = request.env['res.users'].sudo().search([('login', '=', login)], limit=1)
|
|
if not user:
|
|
_logger.warning(f"未找到用户: {login}")
|
|
return request.make_response(
|
|
json.dumps({
|
|
'code': 404,
|
|
'message': '未找到对应的用户账号',
|
|
'data': None
|
|
}),
|
|
headers=[('Content-Type', 'application/json')]
|
|
)
|
|
|
|
employee = user.with_company(user.company_id.id).employee_id
|
|
if not employee:
|
|
oauth_uid = user.oauth_uid
|
|
if oauth_uid:
|
|
employee = request.env['hr.employee'].sudo().search([
|
|
('wecom_userid', '=', oauth_uid)
|
|
], limit=1)
|
|
|
|
if not employee:
|
|
_logger.warning(f"未找到对应的员工信息, user: {login}")
|
|
return request.make_response(
|
|
json.dumps({
|
|
'code': 404,
|
|
'message': '未找到对应的员工信息',
|
|
'data': None
|
|
}),
|
|
headers=[('Content-Type', 'application/json')]
|
|
)
|
|
# access_list = []
|
|
# applet_security_setting_ids = request.env['applet.security'].search(
|
|
# [('employee_id', '=', employee.id)]).applet_security_setting_ids
|
|
# access_list = [access.name for access in applet_security_setting_ids]
|
|
# _logger.info(f"企业微信登录用户{user.name},权限组{access_list}---")
|
|
|
|
applet_security_setting_ids = request.env['applet.security'].sudo().search(
|
|
[('employee_id', '=', employee.id)]).applet_security_setting_ids
|
|
access_list = [access.name for access in applet_security_setting_ids]
|
|
_logger.info(f"企业微信登录用户{user.name},权限组{access_list}---")
|
|
|
|
company_list = []
|
|
for company in user.company_ids:
|
|
company_dict = {
|
|
'state': 1,
|
|
'company_id': company.id,
|
|
'company_name': company.name,
|
|
}
|
|
company_list.append(company_dict)
|
|
|
|
token = str(uuid.uuid4())
|
|
|
|
data = {
|
|
'name': employee.name,
|
|
'id': user.id,
|
|
'access': access_list,
|
|
'phone': employee.mobile_phone,
|
|
'company_data': company_list,
|
|
'token': token,
|
|
}
|
|
|
|
return request.make_response(
|
|
json.dumps({
|
|
'code': 200,
|
|
'message': '',
|
|
'data': data
|
|
}),
|
|
headers=[('Content-Type', 'application/json')]
|
|
)
|
|
|
|
except Exception as e:
|
|
_logger.error(f"企业微信登录异常: {str(e)}", exc_info=True)
|
|
return request.make_response(
|
|
json.dumps({
|
|
'code': 500,
|
|
'message': f'登录失败: {str(e)}',
|
|
'data': None
|
|
}),
|
|
headers=[('Content-Type', 'application/json')]
|
|
)
|
|
|
|
@http.route([
|
|
'/phone/wechat/callback',
|
|
'/phone/wechat/callback/<string:v_route>'
|
|
], type='http', auth="none", csrf=False, cors='*', methods=['GET'])
|
|
def phone_wechat_callback(self, code=None, state=None, v_route='Workbench', **kw):
|
|
try:
|
|
frontend_base_url = 'https://phone.thtzjt.com'
|
|
if code:
|
|
redirect_url = f"{frontend_base_url}/phone/WechatCallback?code={code}&v_route={v_route}"
|
|
if state:
|
|
redirect_url += f"&state={state}"
|
|
_logger.info(f"手机端OAuth回调,重定向到: {redirect_url}")
|
|
else:
|
|
redirect_url = f"{frontend_base_url}/phone/Login"
|
|
_logger.warning("手机端OAuth回调缺少code参数,重定向到登录页")
|
|
|
|
response = WerkzeugResponse(
|
|
'',
|
|
status=302,
|
|
headers=[('Location', redirect_url)]
|
|
)
|
|
|
|
return response
|
|
|
|
except Exception as e:
|
|
_logger.error(f"企业微信回调处理异常: {str(e)}", exc_info=True)
|
|
frontend_base_url = 'https://phone.thtzjt.com'
|
|
redirect_url = f"{frontend_base_url}/phone/Login"
|
|
|
|
return WerkzeugResponse(
|
|
'',
|
|
status=302,
|
|
headers=[('Location', redirect_url)]
|
|
)
|