from unittest.mock import patch import ldap3 import pytest import ceod.utils as utils def test_api_user_not_found(client): status, data = client.get('/api/members/no_such_user') assert status == 404 @pytest.fixture(scope='module') def create_user_resp(client, mocks_for_create_user, mock_mail_server): mock_mail_server.messages.clear() status, data = client.post('/api/members', json={ 'uid': 'test_1', 'cn': 'Test One', 'given_name': 'Test', 'sn': 'One', 'program': 'Math', 'terms': ['s2021'], 'forwarding_addresses': ['test_1@uwaterloo.internal'], }) assert status == 200 assert data[-1]['status'] == 'completed' yield status, data status, data = client.delete('/api/members/test_1') assert status == 200 assert data[-1]['status'] == 'completed' @pytest.fixture(scope='module') def create_user_result(create_user_resp): # convenience method _, data = create_user_resp return data[-1]['result'] def test_api_create_user(cfg, create_user_resp, mock_mail_server): _, data = create_user_resp min_uid = cfg.get('members_min_id') expected = [ {"status": "in progress", "operation": "add_user_to_ldap"}, {"status": "in progress", "operation": "add_group_to_ldap"}, {"status": "in progress", "operation": "add_user_to_kerberos"}, {"status": "in progress", "operation": "create_home_dir"}, {"status": "in progress", "operation": "set_forwarding_addresses"}, {"status": "in progress", "operation": "send_welcome_message"}, {"status": "in progress", "operation": "subscribe_to_mailing_list"}, {"status": "in progress", "operation": "announce_new_user"}, {"status": "completed", "result": { "cn": "Test One", "given_name": "Test", "sn": "One", "uid": "test_1", "uid_number": min_uid, "gid_number": min_uid, "login_shell": "/bin/bash", "home_directory": "/tmp/test_users/test_1", "is_club": False, "is_club_rep": False, "program": "Math", "terms": ["s2021"], "mail_local_addresses": ["test_1@csclub.internal"], "forwarding_addresses": ['test_1@uwaterloo.internal'], "password": "krb5" }}, ] assert data == expected # Two messages should have been sent: a welcome message to the new member, # and an announcement to the ceo mailing list assert len(mock_mail_server.messages) == 2 assert mock_mail_server.messages[0]['to'] == 'test_1@csclub.internal' assert mock_mail_server.messages[1]['to'] == 'ceo@csclub.internal,ctdalek@csclub.internal' mock_mail_server.messages.clear() def test_api_next_uid(cfg, client, create_user_result): min_uid = cfg.get('members_min_id') _, data = client.post('/api/members', json={ 'uid': 'test_2', 'cn': 'Test Two', 'given_name': 'Test', 'sn': 'Two', 'program': 'Math', 'terms': ['s2021'], }) assert data[-1]['status'] == 'completed' result = data[-1]['result'] try: assert result['uid_number'] == min_uid + 1 assert result['gid_number'] == min_uid + 1 finally: client.delete('/api/members/test_2') def test_api_get_user(cfg, client, create_user_result): old_data = create_user_result.copy() uid = old_data['uid'] del old_data['password'] status, data = client.get(f'/api/members/{uid}') assert status == 200 assert data == old_data def test_api_patch_user(client, create_user_result): data = create_user_result uid = data['uid'] prev_login_shell = data['login_shell'] prev_forwarding_addresses = data['forwarding_addresses'] # replace login shell new_shell = '/bin/sh' status, data = client.patch( f'/api/members/{uid}', json={'login_shell': new_shell}) assert status == 200 expected = [ {"status": "in progress", "operation": "replace_login_shell"}, {"status": "completed", "result": "OK"}, ] assert data == expected # replace forwarding addresses new_forwarding_addresses = [ 'test@example1.internal', 'test@example2.internal', ] status, data = client.patch( f'/api/members/{uid}', json={ 'forwarding_addresses': new_forwarding_addresses } ) assert status == 200 expected = [ {"status": "in progress", "operation": "replace_forwarding_addresses"}, {"status": "completed", "result": "OK"}, ] assert data == expected # retrieve the user and make sure that both fields were changed status, data = client.get(f'/api/members/{uid}') assert status == 200 assert data['login_shell'] == new_shell assert data['forwarding_addresses'] == new_forwarding_addresses # replace (restore) both status, data = client.patch( f'/api/members/{uid}', json={ 'login_shell': prev_login_shell, 'forwarding_addresses': prev_forwarding_addresses } ) assert status == 200 expected = [ {"status": "in progress", "operation": "replace_login_shell"}, {"status": "in progress", "operation": "replace_forwarding_addresses"}, {"status": "completed", "result": "OK"}, ] assert data == expected def test_api_renew_user(cfg, client, create_user_result, ldap_conn): data = create_user_result.copy() uid = data['uid'] old_terms = data['terms'] old_non_member_terms = data.get('non_member_terms', []) new_terms = ['f2021'] status, data = client.post( f'/api/members/{uid}/renew', json={'terms': new_terms}) assert status == 200 assert data == {'terms_added': new_terms} new_non_member_terms = ['w2022', 's2022'] status, data = client.post( f'/api/members/{uid}/renew', json={'non_member_terms': new_non_member_terms}) assert status == 200 assert data == {'non_member_terms_added': new_non_member_terms} # check that the changes were applied _, data = client.get(f'/api/members/{uid}') assert data['terms'] == old_terms + new_terms assert data['non_member_terms'] == old_non_member_terms + new_non_member_terms assert data['is_club_rep'] # cleanup base_dn = cfg.get('ldap_users_base') dn = f'uid={uid},{base_dn}' changes = { 'term': [(ldap3.MODIFY_REPLACE, old_terms)], 'nonMemberTerm': [(ldap3.MODIFY_REPLACE, old_non_member_terms)], 'isClubRep': [(ldap3.MODIFY_REPLACE, [])], } ldap_conn.modify(dn, changes) def test_api_reset_password(client, create_user_result): uid = create_user_result['uid'] with patch.object(utils, 'gen_password') as gen_password_mock: gen_password_mock.return_value = 'new_password' status, data = client.post(f'/api/members/{uid}/pwreset') assert status == 200 assert data['password'] == 'new_password' # cleanup status, data = client.post(f'/api/members/{uid}/pwreset') assert status == 200 assert data['password'] == 'krb5' def test_authz_check(client, create_user_result): # non-staff members may not create users status, data = client.post('/api/members', json={ 'uid': 'test_1', 'cn': 'Test One', 'given_name': 'Test', 'sn': 'One', 'terms': ['s2021'], }, principal='regular1') assert status == 403 # non-syscom members may not see forwarding addresses old_data = create_user_result.copy() uid = old_data['uid'] del old_data['password'] del old_data['forwarding_addresses'] _, data = client.get(f'/api/members/{uid}', principal='regular1') assert data == old_data # If we're syscom but we don't pass credentials, the request should fail _, data = client.post('/api/members', json={ 'uid': 'test_1', 'cn': 'Test One', 'given_name': 'Test', 'sn': 'One', 'terms': ['s2021'], }, principal='ctdalek', delegate=False) assert data[-1]['status'] == 'aborted'