Browse Source
Merge pull request #287 from hbrunn/8.0-users_ldap_push
Merge pull request #287 from hbrunn/8.0-users_ldap_push
[ADD] users_ldap_pushpull/353/head
Stefan Rijnhart (Opener)
9 years ago
16 changed files with 732 additions and 0 deletions
-
84users_ldap_push/README.rst
-
21users_ldap_push/__init__.py
-
44users_ldap_push/__openerp__.py
-
23users_ldap_push/models/__init__.py
-
64users_ldap_push/models/res_company_ldap.py
-
40users_ldap_push/models/res_company_ldap_field_mapping.py
-
31users_ldap_push/models/res_partner.py
-
175users_ldap_push/models/res_users.py
-
3users_ldap_push/security/ir.model.access.csv
-
BINusers_ldap_push/static/description/icon.png
-
20users_ldap_push/tests/__init__.py
-
104users_ldap_push/tests/test_users_ldap_push.py
-
27users_ldap_push/views/res_company.xml
-
44users_ldap_push/views/res_users.xml
-
20users_ldap_push/wizards/__init__.py
-
32users_ldap_push/wizards/change_password_user.py
@ -0,0 +1,84 @@ |
|||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
|||
:alt: License: AGPL-3 |
|||
|
|||
================== |
|||
Push users to LDAP |
|||
================== |
|||
|
|||
This module was written in order to use Odoo as a frontend for creating LDAP |
|||
entries by creating user records. Updates to the user record will be propagated |
|||
to the linked LDAP entry afterwards. |
|||
|
|||
When users change their passwords, they will be updated in the LDAP directory |
|||
too. |
|||
|
|||
Configuration |
|||
============= |
|||
|
|||
On the LDAP parameters of your company, check *Create ldap entry* in order to |
|||
activate this functionality. Be sure to configure a bind DN that has |
|||
appropriate permissions to create and modify entries. |
|||
|
|||
Fill in the object classes newly created entries should contain, separated by |
|||
colons. Those classes will determine which mappings from Odoo fields to LDAP |
|||
attributes you need. This is highly dependent on your LDAP setup. |
|||
|
|||
For a standard slapd setup, you might want to use object classes |
|||
`inetOrgPerson,shadowAccount` and the following mapping: |
|||
|
|||
========== ============== == |
|||
Odoo field LDAP attribute DN |
|||
========== ============== == |
|||
Login userid X |
|||
Name cn |
|||
Name sn |
|||
========== ============== == |
|||
|
|||
Matching is done by the new field *ldap_entry_dn*, so after installing this |
|||
module, you'll probably want to set this field. The module will write it when |
|||
a user logs in via Odoo. |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
When you create or update users, their corresponding LDAP entries will be |
|||
updated too. |
|||
|
|||
When creating users, there's a checkbox 'LDAP user' which allows you to push |
|||
the new user to your LDAP directory. This of course only works if you have |
|||
field mappings for all mandatory fields in your schema. |
|||
|
|||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas |
|||
:alt: Try me on Runbot |
|||
:target: https://runbot.odoo-community.org/runbot/149/8.0 |
|||
|
|||
Bug Tracker |
|||
=========== |
|||
|
|||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_. |
|||
In case of trouble, please check there if your issue has already been reported. |
|||
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback |
|||
`here <https://github.com/OCA/server-tools/issues/new?body=module:%20users_ldap_push%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Holger Brunn <hbrunn@therp.nl> |
|||
|
|||
Maintainer |
|||
---------- |
|||
|
|||
.. image:: https://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: https://odoo-community.org |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose |
|||
mission is to support the collaborative development of Odoo features and |
|||
promote its widespread use. |
|||
|
|||
To contribute to this module, please visit http://odoo-community.org. |
@ -0,0 +1,21 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# This module copyright (C) 2015 Therp BV <http://therp.nl>. |
|||
# |
|||
# 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 models |
|||
from . import wizards |
@ -0,0 +1,44 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# This module copyright (C) 2015 Therp BV <http://therp.nl>. |
|||
# |
|||
# 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": "Push users to LDAP", |
|||
"version": "8.0.1.0.0", |
|||
"author": "Therp BV,Odoo Community Association (OCA)", |
|||
"license": "AGPL-3", |
|||
"category": "Authentication", |
|||
"summary": "Creates a ldap entry when you create a user in Odoo", |
|||
"depends": [ |
|||
'auth_ldap', |
|||
'mail', |
|||
], |
|||
"data": [ |
|||
"views/res_users.xml", |
|||
"views/res_company.xml", |
|||
'security/ir.model.access.csv', |
|||
], |
|||
"qweb": [ |
|||
], |
|||
"test": [ |
|||
], |
|||
"installable": True, |
|||
"external_dependencies": { |
|||
'python': ['ldap'], |
|||
}, |
|||
} |
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# This module copyright (C) 2015 Therp BV <http://therp.nl>. |
|||
# |
|||
# 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 res_company_ldap |
|||
from . import res_company_ldap_field_mapping |
|||
from . import res_users |
|||
from . import res_partner |
@ -0,0 +1,64 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# This module copyright (C) 2015 Therp BV (<http://therp.nl>). |
|||
# |
|||
# 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 _, models, fields, api, exceptions |
|||
|
|||
|
|||
class ResCompanyLdap(models.Model): |
|||
_inherit = 'res.company.ldap' |
|||
|
|||
@api.model |
|||
def _create_ldap_entry_field_mappings_default(self): |
|||
return [ |
|||
(0, 0, { |
|||
'field_id': |
|||
self.env.ref('base.field_res_users_login').id, |
|||
'attribute': 'userid', |
|||
'use_for_dn': True, |
|||
}), |
|||
] |
|||
|
|||
create_ldap_entry = fields.Boolean('Create ldap entry', default=True) |
|||
create_ldap_entry_base = fields.Char( |
|||
'Create ldap entry in subtree', |
|||
help='Leave empty to use your LDAP base') |
|||
create_ldap_entry_objectclass = fields.Char( |
|||
'Object class', default='account', |
|||
help='Separate object classes by comma if you need more than one') |
|||
create_ldap_entry_field_mappings = fields.One2many( |
|||
'res.company.ldap.field_mapping', 'ldap_id', string='Field mappings', |
|||
default=_create_ldap_entry_field_mappings_default) |
|||
|
|||
@api.model |
|||
def get_or_create_user(self, conf, login, ldap_entry): |
|||
user_id = super(ResCompanyLdap, self).get_or_create_user( |
|||
conf, login, ldap_entry) |
|||
if user_id: |
|||
self.env['res.users'].browse(user_id).write({ |
|||
'ldap_entry_dn': ldap_entry[0], |
|||
}) |
|||
return user_id |
|||
|
|||
@api.constrains('create_ldap_entry_field_mappings') |
|||
def _constrain_create_ldap_entry_field_mappings(self): |
|||
for this in self: |
|||
if len(this.create_ldap_entry_field_mappings |
|||
.filtered('use_for_dn')) != 1: |
|||
raise exceptions.ValidationError( |
|||
_('You need to set exactly one mapping as DN')) |
@ -0,0 +1,40 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# This module copyright (C) 2015 Therp BV (<http://therp.nl>). |
|||
# |
|||
# 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 models, fields, api |
|||
|
|||
|
|||
class ResCompanyLdapFieldMapping(models.Model): |
|||
_name = 'res.company.ldap.field_mapping' |
|||
_description = 'Mapping from Odoo fields to ldap attributes' |
|||
|
|||
field_id = fields.Many2one( |
|||
'ir.model.fields', string='Odoo field', required=True, |
|||
domain=lambda self: self._field_id_domain()) |
|||
attribute = fields.Char('LDAP attribute', required=True) |
|||
use_for_dn = fields.Boolean('DN') |
|||
ldap_id = fields.Many2one( |
|||
'res.company.ldap', string='LDAP configuration', required=True) |
|||
|
|||
@api.model |
|||
def _field_id_domain(self): |
|||
return [ |
|||
('model_id', '=', self.env.ref('base.model_res_users').id), |
|||
('ttype', 'in', ['selection', 'char', 'text', 'integer', 'float']), |
|||
] |
@ -0,0 +1,31 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# This module copyright (C) 2015 Therp BV (<http://therp.nl>). |
|||
# |
|||
# 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 models, api |
|||
|
|||
|
|||
class ResPartner(models.Model): |
|||
_inherit = 'res.partner' |
|||
|
|||
@api.multi |
|||
def write(self, vals): |
|||
result = super(ResPartner, self).write(vals) |
|||
self.filtered('user_ids.is_ldap_user').mapped('user_ids')\ |
|||
.push_to_ldap(vals) |
|||
return result |
@ -0,0 +1,175 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# This module copyright (C) 2015 Therp BV (<http://therp.nl>). |
|||
# |
|||
# 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/>. |
|||
# |
|||
############################################################################## |
|||
import ldap |
|||
import ldap.modlist |
|||
import logging |
|||
from openerp import _, models, fields, api, exceptions |
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
|
|||
class ResUsers(models.Model): |
|||
_inherit = 'res.users' |
|||
|
|||
ldap_entry_dn = fields.Char('LDAP DN', readonly=True) |
|||
is_ldap_user = fields.Boolean( |
|||
'LDAP user', compute='_compute_is_ldap_user', default=True) |
|||
|
|||
@api.model |
|||
@api.returns('self', lambda record: record.id) |
|||
def create(self, values): |
|||
result = super(ResUsers, self).create(values) |
|||
result.push_to_ldap(values) |
|||
return result |
|||
|
|||
@api.multi |
|||
def write(self, values): |
|||
result = super(ResUsers, self).write(values) |
|||
self.push_to_ldap(values) |
|||
return result |
|||
|
|||
@api.multi |
|||
def _push_to_ldap_possible(self, values): |
|||
return bool(self._get_ldap_configuration()) |
|||
|
|||
@api.multi |
|||
def _get_ldap_configuration(self): |
|||
self.ensure_one() |
|||
return self.sudo().company_id.ldaps.filtered('create_ldap_entry')[:1] |
|||
|
|||
@api.multi |
|||
def _get_ldap_values(self, values): |
|||
self.ensure_one() |
|||
conf = self._get_ldap_configuration() |
|||
result = {} |
|||
for mapping in conf.create_ldap_entry_field_mappings: |
|||
field_name = mapping.field_id.name |
|||
if field_name not in values or not values[field_name]: |
|||
continue |
|||
result[mapping.attribute] = [str(values[field_name])] |
|||
if result: |
|||
result['objectClass'] = conf.create_ldap_entry_objectclass\ |
|||
.encode('utf-8').split(',') |
|||
return result |
|||
|
|||
@api.multi |
|||
def _get_ldap_dn(self, values): |
|||
self.ensure_one() |
|||
conf = self._get_ldap_configuration() |
|||
dn = conf.create_ldap_entry_field_mappings.filtered('use_for_dn') |
|||
assert dn, 'No DN attribute mapping given!' |
|||
assert self[dn.field_id.name], 'DN attribute empty!' |
|||
return '%s=%s,%s' % ( |
|||
dn.attribute, |
|||
ldap.dn.escape_dn_chars(self[dn.field_id.name].encode('utf-8')), |
|||
conf.create_ldap_entry_base or conf.ldap_base) |
|||
|
|||
@api.multi |
|||
def push_to_ldap(self, values): |
|||
for this in self: |
|||
if not values.get('is_ldap_user') and not this.is_ldap_user: |
|||
continue |
|||
if not this._push_to_ldap_possible(values): |
|||
continue |
|||
ldap_values = this._get_ldap_values(values) |
|||
if not ldap_values: |
|||
continue |
|||
ldap_configuration = this._get_ldap_configuration() |
|||
ldap_connection = ldap_configuration.connect( |
|||
ldap_configuration.read()[0]) |
|||
ldap_connection.simple_bind_s( |
|||
(ldap_configuration.ldap_binddn or '').encode('utf-8'), |
|||
(ldap_configuration.ldap_password or '').encode('utf-8')) |
|||
|
|||
try: |
|||
if not this.ldap_entry_dn: |
|||
this._push_to_ldap_create( |
|||
ldap_connection, ldap_configuration, values, |
|||
ldap_values) |
|||
if this.ldap_entry_dn: |
|||
this._push_to_ldap_write( |
|||
ldap_connection, ldap_configuration, values, |
|||
ldap_values) |
|||
except ldap.LDAPError as e: |
|||
_logger.exception(e) |
|||
raise exceptions.Warning(_('Error'), e.message) |
|||
finally: |
|||
ldap_connection.unbind_s() |
|||
|
|||
@api.multi |
|||
def _push_to_ldap_create(self, ldap_connection, ldap_configuration, values, |
|||
ldap_values): |
|||
self.ensure_one() |
|||
dn = self._get_ldap_dn(values) |
|||
ldap_connection.add_s( |
|||
dn, |
|||
ldap.modlist.addModlist(ldap_values)) |
|||
self.write({'ldap_entry_dn': dn}) |
|||
|
|||
@api.multi |
|||
def _push_to_ldap_write(self, ldap_connection, ldap_configuration, values, |
|||
ldap_values): |
|||
self.ensure_one() |
|||
dn = self.ldap_entry_dn.encode('utf-8') |
|||
dn_mapping = ldap_configuration.create_ldap_entry_field_mappings\ |
|||
.filtered('use_for_dn') |
|||
if dn_mapping.attribute in ldap_values: |
|||
ldap_values.pop(dn_mapping.attribute) |
|||
ldap_entry = ldap_connection.search_s( |
|||
dn, ldap.SCOPE_BASE, '(objectClass=*)', |
|||
map(lambda x: x.encode('utf-8'), ldap_values.keys())) |
|||
assert ldap_entry, '%s not found!' % self.ldap_entry_dn |
|||
ldap_entry = ldap_entry[0][1] |
|||
ldap_connection.modify_s( |
|||
dn, |
|||
ldap.modlist.modifyModlist(ldap_entry, ldap_values)) |
|||
|
|||
@api.one |
|||
@api.depends('ldap_entry_dn') |
|||
def _compute_is_ldap_user(self): |
|||
self.is_ldap_user = bool(self.ldap_entry_dn) |
|||
|
|||
@api.one |
|||
def _change_ldap_password(self, new_passwd, auth_dn=None, |
|||
auth_passwd=None): |
|||
ldap_configuration = self.env.user.sudo()._get_ldap_configuration() |
|||
ldap_connection = ldap_configuration.connect( |
|||
ldap_configuration.read()[0]) |
|||
dn = auth_dn or ldap_configuration.ldap_binddn |
|||
old_passwd = auth_passwd or ldap_configuration.ldap_password |
|||
ldap_connection.simple_bind_s( |
|||
dn.encode('utf-8'), old_passwd.encode('utf-8')) |
|||
self.env['ir.model.access'].check('res.users', 'write') |
|||
self.env.user.check_access_rule('write') |
|||
try: |
|||
ldap_connection.passwd_s( |
|||
self.ldap_entry_dn, None, new_passwd.encode('utf-8')) |
|||
except ldap.LDAPError, e: |
|||
raise exceptions.Warning(_('Error'), e.message) |
|||
finally: |
|||
ldap_connection.unbind_s() |
|||
return True |
|||
|
|||
@api.model |
|||
def change_password(self, old_passwd, new_passwd): |
|||
if self.env.user.is_ldap_user: |
|||
return self.env.user._change_ldap_password( |
|||
new_passwd, auth_dn=self.env.user.ldap_entry_dn, |
|||
auth_passwd=old_passwd) |
|||
return super(ResUsers, self).change_password(old_passwd, new_passwd) |
@ -0,0 +1,3 @@ |
|||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" |
|||
read_field_mappings,Read field mappings,model_res_company_ldap_field_mapping,,1,0,0,0 |
|||
crud_field_mappings,CRUD field mappings,model_res_company_ldap_field_mapping,base.group_system,1,1,1,1 |
After Width: 128 | Height: 128 | Size: 9.2 KiB |
@ -0,0 +1,20 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# This module copyright (C) 2015 Therp BV <http://therp.nl>. |
|||
# |
|||
# 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_users_ldap_push |
@ -0,0 +1,104 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# This module copyright (C) 2015 Therp BV <http://therp.nl>. |
|||
# |
|||
# 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/>. |
|||
# |
|||
############################################################################## |
|||
import ldap |
|||
from openerp.tests.common import TransactionCase |
|||
|
|||
|
|||
class FakeLdapConnection(object): |
|||
def __init__(self): |
|||
self.entries = {} |
|||
|
|||
def simple_bind_s(self, dn, passwd): |
|||
pass |
|||
|
|||
def add_s(self, dn, modlist): |
|||
self.entries[dn] = modlist |
|||
|
|||
def search_s(self, dn, scope, ldap_filter, attributes): |
|||
if dn in self.entries: |
|||
return [(dn, dict(self.entries[dn]))] |
|||
return None |
|||
|
|||
def modify_s(self, dn, modlist): |
|||
if dn not in self.entries: |
|||
raise ldap.NO_SUCH_OBJECT() |
|||
for operation, attribute, value in modlist: |
|||
if operation == ldap.MOD_ADD: |
|||
self.entries[dn].append((attribute, value)) |
|||
continue |
|||
|
|||
def unbind_s(self): |
|||
pass |
|||
|
|||
|
|||
class TestUsersLdapPush(TransactionCase): |
|||
def test_users_ldap_push(self): |
|||
company = self.env['res.company'].create({ |
|||
'name': 'testcompany', |
|||
'ldaps': [(0, 0, { |
|||
'ldap_base': 'dc=test', |
|||
'ldap_filter': '(uid=%s)', |
|||
'create_ldap_entry_field_mappings': [ |
|||
( |
|||
0, 0, { |
|||
'field_id': |
|||
self.env.ref('base.field_res_users_login').id, |
|||
'attribute': 'userid', |
|||
'use_for_dn': True, |
|||
}, |
|||
), |
|||
( |
|||
0, 0, { |
|||
'field_id': |
|||
self.env.ref('base.field_res_users_name').id, |
|||
'attribute': 'sn', |
|||
}, |
|||
), |
|||
], |
|||
})], |
|||
}) |
|||
fake_ldap = FakeLdapConnection() |
|||
self.env['res.company.ldap']._patch_method( |
|||
'connect', lambda x, y: fake_ldap) |
|||
user = self.env['res.users'].create({ |
|||
'name': 'testuser', |
|||
'login': 'testuser', |
|||
'company_ids': [(6, 0, company.ids)], |
|||
'company_id': company.id, |
|||
'is_ldap_user': False, |
|||
}) |
|||
self.assertFalse(user.ldap_entry_dn) |
|||
user.unlink() |
|||
user = self.env['res.users'].create({ |
|||
'name': 'testuser', |
|||
'login': 'testuser', |
|||
'company_ids': [(6, 0, company.ids)], |
|||
'company_id': company.id, |
|||
'is_ldap_user': True, |
|||
}) |
|||
self.assertTrue(fake_ldap.entries[user.ldap_entry_dn]) |
|||
self.assertEqual( |
|||
dict(fake_ldap.entries[user.ldap_entry_dn])['userid'], |
|||
[user.login]) |
|||
user.partner_id.write({'name': 'testuser2'}) |
|||
self.assertTrue([ |
|||
v for a, v in fake_ldap.entries[user.ldap_entry_dn] |
|||
if v == ['testuser2'] |
|||
]) |
@ -0,0 +1,27 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<openerp> |
|||
<data> |
|||
<record id="company_form_view" model="ir.ui.view"> |
|||
<field name="model">res.company</field> |
|||
<field name="inherit_id" ref="auth_ldap.company_form_view" /> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//field[@name='ldaps']/form//field[@name='create_user']" position="after"> |
|||
<field name="create_ldap_entry" /> |
|||
</xpath> |
|||
<xpath expr="//field[@name='ldaps']/form/group" position="after"> |
|||
<group name="users_ldap_push" attrs="{'invisible': [('create_ldap_entry', '!=', True)]}"> |
|||
<field name="create_ldap_entry_base" /> |
|||
<field name="create_ldap_entry_objectclass" attrs="{'required': [('create_ldap_entry', '=', True)]}"/> |
|||
<field name="create_ldap_entry_field_mappings" attrs="{'required': [('create_ldap_entry', '=', True)]}"> |
|||
<tree editable="bottom"> |
|||
<field name="field_id" /> |
|||
<field name="attribute" /> |
|||
<field name="use_for_dn" /> |
|||
</tree> |
|||
</field> |
|||
</group> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,44 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<openerp> |
|||
<data> |
|||
<record id="view_users_simple_form" model="ir.ui.view"> |
|||
<field name="model">res.users</field> |
|||
<field name="inherit_id" ref="base.view_users_simple_form" /> |
|||
<field name="arch" type="xml"> |
|||
<field name="email" position="after"> |
|||
<field name="is_ldap_user" invisible="1" readonly="0" /> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
<record id="view_users_form" model="ir.ui.view"> |
|||
<field name="model">res.users</field> |
|||
<field name="inherit_id" ref="base.view_users_form" /> |
|||
<field name="arch" type="xml"> |
|||
<field name="active" position="after"> |
|||
<field name="is_ldap_user" attrs="{'readonly': [('id', '!=', False)], 'invisible': [('id', '!=', False)]}" /> |
|||
<field name="ldap_entry_dn" attrs="{'invisible': [('ldap_entry_dn', '=', False)]}" /> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
<record id="view_users_form_technical" model="ir.ui.view"> |
|||
<field name="model">res.users</field> |
|||
<field name="inherit_id" ref="users_ldap_push.view_users_form" /> |
|||
<field name="groups_id" eval="[(4, ref('base.group_no_one'))]" /> |
|||
<field name="arch" type="xml"> |
|||
<field name="ldap_entry_dn" position="attributes"> |
|||
<attribute name="attrs" /> |
|||
<attribute name="readonly">0</attribute> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
<record id="view_users_search" model="ir.ui.view"> |
|||
<field name="model">res.users</field> |
|||
<field name="inherit_id" ref="base.view_users_search" /> |
|||
<field name="arch" type="xml"> |
|||
<search position="inside"> |
|||
<filter name="is_ldap_user" string="LDAP users" domain="[('ldap_entry_dn', '!=', False)]" /> |
|||
</search> |
|||
</field> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,20 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# This module copyright (C) 2015 Therp BV <http://therp.nl>. |
|||
# |
|||
# 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 change_password_user |
@ -0,0 +1,32 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# This module copyright (C) 2015 Therp BV (<http://therp.nl>). |
|||
# |
|||
# 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 models, api |
|||
|
|||
|
|||
class ChangePasswordUser(models.TransientModel): |
|||
_inherit = 'change.password.user' |
|||
|
|||
@api.multi |
|||
def change_password_button(self): |
|||
for user_line in self.filtered('user_id.is_ldap_user'): |
|||
user_line.user_id._change_ldap_password(user_line.new_passwd) |
|||
user_line.new_passwd = False |
|||
return super(ChangePasswordUser, self.filtered( |
|||
lambda x: not x.user_id.is_ldap_user)).change_password_button() |
Write
Preview
Loading…
Cancel
Save
Reference in new issue