"""Test logic for identity.""" from unittest import mock from unittest.mock import patch import flask import pytest from owsresponse import response from permissions.connectors import neo4j from permissions.logic import identity as identity_logic from permissions.models import resource as model from tests.unit.conftest import get_session_mock def test_get_identity_by_id(): """Test get_identity_by_id.""" identity_id = 'uuid' identity_obj = [{'id': 'uuid', 'email': 'test@theorcahrd.com'}] session_mock = get_session_mock(identity_obj) with mock.patch.object(neo4j, 'db_session', return_value=session_mock): actual = identity_logic.get_identity_by_id(identity_id) assert actual assert actual.message == identity_obj args = session_mock.__enter__().read_transaction.call_args assert args[0][1] == identity_id assert isinstance(args[0][0], type(identity_logic.get_identity_by_id)) @mock.patch('permissions.logic.identity.identity_model') def test_get_identity_by_email(identity_model_mock: mock.MagicMock) -> None: """Test get_identity_by_email.""" email = 'kat@kollectivenr.com' session_mock = get_session_mock([{'id': 'uuid', 'email': email}]) identity_model_mock.get_identity_by_email.return_value = {'id': 'uuid', 'email': email} with mock.patch.object(neo4j, 'db_session', return_value=session_mock): result = identity_logic.get_identity_by_email(email) identity_model_mock.get_identity_by_email.assert_called_once_with( session=session_mock.__enter__(), email=email, ) assert result == {'id': 'uuid', 'email': email} @mock.patch('permissions.models.resource.get_vendor_star_for_admin_by_identity_id') @mock.patch('permissions.logic.identity.g') @mock.patch('permissions.logic.identity.pythonfeatures') def test_is_identity_super_admin_user_not_in_flag( pythonfeatures_mock: mock.MagicMock, g_mock: mock.MagicMock, get_vend_star_mock: mock.MagicMock, app_context: flask.ctx.AppContext, ) -> None: """Test behavior of is_identity_super_admin when user is not in flag.""" pythonfeatures_mock.get_single_feature.return_value = mock.Mock(message='control') request_context = mock.Mock() identity_id = '🐍-🐍-🐍-🐍' result = identity_logic.is_identity_super_admin( request_context=request_context, identity_id=identity_id ) assert result is False pythonfeatures_mock.get_single_feature.assert_called_with( 'settings_app_edit_super_admins', request_context ) g_mock.log.info.assert_called_with( 'User is not in feature flag', resources={'identity_id': identity_id} ) get_vend_star_mock.assert_not_called() @pytest.mark.parametrize( ['vendor_star_result', 'return_value'], [ pytest.param(None, False, id='no vendor * access'), pytest.param(mock.Mock(), True, id='has vendor * access'), ], ) @mock.patch('permissions.models.resource.get_vendor_star_for_admin_by_identity_id') @mock.patch('permissions.connectors.neo4j.db_session') @mock.patch('permissions.logic.identity.g') @mock.patch('permissions.logic.identity.pythonfeatures') def test_is_identity_super_admin_user_is_in_flag( pythonfeatures_mock: mock.MagicMock, g_mock: mock.MagicMock, db_session_mock: mock.MagicMock, get_vend_star_mock: mock.MagicMock, vendor_star_result: response.Response, return_value: bool, app_context: flask.ctx.AppContext, ) -> None: """Test behavior of is_identity_super_admin when user is in flag.""" pythonfeatures_mock.get_single_feature.return_value = mock.Mock(message='enabled') get_vend_star_mock.return_value = vendor_star_result identity_id = '🐍-🐍-🐍-🐍' session_mock = mock.Mock() db_session_mock.return_value.__enter__.return_value = session_mock result = identity_logic.is_identity_super_admin( request_context=mock.Mock(), identity_id=identity_id ) assert result is return_value get_vend_star_mock.assert_called_with( identity_id=identity_id, session=session_mock, ) if not vendor_star_result: g_mock.log.info.assert_called_with( 'User does not have vendor * access', resources={'identity_id': identity_id} ) @mock.patch('permissions.logic.identity.auth0_model') @mock.patch('permissions.logic.identity.identity_model') @mock.patch('permissions.logic.identity.g') def test_deactivate_identity( g_mock: mock.MagicMock, identity_model_mock: mock.MagicMock, auth0_model_mock: mock.MagicMock, app_context: flask.ctx.AppContext, ) -> None: """Test deactivate_identity deactivates in neo4j and auth0.""" session_mock = mock.Mock() identity_mock = mock.Mock(id='user-uuid', auth0_user_id='hexvalue') identity_mock.is_pending.return_value = False identity_logic.deactivate_identity( session=session_mock, identity=identity_mock, admin_id='admin-uuid' ) identity_model_mock.update_identity_active_status.assert_called_once_with( session=session_mock, identity_id='user-uuid', active='N', audit_user_id='admin-uuid', ) auth0_model_mock.activate_deactivate_user.assert_called_once_with( auth0_id='hexvalue', active=False ) @mock.patch('permissions.logic.identity.auth0_model') @mock.patch('permissions.logic.identity.identity_model') @mock.patch('permissions.logic.identity.g') def test_deactivate_identity_pending_user_skips_auth0( g_mock: mock.MagicMock, identity_model_mock: mock.MagicMock, auth0_model_mock: mock.MagicMock, app_context: flask.ctx.AppContext, ) -> None: """Test deactivate_identity skips the auth0 call for a pending user.""" session_mock = mock.Mock() identity_mock = mock.Mock(id='user-uuid', auth0_user_id='user-uuid') identity_mock.is_pending.return_value = True identity_logic.deactivate_identity( session=session_mock, identity=identity_mock, admin_id='admin-uuid' ) identity_model_mock.update_identity_active_status.assert_called_once_with( session=session_mock, identity_id='user-uuid', active='N', audit_user_id='admin-uuid', ) auth0_model_mock.activate_deactivate_user.assert_not_called() g_mock.log.info.assert_called_with( 'Pending user has no auth0 user yet; skipping auth0 deactivation', resources={'identity_id': 'user-uuid'}, ) @pytest.mark.parametrize( ('identity_result', 'activate_result', 'expected'), [ ( # active identity found response.Response(message={'id': 'user-uuid', 'active': 'Y'}), None, response.Response({'id': 'user-uuid', 'active': 'Y'}, status=202), ), ( # Identity not found response.create_not_found_response(), None, response.create_not_found_response(), ), ( # Inactive identity found and activation succeeds. response.Response(message={'id': 'user-uuid', 'active': 'N'}), response.Response({'neo4j': 'success'}), response.Response({'neo4j': 'success'}), ), ( # Inactive identity found but there is a neo4j error. response.Response(message={'id': 'user-uuid', 'active': 'N'}), response.create_fatal_response('Some Neo4j error'), response.create_fatal_response('Some Neo4j error'), ), ], ) @patch('permissions.logic.identity.g') def test_activate_user( _, identity_result: response.Response, activate_result: response.Response, expected: response.Response, app_context, ): """Test activate_user.""" admin_id = 'admin-uuid' admin_context = { 'identity_id': admin_id, 'profile_type': 'InsightsProfile', 'profile_id': 12, } identity_id = 'user-uuid' with patch.object(model, 'activate_user', return_value=activate_result), patch.object( identity_logic, 'get_identity_by_id', return_value=identity_result ): actual = identity_logic.activate_user(admin_context, identity_id) assert actual.status == expected.status if expected.status == 202: # no change identity_logic.get_identity_by_id.assert_called_once_with(identity_id) assert model.activate_user.call_count == 0 elif expected: # success identity_logic.get_identity_by_id.assert_called_once_with(identity_id) model.activate_user.assert_called_once_with(admin_id=admin_id, identity_id=identity_id) elif not expected: assert actual.errors == expected.errors