Browse Source

[ADD] users_ldap_populate: migrate functionality added to 6.1 after 7.0 port (#408)

* [ADD] possibility to deactivate users not found in ldap while populating

* [IMP] search in ldap for every possibly unknown user to be really sure it
actually is not present there

* [FIX] refactoring mistake

* [IMP] don't use self.query() to be sure to be stopped if any error occurs

* [IMP] remove superfluous check as exceptions are not supressed any more

* [FIX] typo in variable name
[FIX] handle unicode characters in search filter
[FIX] search for user's login, not her name

* [FIX] don't pass user_name as assertion_value

* [FIX] don't deactivate users if we got a non-existent ldap configuration

* [FIX] flake8

* [FIX] more flake8

* [FIX] make form usable

* [FIX] name clash between function and field

* [ADD] test
pull/1049/head
Holger Brunn 7 years ago
committed by Alexandre Fayolle
parent
commit
915b053082
  1. 72
      users_ldap_populate/README.rst
  2. 17
      users_ldap_populate/__openerp__.py
  3. 10
      users_ldap_populate/model/populate_wizard.py
  4. 100
      users_ldap_populate/model/users_ldap.py
  5. 4
      users_ldap_populate/tests/__init__.py
  6. 70
      users_ldap_populate/tests/test_users_ldap_populate.py
  7. 2
      users_ldap_populate/view/populate_wizard.xml
  8. 5
      users_ldap_populate/view/users_ldap.xml

72
users_ldap_populate/README.rst

@ -0,0 +1,72 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:alt: License: AGPL-3
=============
LDAP Populate
=============
This module allows to prepopulate the user database with all entries in the
LDAP database.
Installation
============
In order to schedule the population of the user database on a regular basis,
create a new scheduled action with the following properties:
- Object: res.company.ldap
- Function: action_populate
- Arguments: ``[res.company.ldap.id]``
Substitute ``res.company.ldap.id`` with the actual id of the res.company.ldap
object you want to query.
Usage
=====
To use this module, you need to:
* go to your company settings
* open your LDAP configuration
* click the button ``Populate``
.. 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
For further information, please visit:
* https://www.odoo.com/forum/help-1
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/users_ldap_populate/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/users_ldap_populate/issues/new?body=module:%20users_ldap_populate%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>
* Daniel Reis <dgreis@sapo.pt>
* Stefan Rijnhart <stefan@opener.am>
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 https://odoo-community.org.

17
users_ldap_populate/__openerp__.py

@ -20,24 +20,11 @@
##############################################################################
{
"name": "LDAP Populate",
"summary": "Create users from LDAP before they log in",
"version": "8.0.1.2.0",
"author": "Therp BV,Odoo Community Association (OCA)",
"license": "AGPL-3",
"category": 'Tools',
"description": """
This module allows to prepopulate the user database with all entries in the
LDAP database.
In order to schedule the population of the user database on a regular basis,
create a new scheduled action with the following properties:
- Object: res.company.ldap
- Function: action_populate
- Arguments: [res.company.ldap.id]
Substitute res.company.ldap.id with the actual id of the res.company.ldap
object you want to query.
""",
"category": 'Authentication',
"depends": [
'auth_ldap',
],

10
users_ldap_populate/model/populate_wizard.py

@ -18,8 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
from openerp.osv import orm, fields # pylint: disable=W0402
class CompanyLDAPPopulateWizard(orm.TransientModel):
@ -31,12 +30,15 @@ class CompanyLDAPPopulateWizard(orm.TransientModel):
'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'] = ldap_pool.action_populate(
cr, uid, vals['ldap_id'], context=context)
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)

100
users_ldap_populate/model/users_ldap.py

@ -20,7 +20,9 @@
##############################################################################
import re
from openerp.osv import orm
from openerp.osv import orm, fields # pylint: disable=W0402
import ldap
from openerp import SUPERUSER_ID
import logging
_logger = logging.getLogger(__name__)
@ -34,6 +36,22 @@ except ImportError:
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.
@ -52,6 +70,22 @@ class CompanyLDAP(orm.Model):
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
@ -64,16 +98,72 @@ class CompanyLDAP(orm.Model):
"No login attribute found",
"Could not extract login attribute from filter %s" %
conf['ldap_filter'])
ldap_filter = filter_format(conf['ldap_filter'] % '*', ())
for result in self.query(conf, ldap_filter):
self.get_or_create_user(
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)
return 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):
"""

4
users_ldap_populate/tests/__init__.py

@ -0,0 +1,4 @@
# -*- 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

70
users_ldap_populate/tests/test_users_ldap_populate.py

@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
# © 2016 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests.common import TransactionCase
from contextlib import contextmanager
class patch_ldap_connection(object):
def __init__(self, results):
self.results = results
def simple_bind_s(self, user, password):
return True
def search_st(self, base, scope, ldap_filter, attributes, timeout=None):
if ldap_filter == '(uid=*)':
return self.results
else:
return []
def unbind(self):
return True
@contextmanager
def patch_ldap(self, results):
""" defuse ldap functions to return fake entries instead of talking to a
server. Use this in your own ldap related tests """
import ldap
original_initialize = ldap.initialize
def initialize(uri):
return patch_ldap_connection(results)
ldap.initialize = initialize
yield
ldap.initialize = original_initialize
def get_fake_ldap(self):
company = self.env.ref('base.main_company')
company.write({
'ldaps': [(0, 0, {
'ldap_server': 'fake',
'ldap_port': 'fake',
'ldap_filter': '(uid=%s)',
'ldap_base': 'fake',
'deactivate_unknown_users': True,
'no_deactivate_user_ids': [(6, 0, [
self.env.ref('base.user_root').id,
])],
})],
})
return company.ldaps.filtered(
lambda x: x.ldap_server == 'fake'
)
class TestUsersLdapPopulate(TransactionCase):
def test_users_ldap_populate(self):
with patch_ldap(self, [('DN=fake', {
'cn': ['fake'],
'uid': ['fake'],
'mail': ['fake@fakery.com'],
})]):
get_fake_ldap(self).populate_wizard()
self.assertFalse(self.env.ref('base.user_demo').active)
self.assertTrue(self.env.ref('base.user_root').active)
self.assertTrue(self.env['res.users'].search([
('login', '=', 'fake')
]))

2
users_ldap_populate/view/populate_wizard.xml

@ -2,12 +2,12 @@
<openerp>
<data>
<record model="ir.ui.view" id="populate_wizard_view">
<field name="name">Add populate button to ldap view</field>
<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>

5
users_ldap_populate/view/users_ldap.xml

@ -7,7 +7,10 @@
<field name="inherit_id" ref="auth_ldap.company_form_view"/>
<field name="arch" type="xml">
<xpath expr="//form[@string='LDAP Configuration']" position="inside">
<separator string="Populate user database" colspan="4"/>
<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"

Loading…
Cancel
Save