Browse Source
Merge pull request #43 from acsone/auth_from_http_remote_user-8.0
Merge pull request #43 from acsone/auth_from_http_remote_user-8.0
[ADD] Authentication via HTTP Remote User 8.0pull/122/head
9 changed files with 550 additions and 0 deletions
-
24auth_from_http_remote_user/__init__.py
-
162auth_from_http_remote_user/__openerp__.py
-
22auth_from_http_remote_user/controllers/__init__.py
-
110auth_from_http_remote_user/controllers/main.py
-
27auth_from_http_remote_user/model.py
-
65auth_from_http_remote_user/res_users.py
-
28auth_from_http_remote_user/tests/__init__.py
-
90auth_from_http_remote_user/tests/test_res_users.py
-
22auth_from_http_remote_user/utils.py
@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Author: Laurent Mignon |
||||
|
# Copyright 2014 'ACSONE SA/NV' |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
from . import controllers |
||||
|
from . import res_users |
||||
|
from . import model |
@ -0,0 +1,162 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Author: Laurent Mignon |
||||
|
# Copyright 2014 'ACSONE SA/NV' |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
{ |
||||
|
'name': 'Authenticate via HTTP Remote User', |
||||
|
'version': '1.0', |
||||
|
'category': 'Tools', |
||||
|
'description': """ |
||||
|
Allow users to be automatically logged in. |
||||
|
========================================== |
||||
|
|
||||
|
This module initialize the session by looking for the field HTTP_REMOTE_USER in |
||||
|
the HEADER of the HTTP request and trying to bind the given value to a user. |
||||
|
To be active, the module must be installed in the expected databases and loaded |
||||
|
at startup; Add the *--load* parameter to the startup command: :: |
||||
|
|
||||
|
--load=web,web_kanban,auth_from_http_remote_user, ... |
||||
|
|
||||
|
If the field is found in the header and no user matches the given one, the |
||||
|
system issue a login error page. (*401* `Unauthorized`) |
||||
|
|
||||
|
Use case. |
||||
|
--------- |
||||
|
|
||||
|
The module allows integration with external security systems [#]_ that can pass |
||||
|
along authentication of a user via Remote_User HTTP header field. In many |
||||
|
cases, this is achieved via server like Apache HTTPD or nginx proxying Odoo. |
||||
|
|
||||
|
.. important:: When proxying your Odoo server with Apache or nginx, It's |
||||
|
important to filter out the Remote_User HTTP header field before your |
||||
|
request is processed by the proxy to avoid security issues. In apache you |
||||
|
can do it by using the RequestHeader directive in your VirtualHost |
||||
|
section :: |
||||
|
|
||||
|
<VirtualHost *:80> |
||||
|
ServerName MY_VHOST.com |
||||
|
ProxyRequests Off |
||||
|
... |
||||
|
|
||||
|
RequestHeader unset Remote-User early |
||||
|
ProxyPass / http://127.0.0.1:8069/ retry=10 |
||||
|
ProxyPassReverse / http://127.0.0.1:8069/ |
||||
|
ProxyPreserveHost On |
||||
|
</VirtualHost> |
||||
|
|
||||
|
|
||||
|
How to test the module with Apache [#]_ |
||||
|
---------------------------------------- |
||||
|
|
||||
|
Apache can be used as a reverse proxy providing the authentication and adding |
||||
|
the required field in the Http headers. |
||||
|
|
||||
|
Install apache: :: |
||||
|
|
||||
|
$ sudo apt-get install apache2 |
||||
|
|
||||
|
|
||||
|
Define a new vhost to Apache by putting a new file in |
||||
|
/etc/apache2/sites-available: :: |
||||
|
|
||||
|
$ sudo vi /etc/apache2/sites-available/MY_VHOST.com |
||||
|
|
||||
|
with the following content: :: |
||||
|
|
||||
|
<VirtualHost *:80> |
||||
|
ServerName MY_VHOST.com |
||||
|
ProxyRequests Off |
||||
|
<Location /> |
||||
|
AuthType Basic |
||||
|
AuthName "Test Odoo auth_from_http_remote_user" |
||||
|
AuthBasicProvider file |
||||
|
AuthUserFile /etc/apache2/MY_VHOST.htpasswd |
||||
|
Require valid-user |
||||
|
|
||||
|
RewriteEngine On |
||||
|
RewriteCond %{LA-U:REMOTE_USER} (.+) |
||||
|
RewriteRule . - [E=RU:%1] |
||||
|
RequestHeader set Remote-User "%{RU}e" env=RU |
||||
|
</Location> |
||||
|
|
||||
|
RequestHeader unset Remote-User early |
||||
|
ProxyPass / http://127.0.0.1:8069/ retry=10 |
||||
|
ProxyPassReverse / http://127.0.0.1:8069/ |
||||
|
ProxyPreserveHost On |
||||
|
</VirtualHost> |
||||
|
|
||||
|
.. important:: The *RequestHeader* directive is used to add the *Remote-User* |
||||
|
field in the http headers. By default an *'Http-'* prefix is added to the |
||||
|
field name. |
||||
|
In Odoo, header's fields name are normalized. As result of this |
||||
|
normalization, the 'Http-Remote-User' is available as 'HTTP_REMOTE_USER'. |
||||
|
If you don't know how your specified field is seen by Odoo, run your |
||||
|
server in debug mode once the module is activated and look for an entry |
||||
|
like: :: |
||||
|
|
||||
|
DEBUG openerp1 openerp.addons.auth_from_http_remote_user.controllers. |
||||
|
session: |
||||
|
Field 'HTTP_MY_REMOTE_USER' not found in http headers |
||||
|
{'HTTP_AUTHORIZATION': 'Basic YWRtaW46YWRtaW4=', ..., |
||||
|
'HTTP_REMOTE_USER': 'demo') |
||||
|
|
||||
|
Enable the required apache modules: :: |
||||
|
|
||||
|
$ sudo a2enmod headers |
||||
|
$ sudo a2enmod proxy |
||||
|
$ sudo a2enmod rewrite |
||||
|
$ sudo a2enmod proxy_http |
||||
|
|
||||
|
Enable your new vhost: :: |
||||
|
|
||||
|
$ sudo a2ensite MY_VHOST.com |
||||
|
|
||||
|
Create the *htpassword* file used by the configured basic authentication: :: |
||||
|
|
||||
|
$ sudo htpasswd -cb /etc/apache2/MY_VHOST.htpasswd admin admin |
||||
|
$ sudo htpasswd -b /etc/apache2/MY_VHOST.htpasswd demo demo |
||||
|
|
||||
|
For local test, add the *MY_VHOST.com* in your /etc/vhosts file. |
||||
|
|
||||
|
Finally reload the configuration: :: |
||||
|
|
||||
|
$ sudo service apache2 reload |
||||
|
|
||||
|
Open your browser and go to MY_VHOST.com. If everything is well configured, you |
||||
|
are prompted for a login and password outside Odoo and are automatically |
||||
|
logged in the system. |
||||
|
|
||||
|
.. [#] Shibolleth, Tivoli access manager, .. |
||||
|
.. [#] Based on a ubuntu 12.04 env |
||||
|
|
||||
|
""", |
||||
|
'author': 'Acsone SA/NV', |
||||
|
'maintainer': 'ACSONE SA/NV', |
||||
|
'website': 'http://www.acsone.eu', |
||||
|
'depends': ['base', 'web', 'base_setup'], |
||||
|
"license": "AGPL-3", |
||||
|
'data': [], |
||||
|
"demo": [], |
||||
|
"test": [], |
||||
|
"active": False, |
||||
|
"license": "AGPL-3", |
||||
|
"installable": True, |
||||
|
"auto_install": False, |
||||
|
"application": False, |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Author: Laurent Mignon |
||||
|
# Copyright 2014 'ACSONE SA/NV' |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
from . import main |
@ -0,0 +1,110 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Author: Laurent Mignon |
||||
|
# Copyright 2014 'ACSONE SA/NV' |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
from openerp import SUPERUSER_ID |
||||
|
|
||||
|
import openerp |
||||
|
from openerp import http |
||||
|
from openerp.http import request |
||||
|
from openerp.addons.web.controllers import main |
||||
|
from openerp.addons.auth_from_http_remote_user.model import \ |
||||
|
AuthFromHttpRemoteUserInstalled |
||||
|
from .. import utils |
||||
|
|
||||
|
import random |
||||
|
import logging |
||||
|
import werkzeug |
||||
|
|
||||
|
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
|
||||
|
class Home(main.Home): |
||||
|
|
||||
|
_REMOTE_USER_ATTRIBUTE = 'HTTP_REMOTE_USER' |
||||
|
|
||||
|
@http.route('/web', type='http', auth="none") |
||||
|
def web_client(self, s_action=None, **kw): |
||||
|
main.ensure_db() |
||||
|
try: |
||||
|
self._bind_http_remote_user(http.request.session.db) |
||||
|
except http.AuthenticationError: |
||||
|
return werkzeug.exceptions.Unauthorized().get_response() |
||||
|
return super(Home, self).web_client(s_action, **kw) |
||||
|
|
||||
|
def _search_user(self, res_users, login, cr): |
||||
|
user_ids = res_users.search(cr, SUPERUSER_ID, [('login', '=', login), |
||||
|
('active', '=', True)]) |
||||
|
assert len(user_ids) < 2 |
||||
|
if user_ids: |
||||
|
return user_ids[0] |
||||
|
return None |
||||
|
|
||||
|
def _bind_http_remote_user(self, db_name): |
||||
|
try: |
||||
|
registry = openerp.registry(db_name) |
||||
|
with registry.cursor() as cr: |
||||
|
if AuthFromHttpRemoteUserInstalled._name not in registry: |
||||
|
# module not installed in database, |
||||
|
# continue usual behavior |
||||
|
return |
||||
|
|
||||
|
headers = http.request.httprequest.headers.environ |
||||
|
|
||||
|
login = headers.get(self._REMOTE_USER_ATTRIBUTE, None) |
||||
|
if not login: |
||||
|
# no HTTP_REMOTE_USER header, |
||||
|
# continue usual behavior |
||||
|
return |
||||
|
|
||||
|
request_login = request.session.login |
||||
|
if request_login: |
||||
|
if request_login == login: |
||||
|
# already authenticated |
||||
|
return |
||||
|
else: |
||||
|
request.session.logout(keep_db=True) |
||||
|
|
||||
|
res_users = registry.get('res.users') |
||||
|
user_id = self._search_user(res_users, login, cr) |
||||
|
if not user_id: |
||||
|
# HTTP_REMOTE_USER login not found in database |
||||
|
request.session.logout(keep_db=True) |
||||
|
raise http.AuthenticationError() |
||||
|
|
||||
|
# generate a specific key for authentication |
||||
|
key = randomString(utils.KEY_LENGTH, '0123456789abcdef') |
||||
|
res_users.write(cr, SUPERUSER_ID, [user_id], {'sso_key': key}) |
||||
|
request.session.authenticate(db_name, login=login, |
||||
|
password=key, uid=user_id) |
||||
|
except http.AuthenticationError, e: |
||||
|
raise e |
||||
|
except Exception, e: |
||||
|
_logger.error("Error binding Http Remote User session", |
||||
|
exc_info=True) |
||||
|
raise e |
||||
|
|
||||
|
randrange = random.SystemRandom().randrange |
||||
|
|
||||
|
|
||||
|
def randomString(length, chrs): |
||||
|
"""Produce a string of length random bytes, chosen from chrs.""" |
||||
|
n = len(chrs) |
||||
|
return ''.join([chrs[randrange(n)] for _ in xrange(length)]) |
@ -0,0 +1,27 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Author: Laurent Mignon |
||||
|
# Copyright 2014 'ACSONE SA/NV' |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
from openerp.osv import orm |
||||
|
|
||||
|
|
||||
|
class AuthFromHttpRemoteUserInstalled(orm.AbstractModel): |
||||
|
"""An abstract model used to safely know if the module is installed |
||||
|
""" |
||||
|
_name = 'auth_from_http_remote_user.installed' |
@ -0,0 +1,65 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Author: Laurent Mignon |
||||
|
# Copyright 2014 'ACSONE SA/NV' |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
from openerp.modules.registry import RegistryManager |
||||
|
from openerp.osv import orm, fields |
||||
|
from openerp import SUPERUSER_ID |
||||
|
import openerp.exceptions |
||||
|
from openerp.addons.auth_from_http_remote_user import utils |
||||
|
|
||||
|
|
||||
|
class res_users(orm.Model): |
||||
|
_inherit = 'res.users' |
||||
|
|
||||
|
_columns = { |
||||
|
'sso_key': fields.char('SSO Key', size=utils.KEY_LENGTH, |
||||
|
readonly=True), |
||||
|
} |
||||
|
|
||||
|
def copy(self, cr, uid, rid, defaults=None, context=None): |
||||
|
defaults = defaults or {} |
||||
|
defaults['sso_key'] = False |
||||
|
return super(res_users, self).copy(cr, uid, rid, defaults, context) |
||||
|
|
||||
|
def check_credentials(self, cr, uid, password): |
||||
|
try: |
||||
|
return super(res_users, self).check_credentials(cr, uid, password) |
||||
|
except openerp.exceptions.AccessDenied: |
||||
|
res = self.search(cr, SUPERUSER_ID, [('id', '=', uid), |
||||
|
('sso_key', '=', password)]) |
||||
|
if not res: |
||||
|
raise openerp.exceptions.AccessDenied() |
||||
|
|
||||
|
def check(self, db, uid, passwd): |
||||
|
try: |
||||
|
return super(res_users, self).check(db, uid, passwd) |
||||
|
except openerp.exceptions.AccessDenied: |
||||
|
if not passwd: |
||||
|
raise |
||||
|
with RegistryManager.get(db).cursor() as cr: |
||||
|
cr.execute('''SELECT COUNT(1) |
||||
|
FROM res_users |
||||
|
WHERE id=%s |
||||
|
AND sso_key=%s |
||||
|
AND active=%s''', (int(uid), passwd, True)) |
||||
|
if not cr.fetchone()[0]: |
||||
|
raise |
||||
|
self._uid_cache.setdefault(db, {})[uid] = passwd |
@ -0,0 +1,28 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Author: Laurent Mignon |
||||
|
# Copyright 2014 'ACSONE SA/NV' |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
from . import test_res_users |
||||
|
fast_suite = [ |
||||
|
] |
||||
|
|
||||
|
checks = [ |
||||
|
test_res_users, |
||||
|
] |
@ -0,0 +1,90 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Author: Laurent Mignon |
||||
|
# Copyright 2014 'ACSONE SA/NV' |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
from openerp.tests import common |
||||
|
import mock |
||||
|
import os |
||||
|
from contextlib import contextmanager |
||||
|
import unittest |
||||
|
|
||||
|
|
||||
|
@contextmanager |
||||
|
def mock_cursor(cr): |
||||
|
with mock.patch('openerp.sql_db.Connection.cursor') as mocked_cursor_call: |
||||
|
org_close = cr.close |
||||
|
org_autocommit = cr.autocommit |
||||
|
try: |
||||
|
cr.close = mock.Mock() |
||||
|
cr.autocommit = mock.Mock() |
||||
|
mocked_cursor_call.return_value = cr |
||||
|
yield |
||||
|
finally: |
||||
|
cr.close = org_close |
||||
|
cr.autocommit = org_autocommit |
||||
|
|
||||
|
|
||||
|
class test_res_users(common.TransactionCase): |
||||
|
|
||||
|
def test_login(self): |
||||
|
res_users_obj = self.registry('res.users') |
||||
|
res = res_users_obj.authenticate(common.DB, 'admin', 'admin', None) |
||||
|
uid = res |
||||
|
self.assertTrue(res, "Basic login must works as expected") |
||||
|
token = "123456" |
||||
|
res = res_users_obj.authenticate(common.DB, 'admin', token, None) |
||||
|
self.assertFalse(res) |
||||
|
# mimic what the new controller do when it find a value in |
||||
|
# the http header (HTTP_REMODE_USER) |
||||
|
res_users_obj.write(self.cr, self.uid, uid, {'sso_key': token}) |
||||
|
|
||||
|
# Here we need to mock the cursor since the login is natively done |
||||
|
# inside its own connection |
||||
|
with mock_cursor(self.cr): |
||||
|
# We can verifies that the given (uid, token) is authorized for |
||||
|
# the database |
||||
|
res_users_obj.check(common.DB, uid, token) |
||||
|
|
||||
|
# we are able to login with the new token |
||||
|
res = res_users_obj.authenticate(common.DB, 'admin', token, None) |
||||
|
self.assertTrue(res) |
||||
|
|
||||
|
@unittest.skipIf(os.environ.get('TRAVIS'), |
||||
|
'When run by travis, tests runs on a database with all ' |
||||
|
'required addons from server-tools and their dependencies' |
||||
|
' installed. Even if `auth_from_http_remote_user` does ' |
||||
|
'not require the `mail` module, The previous installation' |
||||
|
' of the mail module has created the column ' |
||||
|
'`notification_email_send` as REQUIRED into the table ' |
||||
|
'res_partner. BTW, it\'s no more possible to copy a ' |
||||
|
'res_user without an intefirty error') |
||||
|
def test_copy(self): |
||||
|
'''Check that the sso_key is not copied on copy |
||||
|
''' |
||||
|
res_users_obj = self.registry('res.users') |
||||
|
vals = {'sso_key': '123'} |
||||
|
res_users_obj.write(self.cr, self.uid, self.uid, vals) |
||||
|
read_vals = res_users_obj.read( |
||||
|
self.cr, self.uid, self.uid, ['sso_key']) |
||||
|
self.assertDictContainsSubset(vals, read_vals) |
||||
|
copy = res_users_obj.copy(self.cr, self.uid, self.uid) |
||||
|
read_vals = res_users_obj.read( |
||||
|
self.cr, self.uid, copy, ['sso_key']) |
||||
|
self.assertFalse(read_vals.get('sso_key')) |
@ -0,0 +1,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Author: Laurent Mignon |
||||
|
# Copyright 2014 'ACSONE SA/NV' |
||||
|
# |
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU Affero General Public License as |
||||
|
# published by the Free Software Foundation, either version 3 of the |
||||
|
# License, or (at your option) any later version. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU Affero General Public License for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU Affero General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
KEY_LENGTH = 16 |
Write
Preview
Loading…
Cancel
Save
Reference in new issue