# Copyright (C) 2010-2024 RhodeCode GmbH # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License, version 3 # (only), as published by the Free Software Foundation. # # 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 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 . # # This program is dual-licensed. If you wish to learn more about the # RhodeCode Enterprise Edition, including its added features, Support services, # and proprietary license terms, please see https://rhodecode.com/licenses/ import mock import pytest from rhodecode.lib.auth import _RhodeCodeCryptoBCrypt from rhodecode.authentication.base import RhodeCodeAuthPluginBase from rhodecode.authentication.plugins.auth_ldap import RhodeCodeAuthPlugin from rhodecode.model import db class RcTestAuthPlugin(RhodeCodeAuthPluginBase): def name(self): return 'stub_auth' def test_authenticate_returns_from_auth(stub_auth_data): plugin = RcTestAuthPlugin('stub_id') with mock.patch.object(plugin, 'auth') as auth_mock: auth_mock.return_value = stub_auth_data result = plugin._authenticate(mock.Mock(), 'test', 'password', {}) assert stub_auth_data == result def test_authenticate_returns_empty_auth_data(): auth_data = {} plugin = RcTestAuthPlugin('stub_id') with mock.patch.object(plugin, 'auth') as auth_mock: auth_mock.return_value = auth_data result = plugin._authenticate(mock.Mock(), 'test', 'password', {}) assert auth_data == result def test_authenticate_skips_hash_migration_if_mismatch(stub_auth_data): stub_auth_data['_hash_migrate'] = 'new-hash' plugin = RcTestAuthPlugin('stub_id') with mock.patch.object(plugin, 'auth') as auth_mock: auth_mock.return_value = stub_auth_data result = plugin._authenticate(mock.Mock(), 'test', 'password', {}) user = db.User.get_by_username(stub_auth_data['username']) assert user.password != 'new-hash' assert result == stub_auth_data def test_authenticate_migrates_to_new_hash(stub_auth_data): new_password = b'new-password' new_hash = _RhodeCodeCryptoBCrypt().hash_create(new_password) stub_auth_data['_hash_migrate'] = new_hash plugin = RcTestAuthPlugin('stub_id') with mock.patch.object(plugin, 'auth') as auth_mock: auth_mock.return_value = stub_auth_data result = plugin._authenticate( mock.Mock(), stub_auth_data['username'], new_password, {}) user = db.User.get_by_username(stub_auth_data['username']) assert user.password == new_hash assert result == stub_auth_data @pytest.fixture() def stub_auth_data(user_util): user = user_util.create_user() data = { 'username': user.username, 'password': 'password', 'email': 'test@example.org', 'firstname': 'John', 'lastname': 'Smith', 'groups': [], 'active': True, 'admin': False, 'extern_name': 'test', 'extern_type': 'ldap', 'active_from_extern': True } return data class TestRhodeCodeAuthPlugin(object): def setup_method(self, method): self.finalizers = [] self.user = mock.Mock() self.user.username = 'test' self.user.password = 'old-password' self.fake_auth = { 'username': 'test', 'password': 'test', 'email': 'test@example.org', 'firstname': 'John', 'lastname': 'Smith', 'groups': [], 'active': True, 'admin': False, 'extern_name': 'test', 'extern_type': 'ldap', 'active_from_extern': True } def teardown_method(self, method): if self.finalizers: for finalizer in self.finalizers: finalizer() self.finalizers = [] def test_fake_password_is_created_for_the_new_user(self): self._patch() auth_plugin = RhodeCodeAuthPlugin('stub_id') auth_plugin._authenticate(self.user, 'test', 'test', []) self.password_generator_mock.assert_called_once_with(length=16) create_user_kwargs = self.create_user_mock.call_args[1] assert create_user_kwargs['password'] == 'new-password' def test_fake_password_is_not_created_for_the_existing_user(self): self._patch() self.get_user_mock.return_value = self.user auth_plugin = RhodeCodeAuthPlugin('stub_id') auth_plugin._authenticate(self.user, 'test', 'test', []) assert self.password_generator_mock.called is False create_user_kwargs = self.create_user_mock.call_args[1] assert create_user_kwargs['password'] == self.user.password def _patch(self): get_user_patch = mock.patch('rhodecode.model.db.User.get_by_username') self.get_user_mock = get_user_patch.start() self.get_user_mock.return_value = None self.finalizers.append(get_user_patch.stop) create_user_patch = mock.patch( 'rhodecode.model.user.UserModel.create_or_update') self.create_user_mock = create_user_patch.start() self.create_user_mock.return_value = None self.finalizers.append(create_user_patch.stop) auth_patch = mock.patch.object(RhodeCodeAuthPlugin, 'auth') self.auth_mock = auth_patch.start() self.auth_mock.return_value = self.fake_auth self.finalizers.append(auth_patch.stop) password_generator_patch = mock.patch( 'rhodecode.lib.auth.PasswordGenerator.gen_password') self.password_generator_mock = password_generator_patch.start() self.password_generator_mock.return_value = 'new-password' self.finalizers.append(password_generator_patch.stop) def test_missing_ldap(): from rhodecode.model.validators import Missing try: import ldap_not_existing except ImportError: # means that python-ldap is not installed ldap_not_existing = Missing # missing is singleton assert ldap_not_existing == Missing def test_import_ldap(): from rhodecode.model.validators import Missing try: import ldap except ImportError: # means that python-ldap is not installed ldap = Missing # missing is singleton assert False is (ldap == Missing)