# This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import sys import unittest from unittest.mock import ANY, MagicMock, Mock, patch import ldap3 import trytond.config as config from trytond.modules.ldap_authentication.res import ldap_server, parse_ldap_url from trytond.pool import Pool from trytond.tests.test_tryton import ModuleTestCase, with_transaction section = 'ldap_authentication' class LDAPAuthenticationTestCase(ModuleTestCase): 'Test LDAPAuthentication module' module = 'ldap_authentication' def setUp(self): super().setUp() methods = config.get('session', 'authentications', default='') config.set('session', 'authentications', 'ldap') self.addCleanup(config.set, 'session', 'authentications', methods) config.add_section(section) self.addCleanup(config.remove_section, section) def _get_login( self, uri='ldap://localhost/dc=tryton,dc=org', start_tls=False): pool = Pool() User = pool.get('res.user') config.set(section, 'uri', uri) @patch.object(ldap3, 'Connection') @patch.object(User, 'ldap_search_user') def get_login(login, password, find, ldap_search_user, Connection): con = Connection.return_value = MagicMock() con.__enter__.return_value = con con.bind.return_value = bool(find) if find: ldap_search_user.return_value = [('dn', {'uid': [find]})] else: ldap_search_user.return_value = None user_id = User.get_login(login, { 'password': password, }) if find: Connection.assert_called_with( ANY, ANY, password, auto_bind=ldap3.AUTO_BIND_NONE) if start_tls: con.start_tls.assert_called() else: con.start_tls.assert_not_called() con.bind.assert_called() return user_id return get_login @with_transaction() def test_user_get_login_existing_user(self): "Test User.get_login with existing user" pool = Pool() User = pool.get('res.user') user, = User.search([('login', '=', 'admin')]) get_login = self._get_login() self.assertEqual(get_login('admin', 'admin', 'admin'), user.id) self.assertEqual(get_login('AdMiN', 'admin', 'admin'), user.id) @with_transaction() def test_user_get_login_unknown_user(self): "test User.get_login with unknown user" get_login = self._get_login() self.assertFalse(get_login('foo', 'bar', None)) self.assertFalse(get_login('foo', 'bar', 'foo')) @with_transaction() def test_user_get_login_create_user(self): "Test User.get_login with user to create" pool = Pool() User = pool.get('res.user') config.set(section, 'create_user', 'True') get_login = self._get_login() user_id = get_login('foo', 'bar', 'foo') foo, = User.search([('login', '=', 'foo')]) self.assertEqual(user_id, foo.id) self.assertEqual(foo.name, 'foo') @with_transaction() def test_user_get_login_create_user_case(self): "Test User.get_login with user to create with different case" pool = Pool() User = pool.get('res.user') config.set(section, 'create_user', 'True') get_login = self._get_login() user_id = get_login('BaR', 'foo', 'bar') bar, = User.search([('login', '=', 'bar')]) self.assertEqual(user_id, bar.id) self.assertEqual(bar.name, 'bar') @with_transaction() def test_user_get_login_with_tls(self): "Test User.get_login with TLS" pool = Pool() User = pool.get('res.user') user, = User.search([('login', '=', 'admin')]) get_login = self._get_login( 'ldap+tls://localhost/dc=tryton,dc=org', start_tls=True) self.assertEqual(get_login('admin', 'admin', 'admin'), user.id) @with_transaction() def test_user_get_login_with_ssl(self): "Test User.get_login with SSL" pool = Pool() User = pool.get('res.user') user, = User.search([('login', '=', 'admin')]) get_login = self._get_login('ldaps://localhost/dc=tryton,dc=org') self.assertEqual(get_login('admin', 'admin', 'admin'), user.id) def test_parse_ldap_url(self): 'Test parse_ldap_url' self.assertEqual( parse_ldap_url('ldap:///o=University%20of%20Michigan,c=US')[1], 'o=University of Michigan,c=US') self.assertEqual( parse_ldap_url( 'ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US' )[1], 'o=University of Michigan,c=US') self.assertEqual( parse_ldap_url( 'ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,' 'c=US?postalAddress')[2], 'postalAddress') self.assertEqual( parse_ldap_url( 'ldap://host.com:6666/o=University%20of%20Michigan,' 'c=US??sub?(cn=Babs%20Jensen)')[3:5], ('sub', '(cn=Babs Jensen)')) self.assertEqual( parse_ldap_url( 'ldap:///??sub??bindname=cn=Manager%2co=Foo')[5], {'bindname': ['cn=Manager,o=Foo']}) self.assertEqual( parse_ldap_url( 'ldap:///??sub??!bindname=cn=Manager%2co=Foo')[5], {'!bindname': ['cn=Manager,o=Foo']}) @unittest.skipIf( sys.version_info < (3, 8), "call_args does not have args nor kwargs") def test_ldap_server(self): "Test ldap_server" for uri, (host, tls) in [ ('ldap://localhost/dc=tryton,dc=org', ('ldap://localhost:389', None)), ('ldaps://localhost/dc=tryton,dc=org', ('ldaps://localhost:636', True)), ('ldap+tls://localhost/dc=tryton,dc=org', ('ldap://localhost:389', True)), ]: config.set(section, 'uri', uri) with patch('ldap3.Server') as Server: ldap_server() self.assertEqual(Server.call_args.args, (host,)) if tls: self.assertTrue(Server.call_args.kwargs.get('tls')) else: self.assertFalse(Server.call_args.kwargs.get('tls')) def _ldap_search_user( self, uri='ldap://localhost/dc=tryton,dc=org', auto_bind=ldap3.AUTO_BIND_DEFAULT): pool = Pool() User = pool.get('res.user') config.set(section, 'uri', uri) @patch.object(ldap3, 'Connection') def ldap_search_user(login, attrs, Connection): con = Connection.return_value = MagicMock() con.__enter__.return_value = con con.entries = [Mock()] server = ldap_server() User.ldap_search_user(login, server, attrs=attrs) Connection.assert_called_with( ANY, ANY, ANY, auto_bind=auto_bind) con.search.assert_called() return ldap_search_user @with_transaction() def test_ldap_search_user(self): "Test User.ldap_search_user" ldap_search_user = self._ldap_search_user() ldap_search_user('admin', None) @with_transaction() def test_ldap_search_user_with_tls(self): "Test User.ldap_search_user with TSL" ldap_search_user = self._ldap_search_user( 'ldap+tls://localhost/dc=tryton,dc=org', ldap3.AUTO_BIND_TLS_BEFORE_BIND) ldap_search_user('admin', None) @with_transaction() def test_ldap_search_user_with_ssl(self): "Test User.ldap_search_user with SSL" ldap_search_user = self._ldap_search_user( 'ldaps://localhost/dc=tryton,dc=org') ldap_search_user('admin', None) del ModuleTestCase