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