Files
gzth/yuthon_wechat/models/res_users.py
T
2026-02-23 12:05:53 +08:00

190 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
import werkzeug.urls
from urllib.parse import urlparse
import urllib.request
import requests
import json
from odoo import api, fields, models
from odoo.tools.misc import ustr
from odoo.exceptions import AccessDenied
from odoo.http import request
from odoo.addons.auth_signup.models.res_users import SignupError
from odoo.addons.auth_oauth.controllers.main import OAuthLogin
import logging
from ..utils.common import WeChatOAuthException
_logger = logging.getLogger(__name__)
class OAuthLoginWechat(OAuthLogin):
def list_providers(self):
providers = super(OAuthLoginWechat, self).list_providers()
for provider in providers:
if provider['auth_endpoint'].find('weixin') >= 0:
# 对微信的回调链接做特殊处理
# 企业微信扫码登录链接 https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=CORPID&agentid=AGENTID&redirect_uri=REDIRECT_URI&state=STATE
# 企业微信内直接登录链接 https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect
# 如果auth_endpoint链接中含有 “?create_user” 字样,则自动创建用户
wechat_url = provider['auth_endpoint']
create_user = False
auth_endpoint = wechat_url.split('?')
if len(auth_endpoint) >= 2:
wechat_url = auth_endpoint[0]
create_user = auth_endpoint[1].find('create_user') >= 0
client_ids = provider['client_id'].split(' ')
return_url = request.httprequest.url_root + 'auth_oauth/signin'
state = self.get_state(provider)
if create_user:
state['c'] = {'wechat_create_user': True}
params = dict(
appid=client_ids[0].strip(),
agentid=client_ids[1].strip(),
redirect_uri=return_url,
state=json.dumps(state),
)
if wechat_url.find('authorize') >= 0: # 企业微信内直接登录
params.update({
'response_type': 'code',
'scope': 'snsapi_base',
})
provider['auth_link'] = "%s?%s#wechat_redirect" % (wechat_url, werkzeug.urls.url_encode(params))
else:
provider['auth_link'] = "%s?%s" % (wechat_url, werkzeug.urls.url_encode(params))
return providers
class ResUsers(models.Model):
_inherit = 'res.users'
wecom_fields = fields.Char(string="免登录验证字段", compute='_compute_wecom_fields', stroe=True)
@api.depends('login')
def _compute_wecom_fields(self):
for i in self:
i.wecom_fields = i.login
@api.model
def auth_oauth(self, provider, params):
oauth_provider = self.env['auth.oauth.provider'].browse(provider)
if oauth_provider.auth_endpoint.find('weixin') >= 0: # 微信认证
return self.auth_oauth_wechat(oauth_provider, params)
else:
return super(ResUsers, self).auth_oauth(provider, params)
@api.model
def auth_oauth_wechat(self, provider, params):
# 微信获取访问用户身份的URL https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
# 微信获取access_token的URL https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
#
#
def gettoken(url, corpid, corpsecret):
url_token = "%s?corpid=%s&corpsecret=%s" % (url, corpid, corpsecret)
headers = {'Content-Type': 'application/json'}
response = requests.get(url_token, headers=headers)
dict_token = response.json()
if dict_token["errcode"] == 0:
return dict_token["access_token"]
else:
raise OAuthLogin(
"微信获取access_token错误:err_code=%s, err_msg=%s" % (dict_token["errcode"], dict_token["errmsg"]))
client_ids = provider.client_id.split(' ')
if not client_ids or len(client_ids) != 3:
raise OAuthLogin("请正确设置微信登录验证的corpid agentid corpsecret!")
corpid = client_ids[0]
corpsecret = client_ids[2]
access_token = provider.scope
code = params.get('code', False)
if not access_token:
access_token = gettoken(provider.validation_endpoint, corpid, corpsecret)
if not code:
raise OAuthLogin("微信扫码错误:没有 code!")
provider.scope = access_token
oauth_uid = False
headers = {'Content-Type': 'application/json'}
user_url = "%s?access_token=%s&code=%s" % (provider.data_endpoint, access_token, code)
response = requests.get(user_url, headers=headers)
dict_user = response.json()
errcode = dict_user.get("errcode", 0)
if errcode in [42001]:
response = requests.get(user_url, headers=headers)
dict_user = response.json()
_logger.warning("------------------" + str(dict_user))
if dict_user["errcode"] == 0:
oauth_uid = dict_user["UserId"]
elif dict_user["errcode"] == 40014 or dict_user[
"errcode"] == 42001: # 40014: 不合法的access_token, 42001: access_token expired
access_token = gettoken(provider.validation_endpoint, corpid, corpsecret)
provider.scope = access_token
user_url = "%s?access_token=%s&code=%s" % (provider.data_endpoint, access_token, code)
response = requests.get(user_url, headers=headers)
dict_user = response.json()
_logger.warning("------------------" + str(dict_user))
oauth_uid = dict_user["UserId"]
else:
raise WeChatOAuthException(dict_user["errcode"], "微信获取访问用户身份错误:err_code=%s, err_msg=%s" % (dict_user["errcode"], dict_user["errmsg"]))
# raise OAuthLogin(
# "微信获取访问用户身份错误:err_code=%s, err_msg=%s" % (dict_user["errcode"], dict_user["errmsg"]))
user_id = self.search([('oauth_uid', '=', oauth_uid), ('oauth_uid', '!=', False)], limit=1)
if not user_id:
if self.env.context.get('wechat_create_user', False): # 自动创建企业微信User
user_id = self.create_from_wechat(provider, access_token, oauth_uid, code)
if not user_id:
_logger.exception(
"OAuth Wechat: 微信访问用户【UserId:%s】没有关联Odoo账号,且自动创建失败!" % (oauth_uid,))
raise AccessDenied("微信访问用户【UserId:%s】没有关联系统账号!" % (oauth_uid,))
else:
_logger.exception("OAuth Wechat: 微信访问用户【UserId:%s】没有关联Odoo账号!" % (oauth_uid,))
raise AccessDenied("微信访问用户【UserId:%s】没有关联系统账号!" % (oauth_uid,))
else:
user_id.oauth_access_token = code
return (self.env.cr.dbname, user_id.login, code)
@api.model
def create_from_wechat(self, provider, access_token, userid, code):
# 读取企业通讯录的成员详情: https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&userid=USERID
headers = {'Content-Type': 'application/json'}
user_url = "https://qyapi.weixin.qq.com/cgi-bin/user/get"
user_url = "%s?access_token=%s&userid=%s" % (user_url, access_token, userid)
response = requests.get(user_url, headers=headers)
dict_user = response.json()
if dict_user["errcode"] == 0:
mobile = dict_user.get("mobile", '')
name = dict_user.get("name", '')
email = dict_user.get("email", '')
telephone = dict_user.get("telephone", '')
values = {
'login': email or mobile or userid,
'name': name or userid,
'mobile': mobile,
'email': email,
'phone': telephone,
'oauth_uid': userid,
'oauth_provider_id': provider.id,
'oauth_access_token': code,
'active': True,
}
template_user = self.env.ref("base.default_user")
try:
with self.env.cr.savepoint():
user_id = template_user.sudo().with_context(no_reset_password=True).copy(values)
return user_id
except Exception as e:
_logger.exception("OAuth Wechat: 微信访问用户【UserId:%s】自动创建失败【Odoo错误:%s】!" % (userid, ustr(e)))
raise OAuthLogin(ustr(e))
else:
_logger.exception(
"OAuth Wechat: 微信访问用户【UserId:%s】自动创建失败【微信错误码:%s】!" % (userid, dict_user["errcode"]))
return False