update app_saas
@@ -25,8 +25,8 @@
|
||||
##############################################################################
|
||||
|
||||
{
|
||||
'name': 'odooapp.cn SaaS Client.Odoo中文应用商店SaaS云服务客户端',
|
||||
'version': '18.0.24.11.08',
|
||||
'name': 'odooapp.cn SaaS Client-Ai Passport.Odoo中文应用商店SaaS云服务客户端',
|
||||
'version': '18.0.24.12.06',
|
||||
'author': 'odooai.cn',
|
||||
'category': 'Base',
|
||||
'website': 'https://www.odooai.cn',
|
||||
@@ -37,24 +37,31 @@
|
||||
'currency': 'EUR',
|
||||
'images': ['static/description/banner.png'],
|
||||
'summary': '''
|
||||
Odoo中文云服务客户端,模块持续更新中。
|
||||
AiSaas云服务,使用Ai通行证,一键实现全社交媒体统一登录SSO。
|
||||
支持微信、抖音、QQ、淘宝、钉钉、支付宝、企业微信、Facebook、Google、微软Azure等整合登录。
|
||||
在Odoo中获取最新的中文翻译(另行收费),获取当前Odoo模块最新版本更新等。
|
||||
支持Odoo中文版在线更新及 www.odooapp.cn 的信息推送等。
|
||||
安装即代表同意我司云服务用户协议及隐私政策。 https://www.odooapp.cn/conditions
|
||||
''',
|
||||
'description': '''
|
||||
1. 快速访问Odoo中文应用商店并获取独享Odoo中文通行码
|
||||
2. 一键获取最新应用信息,最新行业服务包
|
||||
3. 一键获取Odoo中文翻译更新
|
||||
4. 快速获取Odoo服务,Odoo升级评估
|
||||
1. Ai通行证实现全社交媒体登录支持
|
||||
2. 快速访问Odoo中文应用商店并获取独享Odoo中文通行码,可随时断开云服务连接
|
||||
3. 一键获取最新Odoo中文应用模块与主题,最新行业服务包
|
||||
4. 一键获取Odoo中文翻译更新(收费)
|
||||
5. 快速获取Odoo服务,Odoo升级评估
|
||||
6. 系统出错时一键提交,获取技术支持(会员专属)
|
||||
11.多语言支持,多公司支持
|
||||
12.仅从Odoo18企业版,社区版版本开始支持
|
||||
12.仅从Odoo18-16企业版,社区版版本开始支持
|
||||
13.代码完全开源
|
||||
''',
|
||||
'depends': [
|
||||
'app_common',
|
||||
'auth_oauth',
|
||||
# 'website',
|
||||
],
|
||||
'data': [
|
||||
'data/auth_oauth_data.xml',
|
||||
'views/auth_oauth_provider_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
],
|
||||
'assets': {
|
||||
@@ -66,8 +73,6 @@
|
||||
],
|
||||
},
|
||||
'demo': [
|
||||
# 'data/model_new_demo.xml',
|
||||
# 'views/website_templates.xml',
|
||||
],
|
||||
# 'pre_init_hook': 'pre_init_hook',
|
||||
# 'post_init_hook': 'post_init_hook',
|
||||
|
||||
24
app_saas/data/auth_oauth_data.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="provider_app_saas" model="auth.oauth.provider">
|
||||
<field name="name">Ai Passport of odooapp.cn</field>
|
||||
<field name="body">Ai通行证-Odoo中文应用商店</field>
|
||||
<field name="auth_endpoint">https://www.odooapp.cn/oauth/authorize</field>
|
||||
<field name="code_endpoint">https://www.odooapp.cn/oauth/token</field>
|
||||
<field name="scope">odoo,profile</field>
|
||||
<field name="validation_endpoint">https://www.odooapp.cn/oauth/profile</field>
|
||||
<field name="data_endpoint"></field>
|
||||
<field name="user_template_id" ref=""/>
|
||||
<field name="css_class">fa fa-2x fa-fw fa-sign-in text-primary</field>
|
||||
<field name="enabled" eval="True"/>
|
||||
</record>
|
||||
<!-- Use database uuid as client_id for OpenERP oauth provider -->
|
||||
<function model="auth.oauth.provider" name="write">
|
||||
<value eval="[ref('app_saas.provider_app_saas')]"/>
|
||||
<value model="ir.config_parameter" eval="{
|
||||
'client_id': obj().env['ir.config_parameter'].get_param('database.uuid'),
|
||||
}"/>
|
||||
</function>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -2,3 +2,7 @@
|
||||
# Part of odooAi.cn. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import res_config_settings
|
||||
from . import auth_oauth_provider
|
||||
from . import res_users
|
||||
|
||||
|
||||
|
||||
16
app_saas/models/auth_oauth_provider.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models, modules, tools, _
|
||||
|
||||
import base64
|
||||
from datetime import timedelta
|
||||
|
||||
class AuthOAuthProvider(models.Model):
|
||||
|
||||
_inherit = 'auth.oauth.provider'
|
||||
_order = 'sequence, name'
|
||||
|
||||
# 取code的url,主要针对国内的先取code,再拿Token的方式
|
||||
code_endpoint = fields.Char(string='Token by Code Endpoint', help='Get Token from Code. Only for Code response type.')
|
||||
user_template_id = fields.Many2one('res.users', string='New User Template',
|
||||
domain=[('active', '=', False)])
|
||||
133
app_saas/models/res_users.py
Normal file
@@ -0,0 +1,133 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
try:
|
||||
import urlparse
|
||||
except:
|
||||
from urllib.parse import urlparse
|
||||
try:
|
||||
import urllib2
|
||||
except:
|
||||
from urllib import request as urllib2
|
||||
|
||||
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import AccessDenied, UserError
|
||||
from odoo.addons.auth_signup.models.res_users import SignupError
|
||||
from odoo.http import request, Response
|
||||
from odoo.tools.misc import ustr
|
||||
|
||||
from ast import literal_eval
|
||||
import json
|
||||
import requests
|
||||
from datetime import timedelta
|
||||
import random
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class OauthBindError(Exception):
|
||||
# 增加一种错误类型
|
||||
pass
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = 'res.users'
|
||||
|
||||
@api.model
|
||||
def auth_oauth(self, provider, params):
|
||||
# 这里原生是没处理code模式,此处将增加使用code取token,不在 controller 中处理
|
||||
code = params.get('code', False)
|
||||
access_token = params.get('access_token')
|
||||
oauth_provider = self.env['auth.oauth.provider'].sudo().browse(provider)
|
||||
|
||||
kw = {}
|
||||
if oauth_provider.code_endpoint and code and not access_token:
|
||||
# odoo 特殊处理,用code取token
|
||||
params.update({
|
||||
'scope': oauth_provider.scope or '',
|
||||
'client_id': oauth_provider.client_id or '',
|
||||
})
|
||||
if hasattr(oauth_provider, 'client_secret') and oauth_provider.client_secret:
|
||||
params.update({
|
||||
'client_secret': oauth_provider.client_secret or '',
|
||||
})
|
||||
response = requests.get(oauth_provider.code_endpoint, params=params, timeout=30)
|
||||
if response.ok:
|
||||
ret = response.json()
|
||||
# todo: 客户机首次连接时,取到的 server 端 key 写入 provider 的 client_secret
|
||||
if ret.get('push_client_secret') and hasattr(oauth_provider, 'client_secret'):
|
||||
oauth_provider.write({'client_secret': ret.get('push_client_secret')})
|
||||
self._cr.commit()
|
||||
kw = {**ret, **params}
|
||||
kw.pop('code', False)
|
||||
self = self.with_context(auth_extra=kw)
|
||||
res = super(ResUsers, self).auth_oauth(provider, kw)
|
||||
return res
|
||||
|
||||
def _auth_oauth_signin(self, provider, validation, params):
|
||||
# 用户绑定的额外处理,如果有同 login 用户则直接绑定
|
||||
# todo: 当前不管多公司,在 social_login 里有更细节判断,后续优化
|
||||
# todo: 当前同名就写 oauth 信息,不安全,要优化
|
||||
oauth_provider = self.env['auth.oauth.provider'].sudo().browse(provider)
|
||||
if oauth_provider and oauth_provider.scope.find('odoo') >= 0:
|
||||
oauth_uid =validation.get('user_id')
|
||||
if oauth_uid:
|
||||
odoo_user = self.sudo().search([('login', '=', oauth_uid)], limit=1)
|
||||
if odoo_user and not (odoo_user.oauth_access_token and odoo_user.oauth_provider_id and odoo_user.oauth_uid):
|
||||
vals = {
|
||||
'oauth_provider_id': provider,
|
||||
'oauth_access_token': params.get('access_token'),
|
||||
'oauth_uid': oauth_uid,
|
||||
}
|
||||
odoo_user.write(vals)
|
||||
_logger.info('========= _auth_oauth_signin res.users write:%s' % vals)
|
||||
self._cr.commit()
|
||||
return odoo_user.user_id.login
|
||||
res = super(ResUsers, self)._auth_oauth_signin(provider, validation, params)
|
||||
return res
|
||||
|
||||
def _create_user_from_template(self, values):
|
||||
# 处理odooapp.cn 为 server 时 默认为内部用户
|
||||
oauth_provider_id = values.get('oauth_provider_id')
|
||||
if oauth_provider_id:
|
||||
provider = request.env['auth.oauth.provider'].sudo().browse(int(oauth_provider_id))
|
||||
if provider:
|
||||
template_user = provider.user_template_id
|
||||
if not template_user and provider.scope.find('odoo') >= 0:
|
||||
template_user = self.sudo().env.ref('base.default_user', False)
|
||||
if not template_user:
|
||||
template_user_id = literal_eval(self.env['ir.config_parameter'].sudo().get_param('base.template_portal_user_id', 'False'))
|
||||
template_user = self.sudo().browse(template_user_id)
|
||||
|
||||
if not values.get('login'):
|
||||
raise ValueError(_('Signup: no login given for new user'))
|
||||
if not values.get('partner_id') and not values.get('name'):
|
||||
raise ValueError(_('Signup: no name or partner given for new user'))
|
||||
|
||||
# create a copy of the template user (attached to a specific partner_id if given)
|
||||
values['active'] = True
|
||||
try:
|
||||
with self.env.cr.savepoint():
|
||||
return template_user.sudo().with_context(no_reset_password=True).copy(values)
|
||||
except Exception as e:
|
||||
# copy may failed if asked login is not available.
|
||||
raise SignupError(ustr(e))
|
||||
res = super(ResUsers, self)._create_user_from_template(values)
|
||||
self._cr.commit()
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _generate_signup_values(self, provider, validation, params):
|
||||
# 此处生成 创建 odoo user 的初始值,增加字段如头像
|
||||
res = super()._generate_signup_values(provider, validation, params)
|
||||
# 后置增加字段,包括 headimgurl
|
||||
if validation.get('mobile'):
|
||||
res['mobile'] = validation.get('mobile')
|
||||
if validation.get('headimgurl'):
|
||||
res['image_1920'] = self.sudo()._get_image_from_url(validation.get('headimgurl'))
|
||||
return res
|
||||
|
||||
def _rpc_api_keys_only(self):
|
||||
# 可直接使用 oauth_access_token 作为 password 登录
|
||||
self.ensure_one()
|
||||
return self.oauth_access_token or super()._rpc_api_keys_only()
|
||||
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 457 KiB |
BIN
app_saas/static/description/banner2.png
Normal file
|
After Width: | Height: | Size: 236 KiB |
BIN
app_saas/static/description/banner3.png
Normal file
|
After Width: | Height: | Size: 241 KiB |
BIN
app_saas/static/description/cnreadme.jpg
Normal file
|
After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 86 KiB |
BIN
app_saas/static/description/demo2.jpg
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
app_saas/static/description/demo3.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
app_saas/static/description/demo4.jpg
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
app_saas/static/description/demo5.jpg
Normal file
|
After Width: | Height: | Size: 145 KiB |
@@ -8,8 +8,8 @@
|
||||
<html>
|
||||
<!-- begin title-->
|
||||
<section class="oe_container container o_cc o_cc2">
|
||||
<h2 class="text-center bg-warning text-white pt24 pb24">odooapp.cn SaaS Client.Odoo中文应用商店SaaS云服务客户端</h2>
|
||||
<h3 class="text-center">快速访问Odoo中文应用商店并获取独享Odoo中文通行码</h3>
|
||||
<h2 class="text-center bg-warning text-white pt24 pb24">Ai通行证.Odoo中文应用商店SaaS云服务客户端,一键微信QQ淘宝抖音全媒体登录SSO</h2>
|
||||
<h3 class="text-center">odooapp.cn SaaS Client-Ai Passport.SSO with </h3>
|
||||
<h4 class="text-center">一键获取最新应用信息,最新行业服务包,一键获取Odoo中文翻译更新,快速获取Odoo服务,Odoo升级评估</h4>
|
||||
</section>
|
||||
<!-- end title-->
|
||||
@@ -17,17 +17,59 @@
|
||||
<!-- quick demo-->
|
||||
<section class="oe_container container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="bg-warning text-center pt8 pb8 mt16 mb16">启用后快速获取Odoo中文应用商店模块及行业方案</h2>
|
||||
<h4 class="oe_slogan">未经用户明确同意,本应用不会获取任何用户系统数据</h4>
|
||||
<h2 class="bg-warning text-center pt8 pb8 mt16 mb16">1.启用后即可使用 odooapp.cn 提供的全网社交媒体整合登录</h2>
|
||||
<h4 class="oe_slogan">支持微信、抖音、QQ、淘宝、钉钉、支付宝、企业微信、Facebook、Google、微软Azure等整合登录。</h4>
|
||||
<p>未经用户明确同意,本应用不会获取任何用户系统数据.</p>
|
||||
<p class="">相关内容,请参考用户协议及我们的隐私政策:</p>
|
||||
<p> https://www.odooapp.cn/conditions</p>
|
||||
<p> https://www.odooapp.cn/privacy-policy</p>
|
||||
<div class="oe_demo oe_screenshot img img-fluid">
|
||||
<img src="banner1.png"/>
|
||||
</div>
|
||||
<p>用户可直接调用SSO一键登录</p>
|
||||
<div class="oe_demo oe_screenshot img img-fluid">
|
||||
<img src="demo1.jpg"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="bg-warning text-center pt8 pb8 mt16 mb16">2. 快速访问Odoo中文应用商店并获取独享Odoo中文通行码,可随时断开云服务连接</h2>
|
||||
<h4 class="oe_slogan">启用云服务后,即可获取最新功能推送,版本比对信息,提供版本升级建议</h4>
|
||||
<div class="oe_demo oe_screenshot img img-fluid">
|
||||
<img src="demo2.jpg"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="bg-warning text-center pt8 pb8 mt16 mb16">标准的Oauth2登录支持,可方便配置</h2>
|
||||
<h4 class="oe_slogan">安装后,用户即可使用 odooapp.cn 提供的 Ai通行证进行登录</h4>
|
||||
<div class="oe_demo oe_screenshot img img-fluid">
|
||||
<img src="demo3.jpg"/>
|
||||
</div>
|
||||
<h4 class="oe_slogan">可以使用微信抖音QQ等在Odoo中文应用商店登录,同意并继续后即可登入自有Odoo</h4>
|
||||
<div class="oe_demo oe_screenshot img img-fluid">
|
||||
<img src="demo4.jpg"/>
|
||||
</div>
|
||||
<h4 class="oe_slogan">自定义默认用户模板,为用户启用不同权限</h4>
|
||||
<div class="oe_demo oe_screenshot img img-fluid">
|
||||
<img src="demo5.jpg"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="bg-warning text-center pt8 pb8 mt16 mb16">相关服务正在紧张开发中,后续可享用更多服务</h2>
|
||||
<div class="oe_demo oe_screenshot img img-fluid">
|
||||
<img src="cnreadme.jpg"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- end quick demo-->
|
||||
|
||||
<!-- begin howto-->
|
||||
|
||||
16
app_saas/views/auth_oauth_provider_views.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="app_view_oauth_provider_form" model="ir.ui.view">
|
||||
<field name="name">app.auth.oauth.provider.form</field>
|
||||
<field name="model">auth.oauth.provider</field>
|
||||
<field name="inherit_id" ref="auth_oauth.view_oauth_provider_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='auth_endpoint']" position="after">
|
||||
<field name="code_endpoint"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='data_endpoint']" position="before">
|
||||
<field name="user_template_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||