diff --git a/src/middlewared/middlewared/plugins/pool_/dataset_quota_and_perms.py b/src/middlewared/middlewared/plugins/pool_/dataset_quota_and_perms.py index 486b5359208be..e62dd34d35831 100644 --- a/src/middlewared/middlewared/plugins/pool_/dataset_quota_and_perms.py +++ b/src/middlewared/middlewared/plugins/pool_/dataset_quota_and_perms.py @@ -10,181 +10,6 @@ class PoolDatasetService(Service): class Config: namespace = 'pool.dataset' - @accepts( - Str('id', required=True), - Dict( - 'pool_dataset_permission', - Str('user'), - Str('group'), - UnixPerm('mode', null=True), - OROperator( - Ref('nfs4_acl'), - Ref('posix1e_acl'), - name='acl' - ), - Dict( - 'options', - Bool('set_default_acl', default=False), - Bool('stripacl', default=False), - Bool('recursive', default=False), - Bool('traverse', default=False), - ), - register=True, - ), - roles=['DATASET_WRITE'] - ) - @returns(Ref('pool_dataset_permission')) - @item_method - @job(lock="dataset_permission_change") - async def permission(self, job, id_, data): - """ - Set permissions for a dataset `id`. Permissions may be specified as - either a posix `mode` or an `acl`. This method is a wrapper around - `filesystem.setperm`, `filesystem.setacl`, and `filesystem.chown` - - `filesystem.setperm` is called if `mode` is specified. - `filesystem.setacl` is called if `acl` is specified or if the - option `set_default_acl` is selected. - `filesystem.chown` is called if neither `mode` nor `acl` is - specified. - - The following `options` are supported: - - `set_default_acl` - apply a default ACL appropriate for specified - dataset. Default ACL is `NFS4_RESTRICTED` or `POSIX_RESTRICTED` - ACL template builtin with additional entries builtin_users group - and builtin_administrators group. See documentation for - `filesystem.acltemplate` for more details. - - `stripacl` - this option must be set in order to apply a POSIX - mode to a dataset that has a non-trivial ACL. The effect will - be to remove existing ACL and replace with specified mode. - - `recursive` - apply permissions recursively to dataset (all files - and directories will be impacted. - - `traverse` - permit recursive job to traverse filesystem boundaries - (child datasets). - - .. examples(websocket):: - - Change permissions of dataset "tank/myuser" to myuser:wheel and 755. - - :::javascript - { - "id": "6841f242-840a-11e6-a437-00e04d680384", - "msg": "method", - "method": "pool.dataset.permission", - "params": ["tank/myuser", { - "user": "myuser", - "acl": [], - "group": "builtin_users", - "mode": "755", - "options": {"recursive": true, "stripacl": true}, - }] - } - - """ - dataset_info = await self.middleware.call('pool.dataset.get_instance', id_) - path = dataset_info['mountpoint'] - acltype = dataset_info['acltype']['value'] - user = data.get('user', None) - group = data.get('group', None) - uid = gid = -1 - mode = data.get('mode', None) - options = data.get('options', {}) - set_default_acl = options.pop('set_default_acl') - acl = data.get('acl', []) - - if mode is None and set_default_acl: - acl_template = 'POSIX_RESTRICTED' if acltype == 'POSIX' else 'NFS4_RESTRICTED' - acl = (await self.middleware.call('filesystem.acltemplate.by_path', { - 'query-filters': [('name', '=', acl_template)], - 'format-options': {'canonicalize': True, 'ensure_builtins': True}, - }))[0]['acl'] - - pjob = None - - verrors = ValidationErrors() - if user is not None: - try: - uid = (await self.middleware.call('user.get_user_obj', {'username': user}))['pw_uid'] - except Exception as e: - verrors.add('pool_dataset_permission.user', str(e)) - - if group is not None: - try: - gid = (await self.middleware.call('group.get_group_obj', {'groupname': group}))['gr_gid'] - except Exception as e: - verrors.add('pool_dataset_permission.group', str(e)) - - if acl and mode: - verrors.add('pool_dataset_permission.mode', - 'setting mode and ACL simultaneously is not permitted.') - - if acl and options['stripacl']: - verrors.add('pool_dataset_permissions.acl', - 'Simultaneously setting and removing ACL is not permitted.') - - if mode and not options['stripacl']: - if not await self.middleware.call('filesystem.acl_is_trivial', path): - verrors.add('pool_dataset_permissions.options', - f'{path} has an extended ACL. The option "stripacl" must be selected.') - verrors.check() - - if not acl and mode is None and not options['stripacl']: - """ - Neither an ACL, mode, or removing the existing ACL are - specified in `data`. Perform a simple chown. - """ - options.pop('stripacl', None) - pjob = await self.middleware.call('filesystem.chown', { - 'path': path, - 'uid': uid, - 'gid': gid, - 'options': options - }) - - elif acl: - pjob = await self.middleware.call('filesystem.setacl', { - 'path': path, - 'dacl': acl, - 'uid': uid, - 'gid': gid, - 'options': options - }) - - elif mode or options['stripacl']: - """ - `setperm` performs one of two possible actions. If - `mode` is not set, but `stripacl` is specified, then - the existing ACL on the file is converted in place via - `acl_strip_np()`. This preserves the existing posix mode - while removing any extended ACL entries. - - If `mode` is set, then the ACL is removed from the file - and the new `mode` is applied. - """ - pjob = await self.middleware.call('filesystem.setperm', { - 'path': path, - 'mode': mode, - 'uid': uid, - 'gid': gid, - 'options': options - }) - else: - """ - This should never occur, but fail safely to avoid undefined - or unintended behavior. - """ - raise CallError(f"Unexpected parameter combination: {data}", - errno.EINVAL) - - await pjob.wait() - if pjob.error: - raise CallError(pjob.error) - return data - # TODO: Document this please @accepts( Str('ds', required=True), diff --git a/tests/api2/test_011_user.py b/tests/api2/test_011_user.py index 32c88c8d76e23..02c01ad67dbd8 100644 --- a/tests/api2/test_011_user.py +++ b/tests/api2/test_011_user.py @@ -381,12 +381,11 @@ def test_031_create_user_with_homedir(request): newly-created home directory.""" # create the dataset call('pool.dataset.create', HomeAssets.Dataset01['create_payload']) - call( - 'pool.dataset.permission', - HomeAssets.Dataset01['create_payload']['name'], - {'acl': HomeAssets.Dataset01['home_acl']}, - job=True - ) + call('filesystem.setacl', { + 'path': os.path.join('/mnt', HomeAssets.Dataset01['create_payload']['name']), + 'acl': HomeAssets.Dataset01['home_acl'] + }, job=True) + # now create the user UserAssets.TestUser02['create_payload']['uid'] = call('user.get_next_uid') call('user.create', UserAssets.TestUser02['create_payload']) diff --git a/tests/api2/test_345_acl_nfs4.py b/tests/api2/test_345_acl_nfs4.py index f38e3ab93c7f2..fc7d0a9820a69 100644 --- a/tests/api2/test_345_acl_nfs4.py +++ b/tests/api2/test_345_acl_nfs4.py @@ -197,10 +197,10 @@ def test_02_create_dataset(initialize_for_acl_tests): def test_04_basic_set_acl_for_dataset(request): depends(request, ["HAS_NFS4_ACLS"]) - call('pool.dataset.permission', TEST_INFO['dataset'], { - 'acl': default_acl, - 'group': group, - 'user': 'nobody' + call('filesystem.setacl', { + 'uid': 65534, + 'gid': 65534, + 'acl': default_acl }, job=True) acl_result = call('filesystem.getacl', TEST_INFO['dataset_path'], True) diff --git a/tests/api2/test_347_posix_mode.py b/tests/api2/test_347_posix_mode.py index c7fb7cce2b72e..d1af58cbfc0aa 100644 --- a/tests/api2/test_347_posix_mode.py +++ b/tests/api2/test_347_posix_mode.py @@ -2,20 +2,19 @@ # License: BSD -import sys -import os import pytest import stat -apifolder = os.getcwd() -sys.path.append(apifolder) -from functions import DELETE, GET, POST, SSH_TEST, wait_on_job -from auto_config import pool_name, user, password -from pytest_dependency import depends -MODE_DATASET = f'{pool_name}/modetest' +from functions import SSH_TEST +from middlewared.test.integration.assets.account import user, group +from middlewared.test.integration.assets.pool import dataset +from middlewared.test.integration.utils import call, ssh + + +MODE_DATASET_NAME = 'modetest' dataset_url = MODE_DATASET.replace('/', '%2F') -MODE_SUBDATASET = f'{pool_name}/modetest/sub1' +MODE_SUBDATASET_NAME = 'modetest/sub1' subdataset_url = MODE_SUBDATASET.replace('/', '%2F') OWNER_BITS = { @@ -43,202 +42,117 @@ MODE_PWD = "modetesting" -def test_01_check_dataset_endpoint(): - assert isinstance(GET('/pool/dataset/').json(), list) +@pytest.fixture(scope='module'): +def get_dataset(): + with dataset(MODE_DATASET_NAME) as ds: + path = os.path.join('/mnt', ds) + ssh(f'mkdir -p {path}/dir1/dir2') + ssh(f'touch {path}/dir1/dir2/testfile') + + with dataset(MODE_SUBDATASET_NAME): + yield ds -@pytest.mark.dependency(name="DATASET_CREATED") -def test_02_create_dataset(request): - result = POST( - '/pool/dataset/', { - 'name': MODE_DATASET - } - ) - assert result.status_code == 200, result.text +@pytest.fixture(scope='module'): +def get_user(): + with group(MODE_GROUP) as g: + with user({ + 'username': MODE_USER, + 'password': MODE_PWD, + 'group_create': True, + 'shell': '/usr/bin/bash', + 'ssh_password_enabled': True, + 'groups': [g['id']] + ) as u: + yield u | {'group_gid': g['gid']} + + +def get_mode_octal(path): + mode = call('filesystem.stat', path)['mode'] + server_mode = f"{stat.S_IMODE(mode):03o}" @pytest.mark.dependency(name="IS_TRIVIAL") -def test_03_verify_acl_is_trivial(request): +def test_verify_acl_is_trivial(get_dataset): depends(request, ["DATASET_CREATED"]) - results = POST('/filesystem/stat/', f'/mnt/{MODE_DATASET}') - assert results.status_code == 200, results.text - assert results.json()['acl'] is False, results.text + st = call('filesystem.stat', os.path.join('/mnt', get_dataset)) + assert st['acl'] is False + + +def get_mode_octal(path): + mode = call('filesystem.stat', path)['mode'] + return f"{stat.S_IMODE(mode):03o}" @pytest.mark.parametrize('mode_bit', MODE.keys()) -def test_04_verify_setting_mode_bits_nonrecursive(request, mode_bit): +def test_verify_setting_mode_bits_nonrecursive(get_dataset, mode_bit): """ This test iterates through possible POSIX permissions bits and verifies that they are properly set on the remote server. """ - depends(request, ["IS_TRIVIAL"]) new_mode = f"{MODE[mode_bit]:03o}" - result = POST( - f'/pool/dataset/id/{dataset_url}/permission/', { - 'acl': [], - 'mode': new_mode, - 'group': 'nogroup', - 'user': 'nobody' - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) - - results = POST('/filesystem/stat/', f'/mnt/{MODE_DATASET}') - assert results.status_code == 200, results.text - server_mode = f"{stat.S_IMODE(results.json()['mode']):03o}" - assert new_mode == server_mode, results.text - - -@pytest.mark.dependency(name="RECURSIVE_PREPARED") -def test_05_prepare_recursive_tests(request): - depends(request, ["IS_TRIVIAL"], scope="session") - result = POST( - '/pool/dataset/', { - 'name': MODE_SUBDATASET - } - ) - assert result.status_code == 200, result.text - - cmd = f'mkdir -p /mnt/{MODE_DATASET}/dir1/dir2' - results = SSH_TEST(cmd, user, password) - assert results['result'] is True, results['output'] + path = os.path.join('/mnt', get_dataset) - cmd = f'touch /mnt/{MODE_DATASET}/dir1/dir2/testfile' - results = SSH_TEST(cmd, user, password) - assert results['result'] is True, results['output'] + call('filesystem.setperm', { + 'path': path, + 'mode': new_mode, + 'uid': 65534, + 'gid': 65534 + }, job=True) - results = POST('/filesystem/stat/', f'/mnt/{MODE_SUBDATASET}') - assert results.status_code == 200, results.text - current_mode = results.json()['mode'] - # new datasets should be created with 755 permissions" - assert f"{stat.S_IMODE(current_mode):03o}" == "755", results.text + server_mode = get_mode_octal(path) + assert new_mode == server_mode @pytest.mark.parametrize('mode_bit', MODE.keys()) -def test_06_verify_setting_mode_bits_recursive_no_traverse(request, mode_bit): +def test_verify_setting_mode_bits_recursive_no_traverse(get_dataset, mode_bit): """ Perform recursive permissions change and verify new mode written to files and subdirectories. """ - depends(request, ["RECURSIVE_PREPARED"]) + ds_path = os.path.join('/mnt', get_dataset) + sub_ds_path = os.path.join(ds_path, 'sub1') + new_mode = f"{MODE[mode_bit]:03o}" - result = POST( - f'/pool/dataset/id/{dataset_url}/permission/', { - 'acl': [], - 'mode': new_mode, - 'group': 'nogroup', - 'user': 'nobody', - 'options': {'recursive': True} - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) - - results = POST('/filesystem/stat/', f'/mnt/{MODE_DATASET}') - assert results.status_code == 200, results.text - server_mode = f"{stat.S_IMODE(results.json()['mode']):03o}" - assert new_mode == server_mode, results.text - - results = POST('/filesystem/stat/', f'/mnt/{MODE_DATASET}/dir1/dir2') - assert results.status_code == 200, results.text - server_mode = f"{stat.S_IMODE(results.json()['mode']):03o}" - assert new_mode == server_mode, results.text - - results = POST('/filesystem/stat/', - f'/mnt/{MODE_DATASET}/dir1/dir2/testfile') - assert results.status_code == 200, results.text - server_mode = f"{stat.S_IMODE(results.json()['mode']):03o}" - assert new_mode == server_mode, results.text - - -def test_07_verify_mode_not_set_on_child_dataset(request): - depends(request, ["RECURSIVE_PREPARED"]) - results = POST('/filesystem/stat/', f'/mnt/{MODE_SUBDATASET}') - assert results.status_code == 200, results.text - current_mode = results.json()['mode'] - # new datasets should be created with 755 permissions" - assert f"{stat.S_IMODE(current_mode):03o}" == "755", results.text - - -def test_08_verify_traverse_to_child_dataset(request): - depends(request, ["RECURSIVE_PREPARED"]) - result = POST( - f'/pool/dataset/id/{dataset_url}/permission/', { - 'acl': [], - 'mode': 777, - 'group': 'nogroup', - 'user': 'nobody', - 'options': {'recursive': True, 'traverse': True} - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) - - results = POST('/filesystem/stat/', f'/mnt/{MODE_SUBDATASET}') - assert results.status_code == 200, results.text - current_mode = results.json()['mode'] - assert f"{stat.S_IMODE(current_mode):03o}" == "777", results.text - - -""" -Create user and group for testing function of POSIX permission bits. -""" - - -@pytest.mark.dependency(name="GROUP_CREATED") -def test_09_create_test_group(request): - depends(request, ["IS_TRIVIAL"]) - global next_gid - global groupid - results = GET('/group/get_next_gid/') - assert results.status_code == 200, results.text - next_gid = results.json() - global groupid - payload = { - "gid": next_gid, - "name": MODE_GROUP, - } - results = POST("/group/", payload) - assert results.status_code == 200, results.text - groupid = results.json() - - -@pytest.mark.dependency(name="USER_CREATED") -def test_10_creating_shareuser_to_test_acls(request): - depends(request, ["GROUP_CREATED"]) - global modeuser_id - global next_uid - results = GET('/user/get_next_uid/') - assert results.status_code == 200, results.text - next_uid = results.json() - payload = { - "username": MODE_USER, - "full_name": "Mode User", - "group_create": True, - "password": MODE_PWD, - "uid": next_uid, - "groups": [groupid], - "shell": '/usr/bin/bash', - "ssh_password_enabled": True, - } - results = POST("/user/", payload) - assert results.status_code == 200, results.text - modeuser_id = results.json() - - -""" -Next series of tests are for correct behavior of POSIX permissions -""" - - -def dir_mode_check(mode_bit): + call('filesystem.setperm', { + 'path': ds_path, + 'mode': new_mode, + 'uid': 65534, + 'gid': 65534 + 'options': {'recursive': True} + }, job=True) + + server_mode = get_mode_octal(ds_path) + assert new_mode == server_mode + + server_mode = get_mode_octal(os.path.join(ds_path, 'dir1', 'dir2')) + assert new_mode == server_mode + + server_mode = get_mode_octal(os.path.join(ds_path, 'dir1', 'dir2', 'testfile')) + assert new_mode == server_mode + + # child dataset shouldn't be touched + server_mode = get_mode_octal(sub_ds_path) + assert server_mode == "755" + + +def test_verify_traverse_to_child_dataset(get_dataset): + ds_path = os.path.join('/mnt', get_dataset) + sub_ds_path = os.path.join(ds_path, 'sub1') + + call('filesystem.setperm', { + 'path': ds_path, + 'mode': '777', + 'uid': 65534, + 'gid': 65534 + 'options': {'recursive': True, 'traverse': True} + }, job=True) + + server_mode = get_mode_octal(sub_ds_path) + assert server_mode == "777" + + +def dir_mode_check(mode_bit, MODE_DATASET): if mode_bit.endswith("READ"): cmd = f'ls /mnt/{MODE_DATASET}' results = SSH_TEST(cmd, MODE_USER, MODE_PWD) @@ -279,7 +193,7 @@ def dir_mode_check(mode_bit): assert results['result'] is False, results['output'] -def file_mode_check(mode_bit): +def file_mode_check(mode_bit, MODE_DATASET): if mode_bit.endswith("READ"): cmd = f'cat /mnt/{MODE_DATASET}/canary' results = SSH_TEST(cmd, MODE_USER, MODE_PWD) @@ -329,7 +243,7 @@ def file_mode_check(mode_bit): assert results['result'] is False, results['output'] -def file_mode_check_xor(mode_bit): +def file_mode_check_xor(mode_bit, MODE_DATASET): """ when this method is called, all permissions bits are set except for the one being tested. @@ -351,306 +265,196 @@ def file_mode_check_xor(mode_bit): @pytest.mark.parametrize('mode_bit', OWNER_BITS.keys()) -def test_11_test_directory_owner_bits_function_allow(mode_bit, request): +def test_directory_owner_bits_function_allow(mode_bit, get_dataset, get_user): """ Verify mode behavior correct when it's the only bit set. In case of directory, Execute must be set concurrently with write in order to verify correct write behavior. """ - depends(request, ["USER_CREATED"], scope="session") + ds_path = os.path.join('/mnt', get_dataset) new_mode = MODE[mode_bit] if new_mode == stat.S_IWUSR: new_mode |= stat.S_IXUSR - result = POST( - f'/pool/dataset/id/{dataset_url}/permission/', { - 'acl': [], - 'mode': f'{new_mode:03o}', - 'group': 'nogroup', - 'user': MODE_USER - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) - - if job_status['state'] != 'SUCCESS': - return + call('filesystem.setperm', { + 'path': ds_path, + 'mode': f'{new_mode:03o}', + 'uid': get_user['uid'], + 'gid': 65534, + }, job=True) - dir_mode_check(mode_bit) + dir_mode_check(mode_bit, get_dataset) @pytest.mark.parametrize('mode_bit', GROUP_BITS.keys()) -def test_12_test_directory_group_bits_function_allow(mode_bit, request): +def test_directory_group_bits_function_allow(mode_bit, get_dataset, get_user): """ Verify mode behavior correct when it's the only bit set. In case of directory, Execute must be set concurrently with write in order to verify correct write behavior. """ - depends(request, ["USER_CREATED"], scope="session") + ds_path = os.path.join('/mnt', get_dataset) + new_mode = MODE[mode_bit] if new_mode == stat.S_IWGRP: new_mode |= stat.S_IXGRP - result = POST( - f'/pool/dataset/id/{dataset_url}/permission/', { - 'acl': [], - 'mode': f'{new_mode:03o}', - 'group': MODE_GROUP, - 'user': 'root' - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) - - if job_status['state'] != 'SUCCESS': - return + call('filesystem.setperm', { + 'path': ds_path, + 'mode': f'{new_mode:03o}', + 'uid': 0, + 'gid': get_user['group_gid'], + }, job=True) - dir_mode_check(mode_bit) + dir_mode_check(mode_bit, get_dataset) @pytest.mark.parametrize('mode_bit', OTHER_BITS.keys()) -def test_13_test_directory_other_bits_function_allow(mode_bit, request): +def test_directory_other_bits_function_allow(mode_bit, get_dataset): """ Verify mode behavior correct when it's the only bit set. In case of directory, Execute must be set concurrently with write in order to verify correct write behavior. """ - depends(request, ["USER_CREATED"], scope="session") + ds_path = os.path.join('/mnt', get_dataset) + new_mode = MODE[mode_bit] if new_mode == stat.S_IWOTH: new_mode |= stat.S_IXOTH - result = POST( - f'/pool/dataset/id/{dataset_url}/permission/', { - 'acl': [], - 'mode': f'{new_mode:03o}', - 'group': 'root', - 'user': 'root' - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) - - if job_status['state'] != 'SUCCESS': - return - - dir_mode_check(mode_bit) - - -def test_14_setup_file_test(request): - depends(request, ["USER_CREATED"], scope="session") - result = POST( - '/filesystem/setperm/', { - 'path': f'/mnt/{MODE_DATASET}', - 'mode': "001", - 'gid': 0, - 'uid': 0, - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) - - cmd = f'echo "echo CANARY" > /mnt/{MODE_DATASET}/canary' + call('filesystem.setperm', { + 'path': ds_path, + 'mode': f'{new_mode:03o}', + 'uid': 0, + 'gid': 0, + }, job=True) + + dir_mode_check(mode_bit, get_dataset) + + +def test_setup_file_test(get_dataset): + ds_path = os.path.join('/mnt', get_dataset) + + call('filesystem.setperm', { + 'path': ds_path, + 'mode': '001', + 'uid': 0, + 'gid': 0, + }, job=True) + + cmd = f'echo "echo CANARY" > {ds_path}/canary' results = SSH_TEST(cmd, user, password) assert results['result'] is True, results['output'] @pytest.mark.parametrize('mode_bit', OWNER_BITS.keys()) -def test_15_test_file_owner_bits_function_allow(mode_bit, request): +def test_file_owner_bits_function_allow(mode_bit, get_dataset, get_user): """ Verify mode behavior correct when it's the only bit set. """ - depends(request, ["USER_CREATED"], scope="session") + ds_path = os.path.join('/mnt', get_dataset) new_mode = MODE[mode_bit] - result = POST( - '/filesystem/setperm/', { - 'path': f'/mnt/{MODE_DATASET}/canary', - 'mode': f'{new_mode:03o}', - 'gid': 0, - 'uid': next_uid - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) - - if job_status['state'] != 'SUCCESS': - return + call('filesystem.setperm', { + 'path': ds_path, + 'mode': f'{new_mode:03o}', + 'uid': get_user['uid'], + 'gid': 0, + }, job=True) - file_mode_check(mode_bit) + file_mode_check(mode_bit, get_dataset) @pytest.mark.parametrize('mode_bit', GROUP_BITS.keys()) -def test_16_test_file_group_bits_function_allow(mode_bit, request): +def test_file_group_bits_function_allow(mode_bit, get_dataset, get_user): """ Verify mode behavior correct when it's the only bit set. """ - depends(request, ["USER_CREATED"], scope="session") + ds_path = os.path.join('/mnt', get_dataset) new_mode = MODE[mode_bit] - result = POST( - '/filesystem/setperm/', { - 'path': f'/mnt/{MODE_DATASET}/canary', - 'mode': f'{new_mode:03o}', - 'gid': next_gid, - 'uid': 0, - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) + call('filesystem.setperm', { + 'path': ds_path, + 'mode': f'{new_mode:03o}', + 'gid': get_user['group_gid'], + 'uid': 0, + }, job=True) - if job_status['state'] != 'SUCCESS': - return - - file_mode_check(mode_bit) + file_mode_check(mode_bit, get_dataset) @pytest.mark.parametrize('mode_bit', OTHER_BITS.keys()) -def test_17_test_file_other_bits_function_allow(mode_bit, request): +def test_file_other_bits_function_allow(mode_bit, get_dataset, get_user): """ Verify mode behavior correct when it's the only bit set. """ - depends(request, ["USER_CREATED"], scope="session") + ds_path = os.path.join('/mnt', get_dataset) new_mode = MODE[mode_bit] - result = POST( - '/filesystem/setperm/', { - 'path': f'/mnt/{MODE_DATASET}/canary', - 'mode': f'{new_mode:03o}', - 'gid': 0, - 'uid': 0, - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) - - if job_status['state'] != 'SUCCESS': - return + call('filesystem.setperm', { + 'path': ds_path, + 'mode': f'{new_mode:03o}', + 'gid': 0, + 'uid': 0, + }, job=True) - file_mode_check(mode_bit) + file_mode_check(mode_bit, get_dataset) @pytest.mark.parametrize('mode_bit', OWNER_BITS.keys()) -def test_18_test_file_owner_bits_xor(mode_bit, request): +def test_file_owner_bits_xor(mode_bit, get_dataset, get_user): """ Verify mode behavior correct when it's the only bit set. """ - depends(request, ["USER_CREATED"], scope="session") + ds_path = os.path.join('/mnt', get_dataset) new_mode = stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO new_mode = new_mode ^ MODE[mode_bit] - result = POST( - '/filesystem/setperm/', { - 'path': f'/mnt/{MODE_DATASET}/canary', - 'mode': f'{new_mode:03o}', - 'gid': 0, - 'uid': next_uid - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) + new_mode = MODE[mode_bit] - if job_status['state'] != 'SUCCESS': - return + call('filesystem.setperm', { + 'path': ds_path, + 'mode': f'{new_mode:03o}', + 'gid': 0, + 'uid': get_user['uid'], + }, job=True) - file_mode_check_xor(mode_bit) + file_mode_check_xor(mode_bit, get_dataset) @pytest.mark.parametrize('mode_bit', GROUP_BITS.keys()) -def test_19_test_file_group_bits_xor(mode_bit, request): +def test_file_group_bits_xor(mode_bit, get_dataset, get_user): """ Verify mode behavior correct when it's the only bit set. """ - depends(request, ["USER_CREATED"], scope="session") + ds_path = os.path.join('/mnt', get_dataset) new_mode = stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO new_mode = new_mode ^ MODE[mode_bit] - result = POST( - '/filesystem/setperm/', { - 'path': f'/mnt/{MODE_DATASET}/canary', - 'mode': f'{new_mode:03o}', - 'gid': next_gid, - 'uid': 0 - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) + call('filesystem.setperm', { + 'path': ds_path, + 'mode': f'{new_mode:03o}', + 'gid': get_user['group_gid'], + 'uid': 0, + }, job=True) - if job_status['state'] != 'SUCCESS': - return - - file_mode_check_xor(mode_bit) + file_mode_check_xor(mode_bit, get_dataset) @pytest.mark.parametrize('mode_bit', OTHER_BITS.keys()) -def test_20_test_file_other_bits_xor(mode_bit, request): +def test_20_test_file_other_bits_xor(mode_bit, get_dataset, get_user): """ Verify mode behavior correct when it's the only bit set. """ - depends(request, ["USER_CREATED"], scope="session") + ds_path = os.path.join('/mnt', get_dataset) new_mode = stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO new_mode = new_mode ^ MODE[mode_bit] - result = POST( - '/filesystem/setperm/', { - 'path': f'/mnt/{MODE_DATASET}/canary', - 'mode': f'{new_mode:03o}', - 'gid': 0, - 'uid': 0 - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) - - if job_status['state'] != 'SUCCESS': - return - - file_mode_check_xor(mode_bit) - - -def test_21_delete_child_dataset(request): - depends(request, ["RECURSIVE_PREPARED"]) - result = DELETE( - f'/pool/dataset/id/{subdataset_url}/' - ) - assert result.status_code == 200, result.text - + call('filesystem.setperm', { + 'path': ds_path, + 'mode': f'{new_mode:03o}', + 'gid': 0, + 'uid': 0, + }, job=True) -def test_22_delete_group(request): - depends(request, ["GROUP_CREATED"]) - results = DELETE(f"/group/id/{groupid}/", {"delete_users": True}) - assert results.status_code == 200, results.text - - -def test_23_delete_user(request): - depends(request, ["USER_CREATED"]) - results = DELETE(f"/user/id/{modeuser_id}/", {"delete_group": True}) - assert results.status_code == 200, results.text - - -def test_24_delete_dataset(request): - depends(request, ["DATASET_CREATED"]) - result = DELETE( - f'/pool/dataset/id/{dataset_url}/' - ) - assert result.status_code == 200, result.text + file_mode_check_xor(mode_bit, get_dataset) diff --git a/tests/api2/test_348_posix_acl.py b/tests/api2/test_348_posix_acl.py index bb6825e522d8c..7ad7afd830a03 100644 --- a/tests/api2/test_348_posix_acl.py +++ b/tests/api2/test_348_posix_acl.py @@ -11,6 +11,7 @@ from functions import DELETE, GET, POST, SSH_TEST, wait_on_job from auto_config import pool_name, user, password from pytest_dependency import depends +from middlewared.test.integration.utils import call ACLTEST_DATASET = f'{pool_name}/posixacltest' @@ -463,22 +464,16 @@ def test_14_recursive_with_traverse(request): def test_15_strip_acl_from_dataset(request): """ - Strip ACL via pool.dataset.permission endpoint. + Strip ACL via filesystem.setperm endpoint. This should work even for POSIX1E ACLs. """ depends(request, ["HAS_POSIX_ACLS"]) - result = POST( - f'/pool/dataset/id/{DATASET_URL}/permission/', { - 'acl': [], - 'mode': '777', - 'options': {'stripacl': True, 'recursive': True} - } - ) - assert result.status_code == 200, result.text - JOB_ID = result.json() - job_status = wait_on_job(JOB_ID, 180) - assert job_status['state'] == 'SUCCESS', str(job_status['results']) + call('filesystem.setperm', { + 'path': os.path.join('/mnt', DATASET), + 'mode': '777', + 'options': {'stripacl': True, 'recursive': True} + }, job=True) """