diff --git a/selftest/test-info1.yml b/selftest/test-info1.yml index f1a689c..2ad9435 100644 --- a/selftest/test-info1.yml +++ b/selftest/test-info1.yml @@ -9,6 +9,13 @@ users: # Backend filesystem of the exported shares backend: glusterfs +# Additional information which may be used by tests here +# some tests may be skipped if the required config option is not set +extra: + # The supplementary group to be used by test_permissions + supplementary_group: test_sg + + # shares: List of dict of exported shares shares: # share export1 @@ -19,6 +26,8 @@ shares: backend: # If present override default backend filesystem name: cephfs.vfs + # if present, provide access to backend filesystem + path: /mnt/backend/export1 # If present, use these credentials to perform the # tests for this share users: diff --git a/selftest/test_testhelper.py b/selftest/test_testhelper.py index b157f95..6dd45e6 100644 --- a/selftest/test_testhelper.py +++ b/selftest/test_testhelper.py @@ -9,6 +9,7 @@ def test_read_yaml1(): assert export1["server"] == "hostname1" assert export1["path"] == "/mnt/share/export1-cephfs-vfs" assert export1["backend"]["name"] == "cephfs.vfs" + assert export1["backend"]["path"] == "/mnt/backend/export1" assert "user1" not in export1["users"] assert "test2" in export1["users"] assert export1["users"]["test2"] == "x" @@ -17,6 +18,7 @@ def test_read_yaml1(): assert export2["server"] == "server_name" assert "path" not in export2 assert export2["backend"]["name"] == "glusterfs" + assert export2["backend"]["path"] is None assert "test2" not in export2["users"] assert "user2" in export2["users"] assert export2["users"]["user2"] == "user2password" @@ -60,3 +62,16 @@ def test_generate_exported_shares(): arr.append((share["server"], share["name"])) assert len(arr) == 1 assert ("server_name", "export2") in arr + + +def test_get_extra_configuration(): + testinfo = testhelper.read_yaml("test-info1.yml") + sg = testhelper.get_extra_configuration(testinfo, "supplementary_group") + assert sg == "test_sg", "Could not read extra configuration" + other = testhelper.get_extra_configuration(testinfo, "other") + assert other is None, "Incorrectly read other from extra configuration" + + # test-info2.yml does not have extra section + testinfo = testhelper.read_yaml("test-info2.yml") + sg = testhelper.get_extra_configuration(testinfo, "supplementary_group") + assert sg is None, "Could not read extra configuration" diff --git a/test-info.yml.example b/test-info.yml.example index f1a689c..371b0a0 100644 --- a/test-info.yml.example +++ b/test-info.yml.example @@ -9,6 +9,12 @@ users: # Backend filesystem of the exported shares backend: glusterfs +# Additional information which may be used by tests here +# some tests may be skipped if the required config option is not set +extra: + # The supplementary group to be used by test_supplementary_group + supplementary_group: sg + # shares: List of dict of exported shares shares: # share export1 @@ -19,6 +25,8 @@ shares: backend: # If present override default backend filesystem name: cephfs.vfs + # If present, provide access to backend filesystem + path: /mnt/backend/export1 # If present, use these credentials to perform the # tests for this share users: diff --git a/testcases/misc/test_permissions.py b/testcases/misc/test_permissions.py new file mode 100644 index 0000000..241d1a8 --- /dev/null +++ b/testcases/misc/test_permissions.py @@ -0,0 +1,93 @@ +import testhelper +import os +import pytest +import pwd +import grp +import shutil +from pathlib import Path + +test_info_file = os.getenv("TEST_INFO_FILE") +test_info = testhelper.read_yaml(test_info_file) +test_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + +# test_supplementary_group: +# This test is to check writes to a folder owned by a supplementary group. +# +# Requirements: +# 1. username in share["user"] exists +# 2. username is part of supplementary group provided in +# user["extra"]["supplementary_group"] +# 3. share["backend"]["path"] should be set +# +# Steps: +# 1. Create folder test_subdir/ on direct path with +# group set to sgroup and mode 0770 +# 2. Upload file to test_subdir/test-cp +# +# Expected: +# Copy passes + + +# function to check if requirements are met +def check_reqs_supplementary_group(share: dict, sgroup: str) -> bool: + if share["backend"]["path"] is None: + return False + + username = list(share["users"].keys())[0] + try: + pwd.getpwnam(username) + if username not in grp.getgrnam(sgroup).gr_mem: + return False + except KeyError: + return False + return True + + +def gen_supplementary_group_param(test_info: dict) -> list: + if not test_info: + return [] + sgroup = testhelper.get_extra_configuration( + test_info, "supplementary_group" + ) + if sgroup is None: + return [] + + arr = [] + for s in testhelper.get_shares(test_info).values(): + if check_reqs_supplementary_group(s, sgroup): + arr.append((s["server"], s["name"])) + return arr + + +@pytest.mark.privileged +@pytest.mark.parametrize( + "ipaddr,share_name", gen_supplementary_group_param(test_info) +) +def test_supplementary_group(ipaddr: str, share_name: str) -> None: + share = testhelper.get_share(test_info, share_name) + fs_path = Path(share["backend"]["path"]) + sgroup = testhelper.get_extra_configuration( + test_info, "supplementary_group" + ) + test_subdir = Path("supplementary_group") + mount_params = testhelper.get_mount_parameters(test_info, share_name) + + # setup local testdir + testdir = fs_path / test_subdir + testdir.mkdir(exist_ok=True) + shutil.chown(testdir, group=sgroup) + testdir.chmod(0o770) + + smbclient = testhelper.SMBClient( + ipaddr, + mount_params["share"], + mount_params["username"], + mount_params["password"], + ) + + try: + remote_test_file = str(Path("/") / test_subdir / Path("test-cp")) + smbclient.write_text(remote_test_file, test_string) + finally: + smbclient.disconnect() + shutil.rmtree(testdir) diff --git a/testhelper/testhelper.py b/testhelper/testhelper.py index 457ca9f..06daecc 100644 --- a/testhelper/testhelper.py +++ b/testhelper/testhelper.py @@ -54,10 +54,11 @@ def read_yaml(test_info_file): shares[sharename] = {"name": sharename} share = shares[sharename] share.setdefault("name", sharename) - share.setdefault("backend", {}) share.setdefault("server", default_server) share.setdefault("users", default_users) + share.setdefault("backend", {}) share["backend"].setdefault("name", default_backend) + share["backend"].setdefault("path", None) test_info["shares"] = shares @@ -191,3 +192,18 @@ def get_exported_shares(test_info: dict) -> typing.List[str]: if not is_premounted_share(share): arr.append(share["name"]) return arr + + +def get_extra_configuration(test_info: dict, extra_var: str) -> typing.Any: + """Return value of config set in extra section + + Parameters: + test_info: Dict containing the parsed yaml file + extra_var: string containing extra config value required + Returns: + value set for the extra_var in the extra section + """ + extra = test_info.get("extra") + if extra is None: + return None + return extra.get(extra_var) diff --git a/tox.ini b/tox.ini index 4f91051..9cac483 100644 --- a/tox.ini +++ b/tox.ini @@ -39,6 +39,7 @@ commands = pytest -vrfEsxXpP testcases/consistency deps = pytest pyyaml + pysmb changedir = {toxinidir}/selftest commands = pytest -vrfEsxXpP .