def test_disassociate_blocked_on_deny(self, mock_fmt_mac, mock_utils): # test with and without return value of the sudo_cmd call for message in ['message', None]: mock_fmt_mac.reset_mock() mock_utils.reset_mock() self._prepare_on_host_mocks(mock_fmt_mac, mock_utils, expected_msg=message) mock_utils.disassociate_user.return_value = 'OK' testuser = UserIdentifier('user', 'aabb') testuser.user_data = UserData() testuser.user_data.join_state = UserData.JOIN_STATE_BLOCKED handler = FirewallAuthHandler() result = handler.on_host_deny(testuser) # ensure the script also gets called when UserData is given self._assert_on_host_call(mock_fmt_mac, mock_utils, '/etc/radguestauth/fw_user_drop.sh', result, expected_msg=message) # blocked user needs to be disassociated as well mock_utils.disassociate_user.assert_called_once_with('aabb') self.assertIn('OK', result)
def test_str_representation(self): # incrementally adds attributes and checks if they appear in str(item). # The str representation is important for several chat commands. identifier = UserIdentifier('foo', 'bar') # str needs to work without password and data self.assertIn('foo', str(identifier)) self.assertIn('bar', str(identifier)) identifier.password = '******' self.assertIn('passw0rd!', str(identifier)) data = UserData() state_str = data.state_string() self.assertIn(state_str, str(data)) # this is 1970-01-01 00:01:40 data.valid_until = 100 data.num_joins = 42 data.max_num_joins = 1234 # keep some freedoms on the actual output self.assertIn('1970', str(data)) self.assertIn('00:01:40', str(data)) self.assertIn('42', str(data)) self.assertIn('1234', str(data)) data_str = str(data) identifier.user_data = data self.assertIn(data_str, str(identifier))
def test_allow_on_may_join(self, mock_usermgr, mock_chat, mock_loader): # prepare test data mock_usermgr_obj = Mock() test_user = UserIdentifier('user', 'aabb') test_user.password = '******' mock_usermgr_obj.find.return_value = test_user mock_usermgr_obj.may_join.return_value = UserData.JOIN_STATE_ALLOWED mock_usermgr.return_value = mock_usermgr_obj mock_auth = self._get_auth_handler_mock(mock_loader) gacore = self._init_and_start() expected_result = (auth.ALLOW, { 'control:Cleartext-Password': '******' }) mock_auth.handle_user_state.return_value = expected_result result = gacore.authorize({ 'User-Name': 'user', 'Calling-Station-Id': 'aabb' }) self._assert_called_once_with_user_id(mock_usermgr_obj.may_join, 'user', 'aabb') self._assert_called_once_with_user_id(mock_auth.handle_user_state, 'user', 'aabb') # User identifier handled above, session ID not given mock_auth.handle_user_state.assert_called_once_with( ANY, UserData.JOIN_STATE_ALLOWED, ANY) mock_usermgr_obj.find.assert_called_once_with('user') self.assertEqual(expected_result, result)
def test_drop_expired_users(self, mock_usermgr, mock_chat, mock_loader): testuser1 = UserIdentifier('user', 'aabb') testuser2 = UserIdentifier('user2', 'aabb2') testuser3 = UserIdentifier('user3', 'aabb3') mock_usermgr_obj = Mock() mock_usermgr_obj.get_expired_users.return_value = [ testuser1, testuser2 ] mock_usermgr_obj.is_request_pending.return_value = True mock_usermgr_obj.get_request.return_value = testuser3 mock_usermgr.return_value = mock_usermgr_obj mock_auth = self._get_auth_handler_mock(mock_loader) gacore = self._init_and_start() gacore.drop_expired_users() mock_usermgr_obj.remove.assert_has_calls( [call(testuser1), call(testuser2)], any_order=True) mock_usermgr_obj.finish_request.assert_called_once() mock_auth.on_host_deny.assert_has_calls( [call(testuser1), call(testuser2), call(testuser3)], any_order=True) # no more calls should have been made self.assertEqual(mock_usermgr_obj.remove.call_count, 2) self.assertEqual(mock_auth.on_host_deny.call_count, 3)
def test_post_auth_timeout_no_valid_until(self, mock_usermgr, mock_chat, mock_loader): testuser = UserIdentifier('user', 'aabb') testuser.user_data = UserData() testuser.user_data.valid_until = None testuser.user_data.max_num_joins = 10 mock_usermgr_obj = Mock() mock_usermgr_obj.find.return_value = testuser mock_usermgr.return_value = mock_usermgr_obj mock_auth = self._get_auth_handler_mock(mock_loader) expected_result = {'control:Test': 'val'} mock_auth.on_post_auth.return_value = expected_result.copy() gacore = self._init_and_start() result = gacore.post_auth({ 'User-Name': 'user', 'Calling-Station-Id': 'aabb' }) mock_auth.on_post_auth.assert_called_once_with( UserIdentifier('user', 'aabb'), ANY) mock_usermgr_obj.find.assert_called_once_with('user') # no timeout should be added without valid_until. self.assertEqual(expected_result, result) self.assertIsNone(result.get('reply:Session-Timeout'))
def test_list_output(self): self.mock_um.list_users.return_value = [ UserIdentifier('someone', '123'), UserIdentifier('someoneElse', 'deviceId1'), ] result = self._exec_list() self.assertFalse('[blocked]' in result)
def test_may_join_other_name_request(self): # also reject if a device ID is used by the current request user mgr, testuser = self._get_mgr_with_one_user() mgr.add_request(UserIdentifier('testuser2', 'device')) result = mgr.may_join(UserIdentifier('other', 'device')) self.assert_state_blocked(result)
def test_post_auth_waiting_add_timeout(self, mock_utils): handler = FirewallAuthHandler() handler.handle_user_state(UserIdentifier('user', 'aabb'), UserData.JOIN_STATE_WAITING, 'session') result = handler.on_post_auth(UserIdentifier('user', 'aabb'), 'session') self.assertIsInstance(result, dict) self.assertIsNotNone(result.get('reply:Session-Timeout'))
def _prepare_timeout_user(self, mock_usermgr): testuser = UserIdentifier('user', 'aabb') testuser.user_data = UserData() test_validity = 600 testuser.user_data.valid_until = time.time() + test_validity mock_usermgr_obj = Mock() mock_usermgr_obj.find.return_value = testuser mock_usermgr.return_value = mock_usermgr_obj return mock_usermgr_obj, test_validity
def test_post_auth_allowed_no_timeout(self, mock_utils): # no timeout should be set for allowed users handler = FirewallAuthHandler() handler.handle_user_state(UserIdentifier('user', 'aabb'), UserData.JOIN_STATE_ALLOWED, 'session') result = handler.on_post_auth(UserIdentifier('user', 'aabb'), 'session') self.assertIsNone(result)
def test_may_join_other_name_different_id_format(self): # also reject when the device IDs are formatted differently data = UserData() data.max_num_joins = 1 data.valid_until = time.time() + 10000 user = UserIdentifier('foo', 'AB-C0') mgr = self._get_mgr_with(user, data) result = mgr.may_join(UserIdentifier('other', 'ab:c0')) self.assert_state_blocked(result)
def test_may_join_pending_request(self): mgr, testuser = self._get_mgr_with_one_user() mgr.add_request(UserIdentifier('other', 'device')) # request user should be in waiting state, but others # still in new state. result = mgr.may_join(UserIdentifier('other', 'device')) result_new_user = mgr.may_join(UserIdentifier('other2', 'device2')) self.assert_state_waiting(result) self.assert_state_new(result_new_user)
def test_mac_formatting(self): test_mac = 'aa:bb:cc:dd:e0:12' items_to_check = [ test_mac, 'AA:BB:CC:DD:E0:12', 'AA-BB-CC-DD-E0-12', 'aa-bb-cc-dd-e0-12', 'aa-bb-cc:Dd-e0-12' ] for addr in items_to_check: formatted_obj = UserIdentifier('a', addr).device_id_as_mac() formatted_static = UserIdentifier.format_mac(addr) self.assertEqual(formatted_obj, test_mac) self.assertEqual(formatted_static, test_mac)
def test_user_allowed(self): user = UserIdentifier('user', '') user.password = '******' handler = DefaultAuthHandler() expected_result = (auth.ALLOW, { 'control:Cleartext-Password': '******' }) result = handler.handle_user_state(user, UserData.JOIN_STATE_ALLOWED, '') self.assertEqual(expected_result, result)
def test_add_request_duplicate(self): mgr = UserManager() testuser = UserIdentifier('foo', 'bar') self.assertFalse(mgr.is_request_pending()) res1 = mgr.add_request(testuser) res2 = mgr.add_request(UserIdentifier('test', '2')) self.assertTrue(res1) self.assertFalse(res2) self.assertTrue(mgr.is_request_pending()) self.assertEqual(testuser, mgr.get_request()) self.assertListEqual(list(mgr.list_users()), [])
def _get_mgr_with_two_users(self): mgr = UserManager() testuser1 = UserIdentifier('foo', 'bar') testuser2 = UserIdentifier('john doe', 'phone') mgr.add_request(testuser1) mgr.update(testuser1) mgr.finish_request() mgr.add_request(testuser2) mgr.update(testuser2) mgr.finish_request() return mgr, testuser1, testuser2
def test_check_expired_pass_to_data(self): # the UserIdentifier call should be forwarded to the # UserData item data_mock = Mock(UserData) data_mock.check_expired.return_value = True user_id = UserIdentifier('name', 'device') user_id.user_data = data_mock self.assertTrue(user_id.check_expired()) data_mock.check_expired.assert_called_once() data_mock.reset_mock() self.assertTrue(user_id.check_expired(True)) data_mock.check_expired.assert_called_once_with(True)
def test_allow_known_on_pending_request(self, mock_usermgr, mock_chat, mock_loader): # prepare test data mock_usermgr_obj = Mock() # state WAITING means there is a request for this user mock_usermgr_obj.may_join.return_value = UserData.JOIN_STATE_WAITING mock_usermgr_obj.get_request.return_value = UserIdentifier( 'user', 'aabb') mock_usermgr.return_value = mock_usermgr_obj mock_auth = self._get_auth_handler_mock(mock_loader) gacore = self._init_and_start() expected_result = (auth.ALLOW, { 'control:Cleartext-Password': '******' }) mock_auth.handle_user_state.return_value = expected_result result = gacore.authorize({ 'User-Name': 'user', 'Calling-Station-Id': 'aabb' }) # no request should have been added and the user should be rejected. self._assert_called_once_with_user_id(mock_usermgr_obj.may_join, 'user', 'aabb') mock_usermgr_obj.get_request.assert_called_once() mock_usermgr_obj.add_request.assert_not_called() self._assert_called_once_with_user_id(mock_auth.handle_user_state, 'user', 'aabb') # User identifier handled above, session ID not given mock_auth.handle_user_state.assert_called_once_with( ANY, UserData.JOIN_STATE_WAITING, ANY) self.assertEqual(expected_result, result)
def test_reject_only_when_blocked(self): user = UserIdentifier('user', '') user.password = '******' expected_reject = (auth.REJECT, None) expected_accept = (auth.ALLOW, {'control:Cleartext-Password': '******'}) result_block = AuthUtils.reject_only_when_blocked( user, UserData.JOIN_STATE_BLOCKED) result_waiting = AuthUtils.reject_only_when_blocked( user, UserData.JOIN_STATE_WAITING) result_ok = AuthUtils.reject_only_when_blocked( user, UserData.JOIN_STATE_ALLOWED) self.assertEqual(expected_reject, result_block) self.assertEqual(expected_accept, result_waiting) self.assertEqual(expected_accept, result_ok)
def test_post_auth_without_handle_state(self): # without handle_user_state call, the VLAN assignment should always # be the network without connectivity to the outside world handler = VlanAuthHandler() result = handler.on_post_auth( UserIdentifier('user', 'aa-bb'), 'session1234' ) self._assert_vlan(self.VLAN_PRIVATE, result)
def test_drop_on_deny(self, mock_fmt_mac, mock_utils): self._prepare_on_host_mocks(mock_fmt_mac, mock_utils) handler = FirewallAuthHandler() result = handler.on_host_deny(UserIdentifier('user', 'aabb')) self._assert_on_host_call(mock_fmt_mac, mock_utils, '/etc/radguestauth/fw_user_drop.sh', result)
def setUp(self): self.mock_um = Mock() self.mock_auth = Mock() self.cmd = ManageUserCommand(self.mock_um, self.mock_auth) self.testuser = UserIdentifier('user', 'device', 'pw') self.testdata = UserData() self.testuser.user_data = self.testdata self.mock_um.find.return_value = self.testuser
def test_remove_user(self): mgr, testuser = self._get_mgr_with_one_user() mgr.remove(testuser) self.assertListEqual(list(mgr.list_users()), []) # check again with a separate object mgr, testuser = self._get_mgr_with_one_user() mgr.remove(UserIdentifier('foo', 'bar')) self.assertListEqual(list(mgr.list_users()), [])
def test_reject_user_request(self): mgr = UserManager() # finish the request without update testuser = UserIdentifier('foo', 'bar') mgr.add_request(testuser) mgr.finish_request() self.assertListEqual(list(mgr.list_users()), []) self.assertFalse(mgr.is_request_pending())
def test_user_state_waiting_drop(self, mock_fmt_mac, mock_utils): self._prepare_on_host_mocks(mock_fmt_mac, mock_utils) handler = FirewallAuthHandler() handler.handle_user_state(UserIdentifier('user', 'aabb'), UserData.JOIN_STATE_WAITING, '') self._assert_on_host_call(mock_fmt_mac, mock_utils, '/etc/radguestauth/fw_user_drop.sh')
def test_add_request_user_has_password(self): mgr = UserManager() testuser = UserIdentifier('foo', 'bar') pw = mgr.generate_password() mgr.add_request(testuser) requested = mgr.get_request() self.assertEqual(pw, requested.password)
def _run_cmd(self, cmd, device_id=None): full_cmd = '/etc/radguestauth/fw_%s.sh' % cmd args = None if device_id: args = [UserIdentifier.format_mac(device_id)] return AuthUtils.sudo_cmd(full_cmd, additional_args=args, error_return='Command failed: %s' % cmd)
def test_may_join_other_name(self): # reject other users having the same device ID data = UserData() data.max_num_joins = 1 data.valid_until = time.time() + 10000 mgr, testuser = self._get_mgr_with_one_user_and_data(data) result = mgr.may_join(UserIdentifier('other', 'bar')) self.assert_state_blocked(result) # this should also apply if the stored user has no assigned data # (i.e. no state) mgr, testuser = self._get_mgr_with_one_user() result = mgr.may_join(UserIdentifier('other', 'bar')) self.assert_state_blocked(result)
def test_notify_join(self, mock_loader): chatc, mock_chat_obj = self._startup_controller(mock_loader) # reset calls to ignore potential startup messages mock_chat_obj.reset_mock() chatc.notify_join(UserIdentifier('fooName', 'barDevice')) # device and username should have been sent, possibly in multiple # messages. self._assert_in_chat_messages(mock_chat_obj, ['fooName', 'barDevice'])
def test_reject_blocked(self): user = UserIdentifier('user', '') handler = DefaultAuthHandler() expected_result = (auth.REJECT, None) result = handler.handle_user_state(user, UserData.JOIN_STATE_BLOCKED, '') self.assertEqual(expected_result, result)