问题
I am developping an Android application, and I'd like to retrieve data from an Odoo server.
For that I developped a custom module in Odoo, in which I created a controller.
My controller :
import json
import xmlrpc.client as xmlrpclib
from odoo import http
from openerp.http import Response
class resUserController(http.Controller):
url = '<my url>'
db = '<name of my database>'
@http.route('/user/login', type='json', method='GET', auth='public')
def get_login(self, **kwargs):
username = kwargs.get('email')
password = kwargs.get('password')
common = xmlrpclib.ServerProxy('{}/xmlrpc/2/common'.format(self.url), allow_none=True)
uid = common.authenticate(self.db, username, password, {})
if uid:
Response.status = '200 Succesful operation'
json_result = {'token': uid}
return json.dumps(json_result)
Response.status = '400 Invalid credentials'
return
When I call it from a python script to try it, it works fine and I get a <Response [200]>
and a json {u'jsonrpc': u'2.0', u'result': u'{"token": 8}', u'id': None}
with the id of the account I connect to.
But then I have an other function I call with an other route in the same controller, but with auth='user'
this time, because I want the user to be able to see only informations he has rights on.
@http.route('/user/getInfo', type='json', method='GET', auth='user')
def get_info(self, **kwargs):
uid = 1
password = '<my admin password>'
models = xmlrpclib.ServerProxy('{}/xmlrpc/2/object'.format(self.url), allow_none=True)
info = models.execute_kw(self.db, uid, password, 'res.users',
'search_read', [[['id', '=', kwargs.get('token')]]],
{'fields': ['info']})[0]['invite_code']
if info:
Response.status = '200 Succesful operation'
json_result = {'info': info}
return json.dumps(json_result)
Response.status = '404 User not found'
return
This function works fine when I use auth='public'
, but when I go for auth='user'
, I get the following json response :
Response [200]
{ u'jsonrpc': u'2.0', u'id': None, u'error': { u'message': u'Odoo Session Expired', u'code': 100, u'data': { u'debug': u'Traceback (most recent call last): File "/usr/lib/python3/dist-packages/odoo/http.py", line 650, in _handle_exception return super(JsonRequest, self)._handle_exception(exception) File "/usr/lib/python3/dist-packages/odoo/http.py", line 310, in _handle_exception raise pycompat.reraise(type(exception), exception, sys.exc_info()[2]) File "/usr/lib/python3/dist-packages/odoo/tools/pycompat.py", line 87, in reraise raise value File "/usr/lib/python3/dist-packages/odoo/addons/http_routing/models/ir_http.py", line 342, in _dispatch cls._authenticate(func.routing[\'auth\']) File "/usr/lib/python3/dist-packages/odoo/addons/base/ir/ir_http.py", line 117, in _authenticate getattr(cls, "_auth_method_%s" % auth_method)() File "/usr/lib/python3/dist-packages/odoo/addons/base/ir/ir_http.py", line 90, in _auth_method_user raise http.SessionExpiredException("Session expired") odoo.http.SessionExpiredException: Session expired', u'exception_type': u'internal_error', u'message': u'Session expired', u'name': u'odoo.http.SessionExpiredException', u'arguments': [u'Session expired'] } } }
I based my work on This documentation, which is an official Odoo doc, but here are the problems :
1 It ask me to write my admin password in each function, which seems dangerous.
2 After authentication, I get the id of my user, but no session token. Then how can I inform my function with auth='user'
that I'm connected and to which user?
Here is my script to test my calls :
import requests
import json
url_connect = "<my url>/user/login"
url = "<my url>/user/getInfo"
headers = {'Content-Type': 'application/json'}
data_connect = {
"params": {
"email": "<my test account email>",
"password": "<my test account password>",
}
}
data = {
"params": {
"token": <my test account id>,
}
}
data_json = json.dumps(data)
r = requests.get(url=url_connect, data=json.dumps(data_connect), headers=headers)
print(r)
print(r.json())
r = requests.get(url=url, data=data_json, headers=headers)
print(r)
print(r.json())
回答1:
Things to note:
- Never send credentials in a GET request
- All Odoo RPC requests are POST requests
- You don't need a custom login route if you use /web/session/authenticate
- The External API is intended to be used outside of odoo framework. When developing a module use self.env['< model >'] if inside a model or http.request.env['< model >'] if in a controller
- The call to /web/session/authenticate returns a json containing the session_id, which you have to pass it in the cookies to subsequent requests until you call /web/session/destroy to logout.
Here is an example using /web/session/autenticate:
import requests
import json
url_connect = "http://localhost:8069/web/session/authenticate"
url = "http://localhost:8069/web/session/get_session_info"
headers = {'Content-Type': 'application/json'}
data_connect = {
"params": {
"db": "demo1",
"login": "admin",
"password": "admin",
}
}
data = {}
session = requests.Session()
r = session.post(url=url_connect, data=json.dumps(data_connect), headers=headers)
if r.ok:
result = r.json()['result']
if result.get('session_id'):
session.cookies['session_id'] = result.get('session_id')
r = session.post(url=url, data=json.dumps(data), headers=headers)
print(r)
print(r.json())
To get the info from your controller you could use request.env.user which holds the logged user at the moment and since you specify auth='user' it is required to be a valid. The sample code might look like:
from odoo.http import request
class UserController(http.Controller):
@http.route('/user/getInfo', type='json', method='POST', auth='user')
def get_info(self, **kwargs):
current_user = request.env.user
Response.status = '200 Succesful operation'
json_result = {'info': current_user.info}
return json.dumps(json_result)
回答2:
Very well write question, you have done the homework. This works for me to be able to authenticate the mobile users sessions using an api style requests.
@http.route([
'/m/login/email',
], type='http', auth="public", website=True, methods=["POST"], csrf=False)
def users_login_email(self, **kwargs):
if kwargs:
data = json.loads(kwargs.keys()[0])
else:
data = json.loads(request.httprequest.data)
email = data.get('email')
password = data.get('password')
if not request.session.db:
setup_db()
uid = request.session.authenticate(request.session.db, email, password)
if uid:
return self._user_details(uid)
body = json.dumps({"body": ["Credenciales Incorrectas"]})
return werkzeug.wrappers.Response(body, status=403, headers=[
('Content-Type', 'application/json'), ('Content-Length', len(body))
])
Also I didn't use auth='user'
to don't let Odoo to mess with redirects and web stuffs checks. I use this annotation wrapper in my controllers routes that require an authenticated user to return the proper HTTP error status code
def check_user(f):
@functools.wraps(f)
def wrap(*args, **kwargs):
if not request.session.db:
setup_db()
request.uid = request.session.uid
if not request.uid:
body = json.dumps({"body": ["Session Expired"]})
return werkzeug.wrappers.Response(body, status=403, headers=[
('Content-Type', 'application/json'), ('Content-Length', len(body))
])
return f(*args, **kwargs)
return wrap
You could use it like this:
@check_user
@http.route([
'/m/<int:shop_id>/cart/info',
], type='http', auth="public", website=True)
def cart_info(self, shop_id, **kwargs):
来源:https://stackoverflow.com/questions/52875925/how-to-connect-to-odoo-database-from-an-android-application