Simone Orsi
7 years ago
committed by
Damien Crier
13 changed files with 230 additions and 282 deletions
-
2users_ldap_populate/__init__.py
-
4users_ldap_populate/__openerp__.py
-
44users_ldap_populate/model/populate_wizard.py
-
192users_ldap_populate/model/users_ldap.py
-
2users_ldap_populate/models/__init__.py
-
33users_ldap_populate/models/populate_wizard.py
-
155users_ldap_populate/models/users_ldap.py
-
3users_ldap_populate/tests/__init__.py
-
1users_ldap_populate/tests/test_users_ldap_populate.py
-
17users_ldap_populate/view/populate_wizard.xml
-
22users_ldap_populate/view/users_ldap.xml
-
15users_ldap_populate/views/populate_wizard.xml
-
22users_ldap_populate/views/users_ldap.xml
@ -1 +1 @@ |
|||
from . import model |
|||
from . import models |
@ -1,44 +0,0 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# OpenERP, Open Source Management Solution |
|||
# This module copyright (C) 2012 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.osv import orm, fields # pylint: disable=W0402 |
|||
|
|||
|
|||
class CompanyLDAPPopulateWizard(orm.TransientModel): |
|||
_name = 'res.company.ldap.populate_wizard' |
|||
_description = 'Populate users from LDAP' |
|||
_columns = { |
|||
'name': fields.char('Name', size=16), |
|||
'ldap_id': fields.many2one( |
|||
'res.company.ldap', 'LDAP Configuration'), |
|||
'users_created': fields.integer( |
|||
'Number of users created', readonly=True), |
|||
'users_deactivated': fields.integer( |
|||
'Number of users deactivated', readonly=True), |
|||
} |
|||
|
|||
def create(self, cr, uid, vals, context=None): |
|||
ldap_pool = self.pool.get('res.company.ldap') |
|||
if 'ldap_id' in vals: |
|||
vals['users_created'], vals['users_deactivated'] =\ |
|||
ldap_pool.action_populate( |
|||
cr, uid, vals['ldap_id'], context=context) |
|||
return super(CompanyLDAPPopulateWizard, self).create( |
|||
cr, uid, vals, context=None) |
@ -1,192 +0,0 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# OpenERP, Open Source Management Solution |
|||
# This module copyright (C) 2012 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 re |
|||
from openerp.osv import orm, fields # pylint: disable=W0402 |
|||
import ldap |
|||
from openerp import SUPERUSER_ID |
|||
import logging |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
try: |
|||
from ldap.filter import filter_format |
|||
except ImportError: |
|||
_logger.debug('Can not `from ldap.filter import filter_format`.') |
|||
|
|||
|
|||
class CompanyLDAP(orm.Model): |
|||
_inherit = 'res.company.ldap' |
|||
|
|||
_columns = { |
|||
'no_deactivate_user_ids': fields.many2many( |
|||
'res.users', 'res_company_ldap_no_deactivate_user_rel', |
|||
'ldap_id', 'user_id', |
|||
'Users never to deactivate', |
|||
help='List users who never should be deactivated by' |
|||
' the deactivation wizard'), |
|||
'deactivate_unknown_users': fields.boolean( |
|||
'Deactivate unknown users'), |
|||
} |
|||
|
|||
_defaults = { |
|||
'no_deactivate_user_ids': [(6, 0, [SUPERUSER_ID])], |
|||
'deactivate_unknown_users': False, |
|||
} |
|||
|
|||
def action_populate(self, cr, uid, ids, context=None): |
|||
""" |
|||
Prepopulate the user table from one or more LDAP resources. |
|||
|
|||
Obviously, the option to create users must be toggled in |
|||
the LDAP configuration. |
|||
|
|||
Return the number of users created (as far as we can tell). |
|||
""" |
|||
if isinstance(ids, (int, float)): |
|||
ids = [ids] |
|||
|
|||
users_pool = self.pool.get('res.users') |
|||
users_no_before = users_pool.search( |
|||
cr, uid, [], context=context, count=True) |
|||
logger = logging.getLogger('orm.ldap') |
|||
logger.debug("action_populate called on res.company.ldap ids %s", ids) |
|||
|
|||
deactivate_unknown = None |
|||
known_user_ids = [uid] |
|||
for this in self.read(cr, uid, ids, |
|||
[ |
|||
'no_deactivate_user_ids', |
|||
'deactivate_unknown_users', |
|||
], |
|||
context=context, load='_classic_write'): |
|||
if deactivate_unknown is None: |
|||
deactivate_unknown = True |
|||
known_user_ids.extend(this['no_deactivate_user_ids']) |
|||
deactivate_unknown &= this['deactivate_unknown_users'] |
|||
|
|||
if deactivate_unknown: |
|||
logger.debug("will deactivate unknown users") |
|||
|
|||
for conf in self.get_ldap_dicts(cr, ids): |
|||
if not conf['create_user']: |
|||
continue |
|||
attribute_match = re.search( |
|||
r'([a-zA-Z_]+)=\%s', conf['ldap_filter']) |
|||
if attribute_match: |
|||
login_attr = attribute_match.group(1) |
|||
else: |
|||
raise orm.except_orm( |
|||
"No login attribute found", |
|||
"Could not extract login attribute from filter %s" % |
|||
conf['ldap_filter']) |
|||
results = self.get_ldap_entry_dicts(conf) |
|||
for result in results: |
|||
user_id = self.get_or_create_user( |
|||
cr, uid, conf, result[1][login_attr][0], result) |
|||
# this happens if something goes wrong while creating the user |
|||
# or fetching information from ldap |
|||
if not user_id: |
|||
deactivate_unknown = False |
|||
known_user_ids.append(user_id) |
|||
|
|||
users_no_after = users_pool.search( |
|||
cr, uid, [], context=context, count=True) |
|||
users_created = users_no_after - users_no_before |
|||
|
|||
deactivated_users_count = 0 |
|||
if deactivate_unknown: |
|||
deactivated_users_count = self.do_deactivate_unknown_users( |
|||
cr, uid, ids, known_user_ids, context=context) |
|||
|
|||
logger.debug("%d users created", users_created) |
|||
logger.debug("%d users deactivated", deactivated_users_count) |
|||
return users_created, deactivated_users_count |
|||
|
|||
def do_deactivate_unknown_users( |
|||
self, cr, uid, ids, known_user_ids, context=None): |
|||
""" |
|||
Deactivate users not found in last populate run |
|||
""" |
|||
res_users = self.pool.get('res.users') |
|||
unknown_user_ids = [] |
|||
for unknown_user in res_users.read( |
|||
cr, uid, |
|||
res_users.search( |
|||
cr, uid, |
|||
[('id', 'not in', known_user_ids)], |
|||
context=context), |
|||
['login'], |
|||
context=context): |
|||
present_in_ldap = False |
|||
for conf in self.get_ldap_dicts(cr, ids): |
|||
present_in_ldap |= bool(self.get_ldap_entry_dicts( |
|||
conf, user_name=unknown_user['login'])) |
|||
if not present_in_ldap: |
|||
res_users.write( |
|||
cr, uid, unknown_user['id'], {'active': False}, |
|||
context=context) |
|||
unknown_user_ids.append(unknown_user['id']) |
|||
|
|||
return len(unknown_user_ids) |
|||
|
|||
def get_ldap_entry_dicts(self, conf, user_name='*'): |
|||
""" |
|||
Execute ldap query as defined in conf |
|||
|
|||
Don't call self.query because it supresses possible exceptions |
|||
""" |
|||
ldap_filter = filter_format(conf['ldap_filter'] % user_name, ()) |
|||
conn = self.connect(conf) |
|||
conn.simple_bind_s(conf['ldap_binddn'] or '', |
|||
conf['ldap_password'] or '') |
|||
results = conn.search_st(conf['ldap_base'], ldap.SCOPE_SUBTREE, |
|||
ldap_filter.encode('utf8'), None, |
|||
timeout=60) |
|||
conn.unbind() |
|||
|
|||
return results |
|||
|
|||
def populate_wizard(self, cr, uid, ids, context=None): |
|||
""" |
|||
GUI wrapper for the populate method that reports back |
|||
the number of users created. |
|||
""" |
|||
if not ids: |
|||
return |
|||
if isinstance(ids, (int, float)): |
|||
ids = [ids] |
|||
wizard_obj = self.pool.get('res.company.ldap.populate_wizard') |
|||
res_id = wizard_obj.create( |
|||
cr, uid, {'ldap_id': ids[0]}, context=context) |
|||
|
|||
return { |
|||
'name': wizard_obj._description, |
|||
'view_type': 'form', |
|||
'view_mode': 'form', |
|||
'res_model': wizard_obj._name, |
|||
'domain': [], |
|||
'context': context, |
|||
'type': 'ir.actions.act_window', |
|||
'target': 'new', |
|||
'res_id': res_id, |
|||
'nodestroy': True, |
|||
} |
@ -1,2 +1,2 @@ |
|||
from . import users_ldap |
|||
from . import populate_wizard |
|||
from . import users_ldap |
@ -0,0 +1,33 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2012 Therp BV (<http://therp.nl>) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/gpl.html). |
|||
|
|||
from openerp import models, fields, api |
|||
|
|||
|
|||
class CompanyLDAPPopulateWizard(models.TransientModel): |
|||
_name = 'res.company.ldap.populate_wizard' |
|||
_description = 'Populate users from LDAP' |
|||
|
|||
name = fields.Char('Name', size=16) |
|||
ldap_id = fields.Many2one( |
|||
'res.company.ldap', |
|||
'LDAP Configuration' |
|||
) |
|||
users_created = fields.Integer( |
|||
'Number of users created', |
|||
readonly=True |
|||
) |
|||
users_deactivated = fields.Integer( |
|||
'Number of users deactivated', |
|||
readonly=True |
|||
) |
|||
|
|||
@api.model |
|||
@api.returns('self', lambda value: value.id) |
|||
def create(self, vals): |
|||
if 'ldap_id' in vals: |
|||
ldap = self.env['res.company.ldap'].browse(vals['ldap_id']) |
|||
vals['users_created'], vals['users_deactivated'] =\ |
|||
ldap.action_populate() |
|||
return super(CompanyLDAPPopulateWizard, self).create(vals) |
@ -0,0 +1,155 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2012 Therp BV (<http://therp.nl>) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/gpl.html). |
|||
|
|||
import re |
|||
|
|||
from openerp import models, fields, api, _, SUPERUSER_ID |
|||
from openerp.exceptions import UserError |
|||
import logging |
|||
import ldap |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
try: |
|||
from ldap.filter import filter_format |
|||
except ImportError: |
|||
_logger.debug('Can not `from ldap.filter import filter_format`.') |
|||
|
|||
|
|||
class CompanyLDAP(models.Model): |
|||
_inherit = 'res.company.ldap' |
|||
|
|||
no_deactivate_user_ids = fields.Many2many( |
|||
comodel_name='res.users', |
|||
relation='res_company_ldap_no_deactivate_user_rel', |
|||
column1='ldap_id', |
|||
column2='user_id', |
|||
string='Users never to deactivate', |
|||
help='List users who never should be deactivated by' |
|||
' the deactivation wizard', |
|||
default=lambda self: [(6, 0, [SUPERUSER_ID])], |
|||
) |
|||
deactivate_unknown_users = fields.Boolean( |
|||
string='Deactivate unknown users', |
|||
default=False, |
|||
) |
|||
|
|||
@api.multi |
|||
def action_populate(self): |
|||
""" |
|||
Prepopulate the user table from one or more LDAP resources. |
|||
|
|||
Obviously, the option to create users must be toggled in |
|||
the LDAP configuration. |
|||
|
|||
Return the number of users created (as far as we can tell). |
|||
""" |
|||
logger = logging.getLogger('orm.ldap') |
|||
logger.debug( |
|||
"action_populate called on res.company.ldap ids %s", self.ids) |
|||
|
|||
users_model = self.env['res.users'] |
|||
users_count_before = users_model.search_count([]) |
|||
|
|||
deactivate_unknown, known_user_ids = self._check_users() |
|||
if deactivate_unknown: |
|||
logger.debug("will deactivate unknown users") |
|||
for conf in self.get_ldap_dicts(): |
|||
if not conf['create_user']: |
|||
continue |
|||
attribute_match = re.search( |
|||
r'([a-zA-Z_]+)=\%s', conf['ldap_filter']) |
|||
if attribute_match: |
|||
login_attr = attribute_match.group(1) |
|||
else: |
|||
raise UserError( |
|||
_("No login attribute found: " |
|||
"Could not extract login attribute from filter %s") % |
|||
conf['ldap_filter']) |
|||
results = self.get_ldap_entry_dicts(conf) |
|||
for result in results: |
|||
user_id = self.get_or_create_user( |
|||
conf, result[1][login_attr][0], result) |
|||
# this happens if something goes wrong while creating the user |
|||
# or fetching information from ldap |
|||
if not user_id: |
|||
deactivate_unknown = False |
|||
known_user_ids.append(user_id) |
|||
|
|||
users_created = users_model.search_count([]) - users_count_before |
|||
|
|||
deactivated_users_count = 0 |
|||
if deactivate_unknown: |
|||
deactivated_users_count = \ |
|||
self.do_deactivate_unknown_users(known_user_ids) |
|||
|
|||
logger.debug("%d users created", users_created) |
|||
logger.debug("%d users deactivated", deactivated_users_count) |
|||
return users_created, deactivated_users_count |
|||
|
|||
def _check_users(self): |
|||
deactivate_unknown = None |
|||
known_user_ids = [self.env.user.id] |
|||
for item in self.read(['no_deactivate_user_ids', |
|||
'deactivate_unknown_users'], |
|||
load='_classic_write'): |
|||
if deactivate_unknown is None: |
|||
deactivate_unknown = True |
|||
known_user_ids.extend(item['no_deactivate_user_ids']) |
|||
deactivate_unknown &= item['deactivate_unknown_users'] |
|||
return deactivate_unknown, known_user_ids |
|||
|
|||
def get_ldap_entry_dicts(self, conf, user_name='*'): |
|||
"""Execute ldap query as defined in conf. |
|||
|
|||
Don't call self.query because it supresses possible exceptions |
|||
""" |
|||
ldap_filter = filter_format(conf['ldap_filter'] % user_name, ()) |
|||
conn = self.connect(conf) |
|||
conn.simple_bind_s(conf['ldap_binddn'] or '', |
|||
conf['ldap_password'] or '') |
|||
results = conn.search_st(conf['ldap_base'], ldap.SCOPE_SUBTREE, |
|||
ldap_filter.encode('utf8'), None, |
|||
timeout=60) |
|||
conn.unbind() |
|||
return results |
|||
|
|||
def do_deactivate_unknown_users(self, known_user_ids): |
|||
"""Deactivate users not found in last populate run.""" |
|||
unknown_user_ids = [] |
|||
users = self.env['res.users'].search( |
|||
[('id', 'not in', known_user_ids)]) |
|||
for unknown_user in users: |
|||
present_in_ldap = False |
|||
for conf in self.get_ldap_dicts(): |
|||
present_in_ldap |= bool(self.get_ldap_entry_dicts( |
|||
conf, user_name=unknown_user.login)) |
|||
if not present_in_ldap: |
|||
unknown_user.active = False |
|||
unknown_user_ids.append(unknown_user.id) |
|||
return len(unknown_user_ids) |
|||
|
|||
@api.multi |
|||
def populate_wizard(self): |
|||
""" |
|||
GUI wrapper for the populate method that reports back |
|||
the number of users created. |
|||
""" |
|||
if not self: |
|||
return |
|||
wizard_obj = self.env['res.company.ldap.populate_wizard'] |
|||
res_id = wizard_obj.create({'ldap_id': self.id}).id |
|||
|
|||
return { |
|||
'name': wizard_obj._description, |
|||
'view_type': 'form', |
|||
'view_mode': 'form', |
|||
'res_model': wizard_obj._name, |
|||
'domain': [], |
|||
'context': self.env.context, |
|||
'type': 'ir.actions.act_window', |
|||
'target': 'new', |
|||
'res_id': res_id, |
|||
'nodestroy': True, |
|||
} |
@ -1,4 +1 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# © 2016 Therp BV <http://therp.nl> |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
from . import test_users_ldap_populate |
@ -1,17 +0,0 @@ |
|||
<?xml version="1.0"?> |
|||
<openerp> |
|||
<data> |
|||
<record model="ir.ui.view" id="populate_wizard_view"> |
|||
<field name="model">res.company.ldap.populate_wizard</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Add populate button to ldap view"> |
|||
<group> |
|||
<field name="users_created"/> |
|||
<field name="users_deactivated"/> |
|||
<button icon="gtk-ok" string="OK" special="cancel"/> |
|||
</group> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -1,22 +0,0 @@ |
|||
<?xml version="1.0"?> |
|||
<openerp> |
|||
<data> |
|||
<record model="ir.ui.view" id="company_form_view"> |
|||
<field name="name">Add populate button to ldap view</field> |
|||
<field name="model">res.company</field> |
|||
<field name="inherit_id" ref="auth_ldap.company_form_view"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//form[@string='LDAP Configuration']" position="inside"> |
|||
<group string="Populate user database"> |
|||
<field name="deactivate_unknown_users"/> |
|||
<field name="no_deactivate_user_ids" attrs="{'invisible': [('deactivate_unknown_users', '=', False)]}" widget="many2many_tags" /> |
|||
</group> |
|||
<button name="populate_wizard" |
|||
string="Populate" |
|||
type="object" |
|||
colspan="2"/> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
</data> |
|||
</openerp> |
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0"?> |
|||
<odoo> |
|||
<record model="ir.ui.view" id="populate_wizard_view"> |
|||
<field name="model">res.company.ldap.populate_wizard</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Add populate button to ldap view"> |
|||
<group> |
|||
<field name="users_created"/> |
|||
<field name="users_deactivated"/> |
|||
<button icon="gtk-ok" string="OK" special="cancel"/> |
|||
</group> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,22 @@ |
|||
<?xml version="1.0"?> |
|||
<odoo> |
|||
<record model="ir.ui.view" id="company_form_view"> |
|||
<field name="name">Add populate button to ldap view</field> |
|||
<field name="model">base.config.settings</field> |
|||
<field name="inherit_id" ref="auth_ldap.view_general_configuration_form_inherit_auth_ldap"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath name="populate_ldap" expr="//field[@name='ldaps']/form" position="inside"> |
|||
<group string="Populate user database"> |
|||
<field name="deactivate_unknown_users"/> |
|||
<field name="no_deactivate_user_ids" |
|||
attrs="{'invisible': [('deactivate_unknown_users', '=', False)]}" |
|||
widget="many2many_tags" /> |
|||
</group> |
|||
<button name="populate_wizard" |
|||
string="Populate" |
|||
type="object" |
|||
colspan="2"/> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue