From 83aa3ae11ecd891c4174db80eee95b06c9e15d7f Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 8 Apr 2022 22:47:48 +0200 Subject: [PATCH 01/57] Feat: Add some proof-of-concept unit tests --- .gitignore | 1 + README.md | 20 ++++++++++++++---- .../linuxmusterLinuxclient7/requirements.txt | 11 ++++++++++ .../linuxmusterLinuxclient7/tests/__init__.py | 0 .../tests/test_localUserHelper.py | 21 +++++++++++++++++++ 5 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/requirements.txt create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/__init__.py create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_localUserHelper.py diff --git a/.gitignore b/.gitignore index d8c2f40..2e91c4e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ debian/linuxmuster-linuxclient7.substvars debian/linuxmuster-linuxclient7/ __pycache__ /public +venv \ No newline at end of file diff --git a/README.md b/README.md index 6767e69..b7401b9 100644 --- a/README.md +++ b/README.md @@ -24,19 +24,19 @@ Package for Ubuntu clients to connect to the linuxmuster.net 7 active directory server. This is the new version of the linuxmuster-client-adsso package. - For user documentation, take a look at the [wiki](https://github.com/linuxmuster/linuxmuster-linuxclient7/wiki). -- For developmer documentation, take a look at the [documentation](https://linuxmuster.github.io/linuxmuster-linuxclient7) +- For developer documentation, take a look at the [documentation](https://linuxmuster.github.io/linuxmuster-linuxclient7) -## Maintainance Details +## Maintenance Details Linuxmuster.net official | ![#f03c15](https://via.placeholder.com/15/f03c15/000000?text=+) NO* :---: | :---: [Community support](https://ask.linuxmuster.net) | ![#c5f015](https://via.placeholder.com/15/c5f015/000000?text=+) YES** Actively developed | ![#c5f015](https://via.placeholder.com/15/c5f015/000000?text=+) YES -Maintainer organisation | Netzint GmbH +Maintainer organization | Netzint GmbH Primary maintainer | dorian@itsblue.de / andreas.till@netzint.de \* Even though this is not an official package, pull requests and issues are being looked at. -** The linuxmuster community consits of people who are nice and happy to help. They are not directly involved in the development though, and might not be able to help in any case. +** The linuxmuster community consists of people who are nice and happy to help. They are not directly involved in the development though, and might not be able to help in any case. ## Version schema: - General: `major.minor.patch` @@ -45,3 +45,15 @@ Primary maintainer | dorian@itsblue.de / andreas.till@netzint.de - Releases are always prefixed with `release`. - So, once version `7.1.1` is ready, it is published as `7.1.1-release` - This concept ensures that stable releases are always evaluated as a higher version number than pre-releases. + +## Setup development environment + +Tested with Python 3.10: + +1. `python3 -m venv ./venv` +2. `. ./venv/bin/activate` +3. `cd ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7` +4. `pip install -r requirements.txt` + +To run tests: +1. `pytest` \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/requirements.txt b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/requirements.txt new file mode 100644 index 0000000..b51e859 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/requirements.txt @@ -0,0 +1,11 @@ +attrs==21.4.0 +iniconfig==1.1.1 +packaging==21.3 +pluggy==1.0.0 +py==1.11.0 +pyasn1==0.4.8 +pyasn1-modules==0.2.8 +pyparsing==3.0.7 +pytest==7.1.1 +python-ldap==3.4.0 +tomli==2.0.1 diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/__init__.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_localUserHelper.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_localUserHelper.py new file mode 100644 index 0000000..6a63a29 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_localUserHelper.py @@ -0,0 +1,21 @@ +from unittest import mock +from .. import localUserHelper + +@mock.patch("linuxmusterLinuxclient7.localUserHelper.subprocess.check_output") +def test_getGroupsOfLocalUserOk(mockOutput): + mockOutput.return_value = b"group1\x00group2\x00" + + rc, groups = localUserHelper.getGroupsOfLocalUser("user1") + + assert rc + assert "user1" in mockOutput.call_args.args[0] + assert "group1" in groups and "group2" in groups + + +@mock.patch("linuxmusterLinuxclient7.localUserHelper.subprocess.check_output") +def test_getGroupsOfLocalUserError(mockOutput): + mockOutput.return_value = None + + rc, groups = localUserHelper.getGroupsOfLocalUser("user1") + + assert not rc \ No newline at end of file From 2ab4b963511fffbaa994fa9f396a0c5e22c1b030 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 8 Apr 2022 22:56:09 +0200 Subject: [PATCH 02/57] Feat: Add workflow for codecov.io --- .github/workflows/test.yml | 38 +++++++++++++++++++ .gitignore | 4 +- README.md | 3 +- .../linuxmusterLinuxclient7/requirements.txt | 2 + 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..46758e9 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,38 @@ +name: Example workflow for Codecov +on: [push] +jobs: + run: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + env: + OS: ${{ matrix.os }} + PYTHON: '3.7' + steps: + - uses: actions/checkout@master + - name: Setup Python + uses: actions/setup-python@master + with: + python-version: 3.7 + - name: Generate coverage report + run: | + pip install pytest + pip install pytest-cov + cd ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7 + pip install -r requirements.txt + + pytest --cov=./ --cov-report=xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v2 + with: + token: ${{ secrets.CODECOV_TOKEN }} + directory: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7 + env_vars: OS,PYTHON + fail_ci_if_error: true + files: ./coverage.xml + flags: unittests + name: codecov-umbrella + path_to_write_report: ./coverage/codecov_report.txt + verbose: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2e91c4e..c06bdb2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ debian/linuxmuster-linuxclient7.substvars debian/linuxmuster-linuxclient7/ __pycache__ /public -venv \ No newline at end of file +venv +.coverage +coverage.xml \ No newline at end of file diff --git a/README.md b/README.md index b7401b9..6f7ebf0 100644 --- a/README.md +++ b/README.md @@ -56,4 +56,5 @@ Tested with Python 3.10: 4. `pip install -r requirements.txt` To run tests: -1. `pytest` \ No newline at end of file +1. `pytest` +2. with coverage: `pytest --cov=./ --cov-report=xml` \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/requirements.txt b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/requirements.txt index b51e859..a274df9 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/requirements.txt +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/requirements.txt @@ -1,4 +1,5 @@ attrs==21.4.0 +coverage==6.3.2 iniconfig==1.1.1 packaging==21.3 pluggy==1.0.0 @@ -7,5 +8,6 @@ pyasn1==0.4.8 pyasn1-modules==0.2.8 pyparsing==3.0.7 pytest==7.1.1 +pytest-cov==3.0.0 python-ldap==3.4.0 tomli==2.0.1 From 900e0df8808258c4eb122fb574295a6e172d8356 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 8 Apr 2022 22:58:20 +0200 Subject: [PATCH 03/57] Fix: install ldap deps --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 46758e9..91fb034 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,6 +17,10 @@ jobs: python-version: 3.7 - name: Generate coverage report run: | + apt-get install build-essential python3-dev \ + libldap2-dev libsasl2-dev slapd ldap-utils tox \ + lcov valgrind + pip install pytest pip install pytest-cov cd ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7 From 82fc68af3898999f7f43d9d4cbba17560f44b8b7 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 8 Apr 2022 22:59:17 +0200 Subject: [PATCH 04/57] Fix: sudo for apt --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 91fb034..8b5b2e9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,8 @@ jobs: python-version: 3.7 - name: Generate coverage report run: | - apt-get install build-essential python3-dev \ + sudo apt-get update + sudo apt-get install build-essential python3-dev \ libldap2-dev libsasl2-dev slapd ldap-utils tox \ lcov valgrind From 19250f2e98024d996dbda0d4398bfef3b3cd49e1 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 8 Apr 2022 23:01:35 +0200 Subject: [PATCH 05/57] Test: python 3.10 --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8b5b2e9..900863c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,13 +8,13 @@ jobs: os: [ubuntu-latest] env: OS: ${{ matrix.os }} - PYTHON: '3.7' + PYTHON: '3.10' steps: - uses: actions/checkout@master - name: Setup Python uses: actions/setup-python@master with: - python-version: 3.7 + python-version: 3.10 - name: Generate coverage report run: | sudo apt-get update From ce3988c2ab427f3e6365cc154b266e4ebd8486e8 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 8 Apr 2022 23:02:26 +0200 Subject: [PATCH 06/57] Fix: version as string --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 900863c..cb9b83b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: - name: Setup Python uses: actions/setup-python@master with: - python-version: 3.10 + python-version: '3.10' - name: Generate coverage report run: | sudo apt-get update From 7dfcd83d6c933a3c5d9dec293fe05b3721d1031b Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 8 Apr 2022 23:04:29 +0200 Subject: [PATCH 07/57] Fix: coverage.xml path --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cb9b83b..ab69605 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,7 +36,7 @@ jobs: directory: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7 env_vars: OS,PYTHON fail_ci_if_error: true - files: ./coverage.xml + files: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7/coverage.xml flags: unittests name: codecov-umbrella path_to_write_report: ./coverage/codecov_report.txt From af4d19e2db58482e4f3274e3aea338b7f968642f Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 8 Apr 2022 23:07:18 +0200 Subject: [PATCH 08/57] Fix: remove invalid import --- .github/workflows/{test.yml => unittests.yml} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename .github/workflows/{test.yml => unittests.yml} (92%) diff --git a/.github/workflows/test.yml b/.github/workflows/unittests.yml similarity index 92% rename from .github/workflows/test.yml rename to .github/workflows/unittests.yml index ab69605..8bff8fc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/unittests.yml @@ -1,4 +1,4 @@ -name: Example workflow for Codecov +name: Unittests on: [push] jobs: run: @@ -39,5 +39,4 @@ jobs: files: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7/coverage.xml flags: unittests name: codecov-umbrella - path_to_write_report: ./coverage/codecov_report.txt verbose: true \ No newline at end of file From e5da7082e7368f420599ff1582db6d9cf26ef1c7 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 8 Apr 2022 23:11:26 +0200 Subject: [PATCH 09/57] Fix: unneeded vars --- .github/workflows/unittests.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index 8bff8fc..0646678 100644 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -33,10 +33,5 @@ jobs: uses: codecov/codecov-action@v2 with: token: ${{ secrets.CODECOV_TOKEN }} - directory: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7 - env_vars: OS,PYTHON fail_ci_if_error: true - files: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7/coverage.xml - flags: unittests - name: codecov-umbrella - verbose: true \ No newline at end of file + files: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7/coverage.xml \ No newline at end of file From ee21027597ab705f8a994f22f1035e25af825d6f Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 8 Apr 2022 23:14:19 +0200 Subject: [PATCH 10/57] Feat: Add unittest and codecov badge --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6f7ebf0..ab176b3 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,11 @@ - - + + + + + Community Forum From 675087bfa2be3db6b1bd0f0208c2ac94aad6a19f Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 8 Apr 2022 23:14:39 +0200 Subject: [PATCH 11/57] Fix: url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab176b3..02e1255 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ - + From 3b004f994b6744c08e1b6e146988e41d3f67981c Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 10:14:55 +0200 Subject: [PATCH 12/57] Tests: Add test for computer module --- .../tests/test_computer.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_computer.py diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_computer.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_computer.py new file mode 100644 index 0000000..615d958 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_computer.py @@ -0,0 +1,70 @@ +from unittest import mock +from .. import computer + +@mock.patch("linuxmusterLinuxclient7.computer.socket.gethostname") +def test_hostname(mockSocketGetHostname): + mockSocketGetHostname.return_value = "computer1.linuxmuster.lan" + assert computer.hostname() == "computer1" + +@mock.patch("linuxmusterLinuxclient7.computer.hostname") +def test_krbHostName(mockHostname): + mockHostname.return_value = "computer1" + assert computer.krbHostName() == "COMPUTER1$" + +@mock.patch("linuxmusterLinuxclient7.computer.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.computer.hostname") +def test_readAttributes(mockHostname, mockLdapHelperSearchOne): + mockHostname.return_value = "computer1" + mockLdapHelperSearchOne.return_value = (True, { + "Attribute": "Value" + }) + + rc, attributes = computer.readAttributes() + assert rc + assert mockLdapHelperSearchOne.call_args.args[0].lower() == "(samaccountname=computer1$)" + assert attributes["Attribute"] == "Value" + + mockLdapHelperSearchOne.return_value = (False, None) + rc, attributes = computer.readAttributes() + assert not rc + assert attributes is None + + +@mock.patch("linuxmusterLinuxclient7.computer.localUserHelper.getGroupsOfLocalUser") +@mock.patch("linuxmusterLinuxclient7.computer.hostname") +def test_isInGroup(mockHostname, mockLocalUserHelperGetGroupsOfLocalUser): + mockHostname.return_value = "computer1" + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (True, ["group1", "group2"]) + assert computer.isInGroup("group1") + assert computer.isInGroup("group2") + assert not computer.isInGroup("group3") + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (False, ["group1", "group2"]) + assert not computer.isInGroup("group1") + assert not computer.isInGroup("group2") + assert not computer.isInGroup("group3") + + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (True, []) + assert not computer.isInGroup("group1") + assert not computer.isInGroup("group2") + assert not computer.isInGroup("group3") + + assert mockLocalUserHelperGetGroupsOfLocalUser.call_args.args[0] == computer.krbHostName() + +@mock.patch("linuxmusterLinuxclient7.computer.localUserHelper.getGroupsOfLocalUser") +@mock.patch("linuxmusterLinuxclient7.computer.hostname") +def test_isInAD(mockHostname, mockLocalUserHelperGetGroupsOfLocalUser): + mockHostname.return_value = "computer1" + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (True, ["domain computers"]) + assert computer.isInAD() + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (True, ["group1"]) + assert not computer.isInAD() + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (False, ["domain computers"]) + assert not computer.isInAD() + + assert mockLocalUserHelperGetGroupsOfLocalUser.call_args.args[0] == computer.krbHostName() \ No newline at end of file From d8466b60e47199f691977e0dab23ff4fca48d242 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 11:03:44 +0200 Subject: [PATCH 13/57] Fix: Only split at first = --- .../dist-packages/linuxmusterLinuxclient7/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/environment.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/environment.py index e25b622..06ea20d 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/environment.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/environment.py @@ -12,7 +12,7 @@ def export(keyValuePair): """ logging.debug("Saving export '{}' to tmp file".format(keyValuePair)) - envList = keyValuePair.split("=") + envList = keyValuePair.split("=", 1) if len(envList) == 2: os.putenv(envList[0], envList[1]) From ad65095dafb13bc8b9a6bb762db7f4f3eccffab7 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 11:03:58 +0200 Subject: [PATCH 14/57] Tests: Add tests for environment module --- .../tests/test_environment.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_environment.py diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_environment.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_environment.py new file mode 100644 index 0000000..b3dfca4 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_environment.py @@ -0,0 +1,42 @@ +from unittest import mock +from .. import environment, fileHelper +import os +import ctypes + +@mock.patch('linuxmusterLinuxclient7.environment.constants.tmpEnvironmentFilePath', "/tmp/env") +@mock.patch("linuxmusterLinuxclient7.environment.user.isInAD") +def test_export(mockUserIsInAD): + fileHelper.deleteFile("/tmp/env") + + mockUserIsInAD.return_value = False + assert not environment.export("Key=Value") + + mockUserIsInAD.return_value = True + os.environ["LinuxmusterLinuxclient7EnvFixActive"] = "0" + assert not environment.export("Key=Value") + + os.environ["LinuxmusterLinuxclient7EnvFixActive"] = "1" + assert environment.export("Key1=Value1") + + # check content of tmp file + with open("/tmp/env", "r") as tmpEnvironmentFile: + assert "export 'Key1=Value1'" in tmpEnvironmentFile.read() + +@mock.patch('linuxmusterLinuxclient7.environment.constants.tmpEnvironmentFilePath', "/tmp") +@mock.patch("linuxmusterLinuxclient7.environment.user.isInAD") +def test_exportUnwritableTmpfile(mockUserIsInAD): + mockUserIsInAD.return_value = True + os.environ["LinuxmusterLinuxclient7EnvFixActive"] = "1" + assert not environment.export("Key=Value") + +@mock.patch('linuxmusterLinuxclient7.environment.constants.tmpEnvironmentFilePath', "/tmp/env") +@mock.patch("linuxmusterLinuxclient7.environment.user.isInAD") +def test_unset(mockUserIsInAD): + fileHelper.deleteFile("/tmp/env") + mockUserIsInAD.return_value = True + os.environ["LinuxmusterLinuxclient7EnvFixActive"] = "1" + + environment.unset("Key") + + with open("/tmp/env", "r") as tmpEnvironmentFile: + assert "unset 'Key'" in tmpEnvironmentFile.read() \ No newline at end of file From 960ba14e68b65bb61dcf0b3293355072ee5c9c62 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 12:29:15 +0200 Subject: [PATCH 15/57] Refactor: Move gtkBookmarksFile path to constants --- .../dist-packages/linuxmusterLinuxclient7/constants.py | 2 ++ .../python3/dist-packages/linuxmusterLinuxclient7/user.py | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/constants.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/constants.py index 34d8243..06cfdf5 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/constants.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/constants.py @@ -5,6 +5,8 @@ defaultDomainAdminUser = "global-admin" # {} will be substituted for the username +gtkBookmarksFile = "/home/{}/.config/gtk-3.0/bookmarks" + shareMountBasepath = "/home/{}/media" hiddenShareMountBasepath = "/srv/samba/{}" machineAccountSysvolMountPath = "/var/lib/samba/sysvol" diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py index aa838f0..f3d64c6 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py @@ -91,13 +91,12 @@ def cleanTemplateUserGtkBookmarks(): """Remove gtk bookmarks of the template user from the current users `~/.config/gtk-3.0/bookmarks` file. """ logging.info("Cleaning {} gtk bookmarks".format(constants.templateUser)) - gtkBookmarksFile = "/home/{0}/.config/gtk-3.0/bookmarks".format(user.username()) - + gtkBookmarksFile = constants.gtkBookmarksFile.format(user.username()) if not os.path.isfile(gtkBookmarksFile): logging.warning("Gtk bookmarks file not found, skipping!") - return + return False - fileHelper.removeLinesInFileContainingString(gtkBookmarksFile, constants.templateUser) + return fileHelper.removeLinesInFileContainingString(gtkBookmarksFile, constants.templateUser) def getHomeShareMountpoint(): """ From 242a78839f3627981325a71f285e6ab3aed0d4e1 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 12:39:46 +0200 Subject: [PATCH 16/57] Doc: Better error message --- usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py index f3d64c6..ca0ce16 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py @@ -151,7 +151,7 @@ def _getHomeShareName(userAttributes=None): return True, shareName except Exception as e: - logging.error("Could not mount home dir of user") + logging.error("Could not find home dir of user.") logging.exception(e) return False, None \ No newline at end of file From 79e7952340917484cd5d49440956c14e3fc02741 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 12:40:11 +0200 Subject: [PATCH 17/57] Doc: Add comment --- usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py | 1 + 1 file changed, 1 insertion(+) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py index ca0ce16..3deae5d 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py @@ -151,6 +151,7 @@ def _getHomeShareName(userAttributes=None): return True, shareName except Exception as e: + # This happens when userAttributes does not contain homeDrive logging.error("Could not find home dir of user.") logging.exception(e) From edeb65a86326f17d54091747ef58578eafbf8960 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 12:51:34 +0200 Subject: [PATCH 18/57] Tests: Add tests for user module --- .../tests/test_user.py | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_user.py diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_user.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_user.py new file mode 100644 index 0000000..d8d260c --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_user.py @@ -0,0 +1,163 @@ +from unittest import mock +from .. import user + +@mock.patch("linuxmusterLinuxclient7.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.user.user.username") +@mock.patch("linuxmusterLinuxclient7.user.user.isInAD") +def test_readAttributes(mockUserIsInAD, mockUserUsername, mockLdapHelperSearchOne): + mockUserIsInAD.return_value = False + assert not user.readAttributes()[0] + + mockUserIsInAD.return_value = True + mockUserUsername.return_value = "user1" + mockLdapHelperSearchOne.return_value = (True, { + "Attribute": "Value" + }) + + rc, attributes = user.readAttributes() + assert rc + assert mockLdapHelperSearchOne.call_args.args[0].lower() == "(samaccountname=user1)" + assert attributes["Attribute"] == "Value" + +@mock.patch("linuxmusterLinuxclient7.user.readAttributes") +def test_school(mockReadAttributes): + mockReadAttributes.return_value = (False, None) + assert not user.school()[0] + + mockReadAttributes.return_value = (True, { + "sophomorixSchoolname": "school1" + }) + assert user.school() == (True, "school1") + +@mock.patch("linuxmusterLinuxclient7.user.getpass.getuser") +def test_username(mockGetpassGetuser): + mockGetpassGetuser.return_value = "user1" + assert user.username() == "user1" + + mockGetpassGetuser.return_value = "USER1" + assert user.username() == "user1" + +@mock.patch("linuxmusterLinuxclient7.user.computer.isInAD") +@mock.patch("linuxmusterLinuxclient7.user.localUserHelper.getGroupsOfLocalUser") +def test_isUserInAD(mockLocalUserHelperGetGroupsOfLocalUser, mockComputerIsInAD): + mockComputerIsInAD.return_value = False + assert not user.isUserInAD("user1") + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (False, None) + mockComputerIsInAD.return_value = True + assert not user.isUserInAD("user1") + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (True, ["group1"]) + mockComputerIsInAD.return_value = True + assert not user.isUserInAD("user1") + assert mockLocalUserHelperGetGroupsOfLocalUser.call_args.args[0] == "user1" + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (True, ["domain users"]) + mockComputerIsInAD.return_value = True + assert user.isUserInAD("user1") + assert mockLocalUserHelperGetGroupsOfLocalUser.call_args.args[0] == "user1" + +@mock.patch("linuxmusterLinuxclient7.user.username") +@mock.patch("linuxmusterLinuxclient7.user.isUserInAD") +def test_isInAD(mockIsUserInAD, mockUsername): + mockIsUserInAD.return_value = False + mockUsername.return_value = "user1" + assert not user.isInAD() + assert mockIsUserInAD.call_args.args[0] == "user1" + + mockIsUserInAD.return_value = True + mockUsername.return_value = "user1" + assert user.isInAD() + assert mockIsUserInAD.call_args.args[0] == "user1" + +@mock.patch("linuxmusterLinuxclient7.user.os.geteuid") +def test_isRoot(mockOsGeteuid): + mockOsGeteuid.return_value = 0 + assert user.isRoot() + + mockOsGeteuid.return_value = 1 + assert not user.isRoot() + +@mock.patch("linuxmusterLinuxclient7.user.localUserHelper.getGroupsOfLocalUser") +@mock.patch("linuxmusterLinuxclient7.user.username") +def test_isInGroup(mockUsername, mockLocalUserHelperGetGroupsOfLocalUser): + mockUsername.return_value = "user1" + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (True, ["group1", "group2"]) + assert user.isInGroup("group1") + assert user.isInGroup("group2") + assert not user.isInGroup("group3") + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (False, ["group1", "group2"]) + assert not user.isInGroup("group1") + assert not user.isInGroup("group2") + assert not user.isInGroup("group3") + + + mockLocalUserHelperGetGroupsOfLocalUser.return_value = (True, []) + assert not user.isInGroup("group1") + assert not user.isInGroup("group2") + assert not user.isInGroup("group3") + + assert mockLocalUserHelperGetGroupsOfLocalUser.call_args.args[0] == "user1" + +@mock.patch("linuxmusterLinuxclient7.user.constants.gtkBookmarksFile", "/tmp/{}-bookmarks") +@mock.patch("linuxmusterLinuxclient7.user.username") +def test_cleanTemplateUserGtkBookmarks(mockUsername): + mockUsername.return_value = "user1" + + with open("/tmp/user1-bookmarks", "w") as originalFile: + originalFile.write("line1\nlinuxadmin line2\nline3") + + assert user.cleanTemplateUserGtkBookmarks() + + with open("/tmp/user1-bookmarks", "r") as originalFile: + newContents = originalFile.read() + assert "linuxadmin" not in newContents and "line2" not in newContents + +@mock.patch("linuxmusterLinuxclient7.user.constants.gtkBookmarksFile", "/tmp") +def test_cleanTemplateUserGtkBookmarksUnwritableFile(): + assert not user.cleanTemplateUserGtkBookmarks() + +@mock.patch("linuxmusterLinuxclient7.user.readAttributes") +@mock.patch("linuxmusterLinuxclient7.user.username") +def test_getHomeShareMountpoint(mockUsername, mockReadAttributes): + mockUsername.return_value = "user1" + mockReadAttributes.return_value = (False, None) + assert not user.getHomeShareMountpoint()[0] + + # homeDrive missing from attributes + mockReadAttributes.return_value = (True, {}) + assert not user.getHomeShareMountpoint()[0] + + + mockReadAttributes.return_value = (True, {"homeDrive": "H:"}) + rc, homeShareMountpoint = user.getHomeShareMountpoint() + assert rc + assert homeShareMountpoint == "/home/user1/media/user1 (H:)" + +@mock.patch("linuxmusterLinuxclient7.user.shares.mountShare") +@mock.patch("linuxmusterLinuxclient7.user.readAttributes") +@mock.patch("linuxmusterLinuxclient7.user.username") +def test_mountHomeShare(mockUsername, mockReadAttributes, mockSharesMountShare): + mockUsername.return_value = "user1" + mockReadAttributes.return_value = (False, None) + assert not user.mountHomeShare()[0] + + mockReadAttributes.return_value = (True, {}) + assert not user.mountHomeShare()[0] + + mockReadAttributes.return_value = (True, {"homeDrive": "H:"}) + assert not user.mountHomeShare()[0] + + mockReadAttributes.return_value = (True, {"homeDrive": "H:", "homeDirectory": "user1"}) + mockSharesMountShare.return_value = (False, None) + assert not user.mountHomeShare()[0] + + mockReadAttributes.return_value = (True, {"homeDrive": "H:", "homeDirectory": "user1"}) + mockSharesMountShare.return_value = (True, "mountpoint") + rc, mountpoint = user.mountHomeShare() + assert rc + assert mountpoint == "mountpoint" + assert mockSharesMountShare.call_args == mock.call('user1', shareName='user1 (H:)', hiddenShare=False, username='user1') + From 1208b912d80ffcd5aadfd8a60c727c2180b2a73d Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 13:00:25 +0200 Subject: [PATCH 19/57] Doc: Update comment --- .../python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py index ad9b55e..b183681 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py @@ -8,7 +8,7 @@ def removeLinesInFileContainingString(filePath, forbiddenStrings): :param filePath: The path to the file :type filePath: str :param forbiddenStrings: The string to search for - :type forbiddenStrings: str + :type forbiddenStrings: str or list[str] :return: True on success, False otherwise :rtype: bool """ From b95d88568e37606dfbeaa587477b9e072cda41d6 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 13:11:23 +0200 Subject: [PATCH 20/57] Doc: More percise comment --- .../python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py index b183681..4e98600 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py @@ -48,7 +48,7 @@ def deleteFile(filePath): :param filePath: The path of the file :type filePath: str - :return: True on success, False otherwise + :return: True on success or if the file does not exist, False otherwise :rtype: bool """ try: From aeeb44f86a266d8d4373c084bc658a222c17389c Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 13:17:49 +0200 Subject: [PATCH 21/57] Fix: Don't delete folders in delteFile function --- .../python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py index 4e98600..4dec509 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py @@ -53,7 +53,7 @@ def deleteFile(filePath): """ try: if os.path.exists(filePath): - os.unlink(filePath) + os.remove(filePath) return True except Exception as e: logging.error("Failed!") From fd5f1b0986e829d565bc392110c036dcd189a96f Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 13:25:41 +0200 Subject: [PATCH 22/57] Doc: More precise comment --- .../python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py index 4dec509..5f69f6c 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py @@ -68,7 +68,7 @@ def deleteFilesWithExtension(directory, extension): :type directory: str :param extension: The file extension :type extension: str - :return: True on success, False otherwise + :return: True on success or if the path does not exist, False otherwise :rtype: bool """ if directory.endswith("/"): From e44813e5fca47ca112d1814302185a9e15e8d460 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 13:32:20 +0200 Subject: [PATCH 23/57] Docs: More precise comment --- .../python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py index 5f69f6c..57a7a0f 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py @@ -105,7 +105,7 @@ def deleteDirectory(directory): def deleteAllInDirectory(directory): """ - Delete all files in a given directory + Delete all files and folders in a given directory :param directory: The path of the directory :type directory: str From bfc1c14683f1f744a76d37287a84bbf87f9f35ec Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 13:36:39 +0200 Subject: [PATCH 24/57] Doc: More precise comment --- .../python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py index 57a7a0f..3fccd1f 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py @@ -109,7 +109,7 @@ def deleteAllInDirectory(directory): :param directory: The path of the directory :type directory: str - :return: True on success, False otherwise + :return: True on success or if the directory does not exist, False otherwise :rtype: bool """ From 6773c6429fa0d000201a08d440935517f46ae0b4 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 13:43:45 +0200 Subject: [PATCH 25/57] Tests: Change naming --- .../dist-packages/linuxmusterLinuxclient7/tests/test_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_user.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_user.py index d8d260c..266004f 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_user.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_user.py @@ -116,7 +116,7 @@ def test_cleanTemplateUserGtkBookmarks(mockUsername): assert "linuxadmin" not in newContents and "line2" not in newContents @mock.patch("linuxmusterLinuxclient7.user.constants.gtkBookmarksFile", "/tmp") -def test_cleanTemplateUserGtkBookmarksUnwritableFile(): +def test_cleanTemplateUserGtkBookmarks_unwritableFile(): assert not user.cleanTemplateUserGtkBookmarks() @mock.patch("linuxmusterLinuxclient7.user.readAttributes") From c1c682c221ffb6e99e6ec750804cb9eae808239c Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 13:43:58 +0200 Subject: [PATCH 26/57] Tests: Add tests for fileHelper module --- .../tests/test_fileHelper.py | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_fileHelper.py diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_fileHelper.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_fileHelper.py new file mode 100644 index 0000000..5fc909f --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_fileHelper.py @@ -0,0 +1,119 @@ +from unittest import mock +from .. import fileHelper +import os + +def test_removeLinesInFileContainingString(): + assert fileHelper.deleteFile("/tmp/fileWithForbiddenLine") + + # unreadable file + assert not fileHelper.removeLinesInFileContainingString("/tmp", "forbidden") + + with open("/tmp/fileWithForbiddenLine", "w") as file: + file.write("line1\nforbidden line2\nline3") + + # unwritable file + os.chmod("/tmp/fileWithForbiddenLine", 0o444) + assert not fileHelper.removeLinesInFileContainingString("/tmp/fileWithForbiddenLine", "forbidden") + + # all fine + os.chmod("/tmp/fileWithForbiddenLine", 0o666) + assert fileHelper.removeLinesInFileContainingString("/tmp/fileWithForbiddenLine", "forbidden") + +def test_deleteFile(): + + assert fileHelper.deleteFile("/tmp/thisFileDoesNotExist") + + with open("/tmp/thisFileShouldBeDeleted", "w") as file: + file.write("line1\nline2\nline3") + + assert fileHelper.deleteFile("/tmp/thisFileShouldBeDeleted") + assert not os.path.exists("/tmp/thisFileShouldBeDeleted") + + # Folders should not be deleted + try: + os.mkdir("/tmp/thisFolderShouldNotBeDeleted") + except FileExistsError: + pass + assert not fileHelper.deleteFile("/tmp/thisFolderShouldNotBeDeleted") + assert os.path.exists("/tmp/thisFolderShouldNotBeDeleted") + +def test_deleteFilesWithExtension(): + for i in range(3): + with open(f"/tmp/thisFile{i}.shouldBeDeleted", "w") as file: + file.write("line1\nline2\nline3") + + with open(f"/tmp/thisFile{i}.shouldAlsoBeDeleted", "w") as file: + file.write("line1\nline2\nline3") + + assert fileHelper.deleteFilesWithExtension("/tmp", ".shouldBeDeleted") + assert fileHelper.deleteFilesWithExtension("/tmp/", ".shouldAlsoBeDeleted") + + for i in range(3): + assert not os.path.exists(f"/tmp/thisFile{i}.shouldBeDeleted") + assert not os.path.exists(f"/tmp/thisFile{i}.shouldAlsoBeDeleted") + + # Non existent + assert fileHelper.deleteFilesWithExtension("/tmp/thisPathDoesNotExist", ".txt") + + # Folders should not be deleted + try: + os.mkdir("/tmp/thisFolderShouldNot.BeDeleted") + except FileExistsError: + pass + + assert not fileHelper.deleteFilesWithExtension("/tmp", ".BeDeleted") + assert os.path.exists("/tmp/thisFolderShouldNot.BeDeleted") + +def test_deleteDirectory(): + try: + os.mkdir("/tmp/ThisFolderShouldBeDeleted") + os.mkdir("/tmp/ThisFolderShouldBeDeleted/ThisOneToo") + os.mkdir("/tmp/ThisFolderShouldBeDeleted/SameForThis") + os.mkdir("/tmp/ThisFolderShouldBeDeleted/SameForThis/AlsoThis") + except FileExistsError: + pass + with open(f"/tmp/ThisFolderShouldBeDeleted/SameForThis/AlsoThis/ThisFileToo", "w") as file: + file.write("line1\nline2\nline3") + + assert fileHelper.deleteDirectory("/tmp/ThisFolderShouldBeDeleted") + assert not os.path.exists("/tmp/ThisFolderShouldBeDeleted") + + assert not fileHelper.deleteDirectory("/tmp/thisPathDoesNotExist") + +def test_deleteAllInDirectory(): + try: + os.mkdir("/tmp/ThisDirShouldStayButItsContensMustGo") + except FileExistsError: + pass + + for i in range(3): + with open(f"/tmp/ThisDirShouldStayButItsContensMustGo/thisFile{i}.shouldBeDeleted", "w") as file: + file.write("line1\nline2\nline3") + try: + os.mkdir(f"/tmp/ThisDirShouldStayButItsContensMustGo/Folder{i}") + except FileExistsError: + pass + + assert fileHelper.deleteAllInDirectory("/tmp/ThisDirShouldStayButItsContensMustGo/") + for i in range(3): + assert not os.path.exists(f"/tmp/ThisDirShouldStayButItsContensMustGo/thisFile{i}.shouldBeDeleted") + assert not os.path.exists(f"/tmp/ThisDirShouldStayButItsContensMustGo/Folder{i}") + + assert fileHelper.deleteAllInDirectory("/tmp/thisPathDoesNotExist") + +@mock.patch("linuxmusterLinuxclient7.fileHelper.deleteFile") +@mock.patch("linuxmusterLinuxclient7.fileHelper.deleteDirectory") +def test_deleteAllInDirectory_deletionError(mockDeleteDirectory, mockDeleteFile): + mockDeleteDirectory.return_value = False + mockDeleteFile.return_value = False + + try: + os.mkdir("/tmp/ThisDirShouldStayButItsContensMustGo") + os.mkdir("/tmp/ThisDirShouldStayButItsContensMustGo/Folder") + except FileExistsError: + pass + + with open("/tmp/ThisDirShouldStayButItsContensMustGo/thisFile.shouldBeDeleted", "w") as file: + file.write("line1\nline2\nline3") + + assert not fileHelper.deleteAllInDirectory("/tmp/ThisDirShouldStayButItsContensMustGo/") \ No newline at end of file From 0be2c86649d21bfd94bbf42ea55a118077f8a34d Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 13:48:36 +0200 Subject: [PATCH 27/57] Tests: ignore tests from coverage --- .github/workflows/unittests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index 0646678..4aa273e 100644 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -34,4 +34,5 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true - files: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7/coverage.xml \ No newline at end of file + files: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7/coverage.xml + gcov_ignore: usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests \ No newline at end of file From c820722df8477bdf81bba3f45ffa4b95bac36520 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 13:55:19 +0200 Subject: [PATCH 28/57] Tests: Ignore tests from coverage --- .github/workflows/unittests.yml | 3 +-- codecov.yml | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 codecov.yml diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index 4aa273e..0646678 100644 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -34,5 +34,4 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true - files: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7/coverage.xml - gcov_ignore: usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests \ No newline at end of file + files: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7/coverage.xml \ No newline at end of file diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..f3e283b --- /dev/null +++ b/codecov.yml @@ -0,0 +1,5 @@ +coverage: + ignore: + - "test_*.py" +ignore: + - "test_*.py" \ No newline at end of file From 6cb79e63619871235bfd2370427c0b0995f70ca7 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 13:58:07 +0200 Subject: [PATCH 29/57] Fix: Move codecove config to right location --- codecov.yml => .github/.codecov.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename codecov.yml => .github/.codecov.yml (100%) diff --git a/codecov.yml b/.github/.codecov.yml similarity index 100% rename from codecov.yml rename to .github/.codecov.yml From e50a105bc34cc83cf074db28c3c2759efa41f514 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 9 Apr 2022 14:02:41 +0200 Subject: [PATCH 30/57] Tests: Try to fix ignore --- .github/.codecov.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/.codecov.yml b/.github/.codecov.yml index f3e283b..b6b0ec2 100644 --- a/.github/.codecov.yml +++ b/.github/.codecov.yml @@ -1,5 +1,3 @@ -coverage: - ignore: - - "test_*.py" ignore: - - "test_*.py" \ No newline at end of file + - "**/test_*.py" + - "usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests" \ No newline at end of file From 79a2bd0e140230a78f63ce4f0049f2ee45feddd3 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sun, 10 Apr 2022 20:42:47 +0200 Subject: [PATCH 31/57] Refactor: fix better return codes and error handling --- .../linuxmusterLinuxclient7/gpo.py | 75 ++++++++++++------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py index 20f10ba..8c0688a 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py @@ -19,13 +19,17 @@ def processAllPolicies(): logging.fatal("* Error when loading applicable GPOs! Shares and printers will not work.") return False + allSuccessfull = True for policyDn in policyDnList: - _parsePolicy(policyDn) + allSuccessfull = _parsePolicy(policyDn) and allSuccessfull + + return allSuccessfull # -------------------- # - Helper functions - # -------------------- - +""" +Currently unused. May be useful for solving issue def _parseGplinkSring(string): # a gPLink strink looks like this: # [LDAP://;][LDAP://;][...] @@ -44,6 +48,8 @@ def _extractOUsFromDN(dn): ouList.reverse() return ouList +""" + def _findApplicablePolicies(): policyDnList = [] @@ -85,39 +91,39 @@ def _findApplicablePolicies(): def _parsePolicy(policyDn): logging.info("=== Parsing policy [{0};{1}] ===".format(policyDn[0], policyDn[1])) + """ (not needed because it's currently hardcoded) # Check if the policy is disabled if policyDn[1] == 1: logging.info("===> Policy is disabled! ===") return True + """ # Find policy in AD rc, policyAdObject = ldapHelper.searchOne("(distinguishedName={})".format(policyDn[0])) if not rc: logging.error("===> Could not find poilcy in AD! ===") - return False, None + return False # mount the share the policy is on (probaply already mounted, just to be sure) rc, localPolicyPath = shares.getMountpointOfRemotePath(policyAdObject["gPCFileSysPath"], hiddenShare = True, autoMount = True) if not rc: logging.error("===> Could not mount path of poilcy! ===") - return False, None + return False try: # parse drives - _processDrivesPolicy(localPolicyPath) + allSuccessfull = _processDrivesPolicy(localPolicyPath) # parse printers - _processPrintersPolicy(localPolicyPath) + _processPrintersPolicy(localPolicyPath) and allSuccessfull except Exception as e: logging.error("An error occured when parsing policy!") logging.exception(e) + return False logging.info("===> Parsed policy [{0};{1}] ===".format(policyDn[0], policyDn[1])) + return allSuccessfull def _parseXmlFilters(filtersXmlNode): - if not filtersXmlNode.tag == "Filters": - logging.warning("Tried to parse a non-filter node as a filter!") - return [] - filters = [] for xmlFilter in filtersXmlNode: @@ -129,6 +135,12 @@ def _parseXmlFilters(filtersXmlNode): # userContext defines if the filter applies in user or computer context "type": xmlFilter.tag }) + else: + filters.append({ + "bool": "AND", + "type": "FilterInvalid" + }) + logging.warning(f"Unknown filter type: {xmlFilter.tag}! Assuming condition is false.") return filters @@ -136,19 +148,19 @@ def _processFilters(policies): filteredPolicies = [] for policy in policies: - if not len(policy["filters"]) > 0: + if not len(policy["filters"]) > 0: filteredPolicies.append(policy) else: filtersPassed = True for filter in policy["filters"]: logging.debug("Testing filter: {}".format(filter)) - # TODO: check for AND and OR if filter["bool"] == "AND": filtersPassed = filtersPassed and _processFilter(filter) elif filter["bool"] == "OR": filtersPassed = filtersPassed or _processFilter(filter) else: - logging.warning("Unknown boolean operation: {}! Cannot process filter!".format(filter["bool"])) + logging.warning("Unknown boolean operation: {}! Assuming condition is false.".format(filter["bool"])) + filtersPassed = False if filtersPassed: filteredPolicies.append(policy) @@ -162,7 +174,8 @@ def _processFilter(filter): elif filter["userContext"] == "0": return computer.isInGroup(filter["name"]) - return False + elif filter["type"] == "FilterInvalid": + return False def _parseXmlPolicy(policyFile): if not os.path.isfile(policyFile): @@ -186,13 +199,13 @@ def _processDrivesPolicy(policyBasepath): if not rc: logging.error("==> Error while reading Drives policy file, skipping! ==") - return + return False xmlDrives = tree.getroot() if not xmlDrives.tag == "Drives": logging.warning("==> Drive policy xml File is of invalid format, skipping! ==") - return + return False for xmlDrive in xmlDrives: @@ -209,29 +222,32 @@ def _processDrivesPolicy(policyBasepath): drive["path"] = xmlDriveProperty.attrib["path"] drive["useLetter"] = xmlDriveProperty.attrib["useLetter"] except Exception as e: - logging.warning("Exception when parsing a drive policy XML file") + logging.error("Exception when parsing a drive policy, it is missing an attribute:") logging.exception(e) - continue + break if xmlDriveProperty.tag == "Filters": drive["filters"] = _parseXmlFilters(xmlDriveProperty) - - shareList.append(drive) + else: + shareList.append(drive) shareList = _processFilters(shareList) logging.info("Found shares:") for drive in shareList: - logging.info("* {:10}| {:5}| {:40}| {:5}".format(drive["label"], drive["letter"], drive["path"], drive["useLetter"])) + logging.info("* {:15}| {:5}| {:40}| {:5}".format(drive["label"], drive["letter"], drive["path"], drive["useLetter"])) for drive in shareList: if drive["useLetter"] == "1": shareName = f"{drive['label']} ({drive['letter']}:)" else: shareName = drive["label"] + shares.mountShare(drive["path"], shareName=shareName) logging.info("==> Successfully parsed a drive policy! ==") + + return True def _processPrintersPolicy(policyBasepath): logging.info("== Parsing a printer policy! ==") @@ -242,16 +258,15 @@ def _processPrintersPolicy(policyBasepath): if not rc: logging.error("==> Error while reading Printer policy file, skipping! ==") - return + return False xmlPrinters = tree.getroot() if not xmlPrinters.tag == "Printers": logging.warning("==> Printer policy xml File is of invalid format, skipping! ==") - return + return False for xmlPrinter in xmlPrinters: - if xmlPrinter.tag != "SharedPrinter" or ("disabled" in xmlPrinter.attrib and xmlPrinter.attrib["disabled"] == "1"): continue @@ -261,8 +276,8 @@ def _processPrintersPolicy(policyBasepath): try: printer["name"] = xmlPrinter.attrib["name"] except Exception as e: - logging.warning("Exception when reading a printer name from a printer policy XML file") - logging.exception(e) + logging.error("Exception when parsing a printer policy, it is missing the name attribute.") + continue for xmlPrinterProperty in xmlPrinter: if xmlPrinterProperty.tag == "Properties": @@ -273,12 +288,12 @@ def _processPrintersPolicy(policyBasepath): except Exception as e: logging.warning("Exception when parsing a printer policy XML file") logging.exception(e) - continue + break if xmlPrinterProperty.tag == "Filters": printer["filters"] = _parseXmlFilters(xmlPrinterProperty) - - printerList.append(printer) + else: + printerList.append(printer) printerList = _processFilters(printerList) @@ -288,3 +303,5 @@ def _processPrintersPolicy(policyBasepath): printers.installPrinter(printer["path"], printer["name"]) logging.info("==> Successfully parsed a printer policy! ==") + + return True \ No newline at end of file From 55a3c75407d633e49354f61822baf668b7418dcf Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sun, 10 Apr 2022 20:43:08 +0200 Subject: [PATCH 32/57] Tests: Add tests for gpo module --- .../User/Preferences/Drives/Drives.xml | 3 + .../User/Preferences/Printers/Printers.xml | 3 + .../User/Preferences/Drives/Drives.xml | 18 ++ .../User/Preferences/Printers/Printers.xml | 24 ++ .../User/Preferences/Drives/Drives.xml | 1 + .../User/Preferences/Printers/Printers.xml | 15 ++ .../User/Preferences/Drives/Drives.xml | 18 ++ .../User/Preferences/Printers/Printers.xml | 17 ++ .../linuxmusterLinuxclient7/tests/test_gpo.py | 251 ++++++++++++++++++ 9 files changed, 350 insertions(+) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid1/User/Preferences/Drives/Drives.xml create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid1/User/Preferences/Printers/Printers.xml create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid2/User/Preferences/Drives/Drives.xml create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid2/User/Preferences/Printers/Printers.xml create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid3/User/Preferences/Drives/Drives.xml create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid3/User/Preferences/Printers/Printers.xml create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy1/User/Preferences/Drives/Drives.xml create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy1/User/Preferences/Printers/Printers.xml create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_gpo.py diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid1/User/Preferences/Drives/Drives.xml b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid1/User/Preferences/Drives/Drives.xml new file mode 100644 index 0000000..553aaaa --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid1/User/Preferences/Drives/Drives.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid1/User/Preferences/Printers/Printers.xml b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid1/User/Preferences/Printers/Printers.xml new file mode 100644 index 0000000..9046236 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid1/User/Preferences/Printers/Printers.xml @@ -0,0 +1,3 @@ + + + diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid2/User/Preferences/Drives/Drives.xml b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid2/User/Preferences/Drives/Drives.xml new file mode 100644 index 0000000..f893f5a --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid2/User/Preferences/Drives/Drives.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid2/User/Preferences/Printers/Printers.xml b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid2/User/Preferences/Printers/Printers.xml new file mode 100644 index 0000000..1e781ee --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid2/User/Preferences/Printers/Printers.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid3/User/Preferences/Drives/Drives.xml b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid3/User/Preferences/Drives/Drives.xml new file mode 100644 index 0000000..4f588d5 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid3/User/Preferences/Drives/Drives.xml @@ -0,0 +1 @@ +This is not xml \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid3/User/Preferences/Printers/Printers.xml b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid3/User/Preferences/Printers/Printers.xml new file mode 100644 index 0000000..5e75368 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy-invalid3/User/Preferences/Printers/Printers.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy1/User/Preferences/Drives/Drives.xml b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy1/User/Preferences/Drives/Drives.xml new file mode 100644 index 0000000..c25a7c6 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy1/User/Preferences/Drives/Drives.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy1/User/Preferences/Printers/Printers.xml b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy1/User/Preferences/Printers/Printers.xml new file mode 100644 index 0000000..97d46d7 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/policy1/User/Preferences/Printers/Printers.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_gpo.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_gpo.py new file mode 100644 index 0000000..596e4e1 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_gpo.py @@ -0,0 +1,251 @@ +from unittest import mock +from .. import gpo +import os + +@mock.patch("linuxmusterLinuxclient7.gpo.computer.isInGroup") +@mock.patch("linuxmusterLinuxclient7.gpo.user.isInGroup") +@mock.patch("linuxmusterLinuxclient7.gpo.printers.installPrinter") +@mock.patch("linuxmusterLinuxclient7.gpo.shares.mountShare") +@mock.patch("linuxmusterLinuxclient7.gpo.shares.getMountpointOfRemotePath") +@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_allOkAllTrue(mockUserSchool, mockLdapHelperSearchOne, mockSharesGetMountpointOfRemotePath, mockSharesmMountShare, mockPrintersInstallPrinter, mockUserIsInGroup, mockComputerIsInGroup): + mockUserSchool.return_value = (True, "school1") + mockLdapHelperSearchOne.return_value = (True, { + "distinguishedName": "policy1", + "gPCFileSysPath": "\\\\linuxmuster.lan\\sysvol\\linuxmuster.lan\\Policies\\policy1" + }) + mockSharesGetMountpointOfRemotePath.return_value = (True, f"{os.path.dirname(os.path.realpath(__file__))}/files/policy1") + mockSharesmMountShare.return_value = (True, "") + mockPrintersInstallPrinter.return_value = True + mockUserIsInGroup.return_value = True + mockComputerIsInGroup.return_value = True + + # Drives: /User/Preferences/Drives/Drives.xml + # Printers: /User/Preferences/Printers/Printers.xml + + assert gpo.processAllPolicies() + + assert mockLdapHelperSearchOne.call_args_list[0].args[0] == "(displayName=sophomorix:school:school1)" + assert mockLdapHelperSearchOne.call_args_list[1].args[0] == "(distinguishedName=policy1)" + assert mockSharesGetMountpointOfRemotePath.call_args_list[0].args[0] == "\\\\linuxmuster.lan\\sysvol\\linuxmuster.lan\\Policies\\policy1" + + # Check shares + assert mockUserIsInGroup.call_args_list[0].args[0] == "teachers" + assert len(mockSharesmMountShare.call_args_list) == 3 + assert mockSharesmMountShare.call_args_list[0] == mock.call('\\\\server\\default-school\\program', shareName='Programs (K:)') + # Projects (P:) is disabled and should not be mounted + assert mockSharesmMountShare.call_args_list[1] == mock.call('\\\\server\\default-school\\students', shareName='Students-Home (S:)') + assert mockSharesmMountShare.call_args_list[2] == mock.call('\\\\server\\default-school\\share', shareName='Shares') + + # Check printers + assert mockUserIsInGroup.call_args_list[1].args[0] == "printer1" + # computer.isInGroup does not have to be called, because it is + # an or condition and the user is already in the group. + # assert mockComputerIsInGroup.call_args_list[1].args[0] == "printer1" + assert len(mockPrintersInstallPrinter.call_args_list) == 1 + assert mockPrintersInstallPrinter.call_args_list[0] == mock.call('ipp://SERVER/printers/PRINTER1', 'PRINTER1') + +@mock.patch("linuxmusterLinuxclient7.gpo.computer.isInGroup") +@mock.patch("linuxmusterLinuxclient7.gpo.user.isInGroup") +@mock.patch("linuxmusterLinuxclient7.gpo.printers.installPrinter") +@mock.patch("linuxmusterLinuxclient7.gpo.shares.mountShare") +@mock.patch("linuxmusterLinuxclient7.gpo.shares.getMountpointOfRemotePath") +@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_allOkUserInGroupFalse(mockUserSchool, mockLdapHelperSearchOne, mockSharesGetMountpointOfRemotePath, mockSharesmMountShare, mockPrintersInstallPrinter, mockUserIsInGroup, mockComputerIsInGroup): + mockUserSchool.return_value = (True, "school1") + mockLdapHelperSearchOne.return_value = (True, { + "distinguishedName": "policy1", + "gPCFileSysPath": "\\\\linuxmuster.lan\\sysvol\\linuxmuster.lan\\Policies\\policy1" + }) + mockSharesGetMountpointOfRemotePath.return_value = (True, f"{os.path.dirname(os.path.realpath(__file__))}/files/policy1") + mockSharesmMountShare.return_value = (True, "") + mockPrintersInstallPrinter.return_value = True + mockUserIsInGroup.return_value = False + mockComputerIsInGroup.return_value = True + + assert gpo.processAllPolicies() + + # Checks which are already done in other tests are not needed again + + # Check shares + assert mockUserIsInGroup.call_args_list[0].args[0] == "teachers" + assert len(mockSharesmMountShare.call_args_list) == 2 + assert mockSharesmMountShare.call_args_list[0] == mock.call('\\\\server\\default-school\\program', shareName='Programs (K:)') + # Projects (P:) is disabled and should not be mounted + # User is not member of teachers assert mockSharesmMountShare.call_args_list[1] == mock.call('\\\\server\\default-school\\students', shareName='Students-Home (S:)') + assert mockSharesmMountShare.call_args_list[1] == mock.call('\\\\server\\default-school\\share', shareName='Shares') + + # Check printers + assert mockUserIsInGroup.call_args_list[1].args[0] == "printer1" + assert mockComputerIsInGroup.call_args_list[0].args[0] == "printer1" + assert len(mockPrintersInstallPrinter.call_args_list) == 1 + assert mockPrintersInstallPrinter.call_args_list[0] == mock.call('ipp://SERVER/printers/PRINTER1', 'PRINTER1') + +@mock.patch("linuxmusterLinuxclient7.gpo.computer.isInGroup") +@mock.patch("linuxmusterLinuxclient7.gpo.user.isInGroup") +@mock.patch("linuxmusterLinuxclient7.gpo.printers.installPrinter") +@mock.patch("linuxmusterLinuxclient7.gpo.shares.mountShare") +@mock.patch("linuxmusterLinuxclient7.gpo.shares.getMountpointOfRemotePath") +@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_allOkUserInGroupFalseComputerInGroupFalse(mockUserSchool, mockLdapHelperSearchOne, mockSharesGetMountpointOfRemotePath, mockSharesmMountShare, mockPrintersInstallPrinter, mockUserIsInGroup, mockComputerIsInGroup): + mockUserSchool.return_value = (True, "school1") + mockLdapHelperSearchOne.return_value = (True, { + "distinguishedName": "policy1", + "gPCFileSysPath": "\\\\linuxmuster.lan\\sysvol\\linuxmuster.lan\\Policies\\policy1" + }) + mockSharesGetMountpointOfRemotePath.return_value = (True, f"{os.path.dirname(os.path.realpath(__file__))}/files/policy1") + mockSharesmMountShare.return_value = (True, "") + mockPrintersInstallPrinter.return_value = True + mockUserIsInGroup.return_value = False + mockComputerIsInGroup.return_value = False + + assert gpo.processAllPolicies() + + # Checks which are already done in other tests are not needed again + + # Check printers + assert mockUserIsInGroup.call_args_list[1].args[0] == "printer1" + assert mockComputerIsInGroup.call_args_list[0].args[0] == "printer1" + assert len(mockPrintersInstallPrinter.call_args_list) == 0 + # Printer should not be applied assert mockPrintersInstallPrinter.call_args_list[0] == mock.call('ipp://SERVER/printers/PRINTER1', 'PRINTER1') + +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_userSchoolError(mockUserSchool): + mockUserSchool.return_value = (False, None) + + assert not gpo.processAllPolicies() + +@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_ldapSearchOneErrorFirst(mockUserSchool, mockLdapHelperSearchOne): + mockUserSchool.return_value = (True, "school1") + mockLdapHelperSearchOne.return_value = (False, None) + + assert not gpo.processAllPolicies() + +@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_ldapSearchOneErrorSecond(mockUserSchool, mockLdapHelperSearchOne): + mockUserSchool.return_value = (True, "school1") + mockLdapHelperSearchOne.side_effect = [(True, { + "distinguishedName": "policy1" + }), (False, None)] + + assert not gpo.processAllPolicies() + +@mock.patch("linuxmusterLinuxclient7.gpo.shares.getMountpointOfRemotePath") +@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_sharesGetMountpointOfRemotePathError(mockUserSchool, mockLdapHelperSearchOne, mockSharesGetMountpointOfRemotePath): + mockUserSchool.return_value = (True, "school1") + mockLdapHelperSearchOne.return_value = (True, { + "distinguishedName": "policy1", + "gPCFileSysPath": "\\\\linuxmuster.lan\\sysvol\\linuxmuster.lan\\Policies\\policy1" + }) + mockSharesGetMountpointOfRemotePath.return_value = (False, None) + + assert not gpo.processAllPolicies() + +@mock.patch("linuxmusterLinuxclient7.gpo.shares.getMountpointOfRemotePath") +@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_policyPathInvalid(mockUserSchool, mockLdapHelperSearchOne, mockSharesGetMountpointOfRemotePath): + mockUserSchool.return_value = (True, "school1") + mockLdapHelperSearchOne.return_value = (True, { + "distinguishedName": "policy1", + "gPCFileSysPath": "\\\\linuxmuster.lan\\sysvol\\linuxmuster.lan\\Policies\\policy1" + }) + mockSharesGetMountpointOfRemotePath.return_value = (True, "/tmp/thisFolderDoesNotExist") + + assert not gpo.processAllPolicies() + +@mock.patch("linuxmusterLinuxclient7.gpo.shares.getMountpointOfRemotePath") +@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_policyXmlRootTagIncorrect(mockUserSchool, mockLdapHelperSearchOne, mockSharesGetMountpointOfRemotePath): + mockUserSchool.return_value = (True, "school1") + mockLdapHelperSearchOne.return_value = (True, { + "distinguishedName": "policy1", + "gPCFileSysPath": "\\\\linuxmuster.lan\\sysvol\\linuxmuster.lan\\Policies\\policy1" + }) + mockSharesGetMountpointOfRemotePath.return_value = (True, f"{os.path.dirname(os.path.realpath(__file__))}/files/policy-invalid1") + + assert not gpo.processAllPolicies() + +@mock.patch("linuxmusterLinuxclient7.gpo.shares.getMountpointOfRemotePath") +@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_policyXmlInvalidFormatAndInvalidFilters(mockUserSchool, mockLdapHelperSearchOne, mockSharesGetMountpointOfRemotePath): + mockUserSchool.return_value = (True, "school1") + mockLdapHelperSearchOne.return_value = (True, { + "distinguishedName": "policy1", + "gPCFileSysPath": "\\\\linuxmuster.lan\\sysvol\\linuxmuster.lan\\Policies\\policy1" + }) + mockSharesGetMountpointOfRemotePath.return_value = (True, f"{os.path.dirname(os.path.realpath(__file__))}/files/policy-invalid3") + + assert not gpo.processAllPolicies() + +@mock.patch("linuxmusterLinuxclient7.gpo.computer.isInGroup") +@mock.patch("linuxmusterLinuxclient7.gpo.user.isInGroup") +@mock.patch("linuxmusterLinuxclient7.gpo.printers.installPrinter") +@mock.patch("linuxmusterLinuxclient7.gpo.shares.mountShare") +@mock.patch("linuxmusterLinuxclient7.gpo.shares.getMountpointOfRemotePath") +@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_policyXmlMissingAttributes(mockUserSchool, mockLdapHelperSearchOne, mockSharesGetMountpointOfRemotePath, mockSharesmMountShare, mockPrintersInstallPrinter, mockUserIsInGroup, mockComputerIsInGroup): + # Programs (K:) is missing the label + # PRINTER1 is missing the name + # PRINTER2 is missing the path + mockUserSchool.return_value = (True, "school1") + mockLdapHelperSearchOne.return_value = (True, { + "distinguishedName": "policy1", + "gPCFileSysPath": "\\\\linuxmuster.lan\\sysvol\\linuxmuster.lan\\Policies\\policy1" + }) + mockSharesGetMountpointOfRemotePath.return_value = (True, f"{os.path.dirname(os.path.realpath(__file__))}/files/policy-invalid2") + mockSharesmMountShare.return_value = (True, "") + mockPrintersInstallPrinter.return_value = True + mockUserIsInGroup.return_value = True + mockComputerIsInGroup.return_value = True + + assert gpo.processAllPolicies() + + # Check shares + assert mockUserIsInGroup.call_args_list[0].args[0] == "teachers" + assert len(mockSharesmMountShare.call_args_list) == 2 + # Programs (K:) is invalid assert mockSharesmMountShare.call_args_list[0] == mock.call('\\\\server\\default-school\\program', shareName='Programs (K:)') + # Projects (P:) is disabled and should not be mounted + assert mockSharesmMountShare.call_args_list[0] == mock.call('\\\\server\\default-school\\students', shareName='Students-Home (S:)') + assert mockSharesmMountShare.call_args_list[1] == mock.call('\\\\server\\default-school\\share', shareName='Shares') + + # Check printers + assert mockUserIsInGroup.call_args_list[1].args[0] == "printer3" + assert len(mockPrintersInstallPrinter.call_args_list) == 1 + assert mockPrintersInstallPrinter.call_args_list[0] == mock.call('ipp://SERVER/printers/PRINTER3', 'PRINTER3') + +@mock.patch("linuxmusterLinuxclient7.gpo.computer.isInGroup") +@mock.patch("linuxmusterLinuxclient7.gpo.user.isInGroup") +@mock.patch("linuxmusterLinuxclient7.gpo.printers.installPrinter") +@mock.patch("linuxmusterLinuxclient7.gpo.shares.mountShare") +@mock.patch("linuxmusterLinuxclient7.gpo.shares.getMountpointOfRemotePath") +@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne") +@mock.patch("linuxmusterLinuxclient7.gpo.user.school") +def test_sharesMountShareException(mockUserSchool, mockLdapHelperSearchOne, mockSharesGetMountpointOfRemotePath, mockSharesmMountShare, mockPrintersInstallPrinter, mockUserIsInGroup, mockComputerIsInGroup): + # Programs (K:) is missing the label + # PRINTER1 is missing the name + # PRINTER2 is missing the path + mockUserSchool.return_value = (True, "school1") + mockLdapHelperSearchOne.return_value = (True, { + "distinguishedName": "policy1", + "gPCFileSysPath": "\\\\linuxmuster.lan\\sysvol\\linuxmuster.lan\\Policies\\policy1" + }) + mockSharesGetMountpointOfRemotePath.return_value = (True, f"{os.path.dirname(os.path.realpath(__file__))}/files/policy-invalid2") + mockSharesmMountShare.side_effect = Exception() + + mockPrintersInstallPrinter.return_value = True + mockUserIsInGroup.return_value = True + mockComputerIsInGroup.return_value = True + + assert not gpo.processAllPolicies() \ No newline at end of file From 423b730de2941acfd760427b50ee63aa6d9877ab Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Wed, 17 Aug 2022 08:04:04 +0200 Subject: [PATCH 33/57] Tests: Add new tests --- .gitignore | 3 +- Makefile | 5 +- .../linuxmusterLinuxclient7/hooks.py | 2 +- .../tests/test_hooks.py | 66 +++++++++++++++++++ 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py diff --git a/.gitignore b/.gitignore index c06bdb2..c705f86 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ __pycache__ /public venv .coverage -coverage.xml \ No newline at end of file +coverage.xml +.vscode/*.log \ No newline at end of file diff --git a/Makefile b/Makefile index b6ee235..6da5c0b 100644 --- a/Makefile +++ b/Makefile @@ -13,4 +13,7 @@ docs: cd docs && sphinx-build . ../public clean: - rm -r public \ No newline at end of file + rm -r public + +tests: + pytest -v --cov=./ --cov-report=xml \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/hooks.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/hooks.py index 74e5030..1eae3f1 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/hooks.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/hooks.py @@ -50,8 +50,8 @@ def runLocalHook(hookType): :param hookType: The type of hook to run :type hookType: hooks.Type """ - logging.info("=== Running local hook on{0} ===".format(hookType.name)) hookDir = _getLocalHookDir(hookType) + logging.info("=== Running local hook on{0} in {1} ===".format(hookType.name, hookDir)) if os.path.exists(hookDir): _prepareEnvironment() for fileName in os.listdir(hookDir): diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py new file mode 100644 index 0000000..c9fa3fa --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py @@ -0,0 +1,66 @@ +from unittest import mock +from .. import hooks, fileHelper +import pytest, os + +@mock.patch("subprocess.call") +@mock.patch("linuxmusterLinuxclient7.hooks.constants.etcBaseDir", "/tmp/hooks") +@pytest.mark.parametrize("hook", [hooks.Type.Boot, hooks.Type.Shutdown, hooks.Type.LoginAsRoot, hooks.Type.Login, hooks.Type.SessionStarted, hooks.Type.LogoutAsRoot, hooks.Type.LoginLogoutAsRoot]) +def test_runHook(mockSubprocessCall,hook): + try: + os.mkdir("/tmp/hooks") + except FileExistsError: + pass + + try: + os.mkdir(f"/tmp/hooks/on{hook.name}.d") + except FileExistsError: + pass + + for i in range(2): + with open(f"/tmp/hooks/on{hook.name}.d/script-{i}", "w") as file: + file.write("#!/bin/bash\necho \"test-{i}\"") + os.chmod(f"/tmp/hooks/on{hook.name}.d/script-{i}", 0o777) + + hooks.runHook(hook) + + calls = [] + for i in range(len(mockSubprocessCall.call_args_list)): + firstArg = mockSubprocessCall.call_args_list[i].args[0][0] + if not firstArg in calls: + calls.append(firstArg) + + for i in range(2): + assert f"/tmp/hooks/on{hook.name}.d/script-{i}" in calls + + fileHelper.deleteDirectory("/tmp/hooks") + +@pytest.mark.parametrize("hook", [hooks.Type.Boot, hooks.Type.Shutdown, hooks.Type.LoginAsRoot, hooks.Type.Login, hooks.Type.SessionStarted, hooks.Type.LogoutAsRoot, hooks.Type.LoginLogoutAsRoot]) +def test_getLocalHookScript(hook): + assert hooks.getLocalHookScript(hook) == f"/usr/share/linuxmuster-linuxclient7/scripts/on{hook.name}" + +@mock.patch("linuxmusterLinuxclient7.hooks.user.isUserInAD") +@mock.patch("linuxmusterLinuxclient7.hooks.user.username") +@mock.patch("linuxmusterLinuxclient7.hooks.computer.isInAD") +@mock.patch("linuxmusterLinuxclient7.hooks.setup.isSetup") +def test_shouldHooksBeExecuted(mockSetupIsSetup, mockComputerIsInAD, mockUserUsername, mockUserIsUserInAD): + mockSetupIsSetup.return_value = True + mockComputerIsInAD.return_value = True + mockUserUsername.return_value = "user1" + mockUserIsUserInAD.return_value = True + + assert hooks.shouldHooksBeExecuted() + assert mockUserIsUserInAD.call_args.args[0] == "user1" + + assert hooks.shouldHooksBeExecuted("user2") + assert mockUserIsUserInAD.call_args.args[0] == "user2" + + mockSetupIsSetup.return_value = False + assert not hooks.shouldHooksBeExecuted() + + mockSetupIsSetup.return_value = True + mockComputerIsInAD.return_value = False + assert not hooks.shouldHooksBeExecuted() + + mockComputerIsInAD.return_value = True + mockUserIsUserInAD.return_value = False + assert not hooks.shouldHooksBeExecuted() \ No newline at end of file From 16db20b12760d67cd8285579aa72de60a38b2404 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Wed, 17 Aug 2022 09:10:48 +0200 Subject: [PATCH 34/57] Tests: test remote hook execution --- .vscode/extensions.json | 3 +- .../tests/test_hooks.py | 81 +++++++++++++++---- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 9fa560c..e5f62b0 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,7 @@ { "recommendations": [ "njpwerner.autodocstring", - "ms-python.python" + "ms-python.python", + "ryanluker.vscode-coverage-gutters" ] } \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py index c9fa3fa..c4c3d14 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py @@ -1,26 +1,27 @@ +from email.mime import base +from pathlib import Path from unittest import mock from .. import hooks, fileHelper import pytest, os @mock.patch("subprocess.call") +@mock.patch("linuxmusterLinuxclient7.hooks.config.network") +@mock.patch("linuxmusterLinuxclient7.hooks.user.readAttributes") +@mock.patch("linuxmusterLinuxclient7.hooks.computer.readAttributes") +@mock.patch("linuxmusterLinuxclient7.hooks.shares.getLocalSysvolPath") @mock.patch("linuxmusterLinuxclient7.hooks.constants.etcBaseDir", "/tmp/hooks") @pytest.mark.parametrize("hook", [hooks.Type.Boot, hooks.Type.Shutdown, hooks.Type.LoginAsRoot, hooks.Type.Login, hooks.Type.SessionStarted, hooks.Type.LogoutAsRoot, hooks.Type.LoginLogoutAsRoot]) -def test_runHook(mockSubprocessCall,hook): - try: - os.mkdir("/tmp/hooks") - except FileExistsError: - pass +def test_runHook(mockSharesGetLocalSysvolPath, mockComputerAttributes, mockUserAttributes, mockConfigNetwork, mockSubprocessCall, hook): - try: - os.mkdir(f"/tmp/hooks/on{hook.name}.d") - except FileExistsError: - pass + # mock ennvironment + mockConfigNetwork.return_value = (True, {"domain": "linuxmuster.lan"}) + mockUserAttributes.return_value = (True, {"sophomorixSchoolname": "default-school"}) + mockComputerAttributes.return_value = (True, {"sophomorixSchoolname": "default-school"}) + mockSharesGetLocalSysvolPath.return_value = (True, "/tmp/sysvol") + + hookScripts = _createLocalHookScripts(hook) + hookScripts = hookScripts + _createRemoteHookScripts(hook) - for i in range(2): - with open(f"/tmp/hooks/on{hook.name}.d/script-{i}", "w") as file: - file.write("#!/bin/bash\necho \"test-{i}\"") - os.chmod(f"/tmp/hooks/on{hook.name}.d/script-{i}", 0o777) - hooks.runHook(hook) calls = [] @@ -29,10 +30,11 @@ def test_runHook(mockSubprocessCall,hook): if not firstArg in calls: calls.append(firstArg) - for i in range(2): - assert f"/tmp/hooks/on{hook.name}.d/script-{i}" in calls + for script in hookScripts: + assert script in calls fileHelper.deleteDirectory("/tmp/hooks") + fileHelper.deleteDirectory("/tmp/sysvol") @pytest.mark.parametrize("hook", [hooks.Type.Boot, hooks.Type.Shutdown, hooks.Type.LoginAsRoot, hooks.Type.Login, hooks.Type.SessionStarted, hooks.Type.LogoutAsRoot, hooks.Type.LoginLogoutAsRoot]) def test_getLocalHookScript(hook): @@ -63,4 +65,49 @@ def test_shouldHooksBeExecuted(mockSetupIsSetup, mockComputerIsInAD, mockUserUse mockComputerIsInAD.return_value = True mockUserIsUserInAD.return_value = False - assert not hooks.shouldHooksBeExecuted() \ No newline at end of file + assert not hooks.shouldHooksBeExecuted() + + +# -------------------- +# - Helper functions - +# -------------------- + +def _createLocalHookScripts(hook, basedir="/tmp/hooks"): + try: + os.mkdir(basedir) + except FileExistsError: + pass + + try: + os.mkdir(f"{basedir}/on{hook.name}.d") + except FileExistsError: + pass + + createdFiles = [] + for i in range(2): + thisFile = f"{basedir}/on{hook.name}.d/script-{i}" + with open(thisFile, "w") as file: + file.write("#!/bin/bash\necho \"test-{i}\"") + os.chmod(thisFile, 0o777) + createdFiles.append(thisFile) + + return createdFiles + +def _createRemoteHookScripts(hook, sysvolPath="/tmp/sysvol", domain="linuxmuster.lan", school="default-school"): + if not hook in hooks.remoteScriptNames: + return [] + + hookScriptPathTemplate = "{0}/{1}/scripts/{2}/{3}/linux/{4}".format(sysvolPath, domain, school, "{}", hooks.remoteScriptNames[hook]) + + createdFiles = [] + for hookKind in ["lmn", "custom"]: + hookScriptPath = hookScriptPathTemplate.format(hookKind) + hookDir = os.path.dirname(hookScriptPath) + Path(hookDir).mkdir(parents=True, exist_ok=True) + + with open(hookScriptPath, "w") as file: + file.write("#!/bin/bash\necho \"test-{i}\"") + os.chmod(hookScriptPath, 0o777) + createdFiles.append(hookScriptPath) + + return createdFiles \ No newline at end of file From 486d603d9933461ad6d9e3b953b3fd8829cc8e8b Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Wed, 17 Aug 2022 12:59:32 +0200 Subject: [PATCH 35/57] Tests: testing of error handling in hooks --- .../tests/test_hooks.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py index c4c3d14..bb06bda 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py @@ -67,11 +67,52 @@ def test_shouldHooksBeExecuted(mockSetupIsSetup, mockComputerIsInAD, mockUserUse mockUserIsUserInAD.return_value = False assert not hooks.shouldHooksBeExecuted() +@mock.patch("subprocess.call") +@mock.patch("linuxmusterLinuxclient7.hooks.config.network") +@mock.patch("linuxmusterLinuxclient7.hooks.user.readAttributes") +@mock.patch("linuxmusterLinuxclient7.hooks.computer.readAttributes") +@mock.patch("linuxmusterLinuxclient7.hooks.shares.getLocalSysvolPath") +@mock.patch("linuxmusterLinuxclient7.hooks.constants.etcBaseDir", "/tmp/hooks") +def test_runHookError(mockSharesGetLocalSysvolPath, mockComputerAttributes, mockUserAttributes, mockConfigNetwork, mockSubprocessCall): + mockConfigNetwork.return_value = (False, None) + mockUserAttributes.return_value = (False, None) + mockComputerAttributes.return_value = (False, None) + mockSharesGetLocalSysvolPath.return_value = (False, None) + + # mock ennvironment + hooks.runRemoteHook(hooks.Type.Boot) + mockConfigNetwork.return_value = (True, {"domain": "linuxmuster.lan"}) + hooks.runRemoteHook(hooks.Type.Boot) + mockUserAttributes.return_value = (True, {"sophomorixSchoolname": "default-school"}) + hooks.runRemoteHook(hooks.Type.Boot) + mockComputerAttributes.return_value = (True, {"sophomorixSchoolname": "default-school"}) + hooks.runRemoteHook(hooks.Type.Boot) + mockSharesGetLocalSysvolPath.return_value = (True, "/tmp/sysvol") + + _createRemoteHookScripts(hooks.Type.Login) + + hooks.runHook(hooks.Type.Boot) + + calls = _getFirstArgugemtOfAllCalls(mockSubprocessCall) + + for call in calls: + assert not call.startswith("/tmp/sysvol") + + fileHelper.deleteDirectory("/tmp/sysvol") # -------------------- # - Helper functions - # -------------------- +def _getFirstArgugemtOfAllCalls(mockSubprocessCall): + calls = [] + for i in range(len(mockSubprocessCall.call_args_list)): + firstArg = mockSubprocessCall.call_args_list[i].args[0][0] + if not firstArg in calls: + calls.append(firstArg) + + return calls + def _createLocalHookScripts(hook, basedir="/tmp/hooks"): try: os.mkdir(basedir) From ace362b2bf64007bc7be37849b28cd772b0b5864 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 08:59:32 +0200 Subject: [PATCH 36/57] Tests: improove hook tests --- .../linuxmusterLinuxclient7/realm.py | 2 +- .../tests/test_hooks.py | 47 ++++++++++++++----- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/realm.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/realm.py index 3847529..e578f11 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/realm.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/realm.py @@ -189,7 +189,7 @@ def clearUserCache(): # - Helper functions - # -------------------- -def _readConfigFromString(string): +def _readConfigFromStyring(string): configParser = configparser.ConfigParser() configParser.read_string(string) return configParser \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py index bb06bda..feccc89 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py @@ -4,18 +4,17 @@ from .. import hooks, fileHelper import pytest, os -@mock.patch("subprocess.call") @mock.patch("linuxmusterLinuxclient7.hooks.config.network") @mock.patch("linuxmusterLinuxclient7.hooks.user.readAttributes") @mock.patch("linuxmusterLinuxclient7.hooks.computer.readAttributes") @mock.patch("linuxmusterLinuxclient7.hooks.shares.getLocalSysvolPath") @mock.patch("linuxmusterLinuxclient7.hooks.constants.etcBaseDir", "/tmp/hooks") @pytest.mark.parametrize("hook", [hooks.Type.Boot, hooks.Type.Shutdown, hooks.Type.LoginAsRoot, hooks.Type.Login, hooks.Type.SessionStarted, hooks.Type.LogoutAsRoot, hooks.Type.LoginLogoutAsRoot]) -def test_runHook(mockSharesGetLocalSysvolPath, mockComputerAttributes, mockUserAttributes, mockConfigNetwork, mockSubprocessCall, hook): +def test_runHook(mockSharesGetLocalSysvolPath, mockComputerAttributes, mockUserAttributes, mockConfigNetwork, hook): # mock ennvironment mockConfigNetwork.return_value = (True, {"domain": "linuxmuster.lan"}) - mockUserAttributes.return_value = (True, {"sophomorixSchoolname": "default-school"}) + mockUserAttributes.return_value = (True, {"sophomorixSchoolname": "default-school", "sophomorxCustomMulti1": ["test1", "test2"]}) mockComputerAttributes.return_value = (True, {"sophomorixSchoolname": "default-school"}) mockSharesGetLocalSysvolPath.return_value = (True, "/tmp/sysvol") @@ -24,14 +23,19 @@ def test_runHook(mockSharesGetLocalSysvolPath, mockComputerAttributes, mockUserA hooks.runHook(hook) - calls = [] - for i in range(len(mockSubprocessCall.call_args_list)): - firstArg = mockSubprocessCall.call_args_list[i].args[0][0] - if not firstArg in calls: - calls.append(firstArg) - for script in hookScripts: - assert script in calls + with open(f"{script}-env", "r") as file: + env = file.read() + + env = env.split("\n") + env = [line for line in env if line != ""] + + assert env.index("User_sophomorixSchoolname=default-school") != -1 + assert env.index("User_sophomorxCustomMulti1=test1") != -1 + assert env.index("test2") != -1 + assert env.index("User_sophomorxCustomMulti1=test1") == env.index("test2") -1 + assert env.index("Computer_sophomorixSchoolname=default-school") != -1 + assert env.index("Network_domain=linuxmuster.lan") != -1 fileHelper.deleteDirectory("/tmp/hooks") fileHelper.deleteDirectory("/tmp/sysvol") @@ -83,16 +87,33 @@ def test_runHookError(mockSharesGetLocalSysvolPath, mockComputerAttributes, mock hooks.runRemoteHook(hooks.Type.Boot) mockConfigNetwork.return_value = (True, {"domain": "linuxmuster.lan"}) hooks.runRemoteHook(hooks.Type.Boot) + hooks.runRemoteHook(hooks.Type.Login) + + mockUserAttributes.return_value = (True, {}) + hooks.runRemoteHook(hooks.Type.Login) mockUserAttributes.return_value = (True, {"sophomorixSchoolname": "default-school"}) + hooks.runRemoteHook(hooks.Type.Login) + + mockComputerAttributes.return_value = (True, {}) hooks.runRemoteHook(hooks.Type.Boot) mockComputerAttributes.return_value = (True, {"sophomorixSchoolname": "default-school"}) hooks.runRemoteHook(hooks.Type.Boot) mockSharesGetLocalSysvolPath.return_value = (True, "/tmp/sysvol") - _createRemoteHookScripts(hooks.Type.Login) + createdScripts = _createRemoteHookScripts(hooks.Type.Login) + # Scripts don't exist hooks.runHook(hooks.Type.Boot) + calls = _getFirstArgugemtOfAllCalls(mockSubprocessCall) + + for call in calls: + assert not call.startswith("/tmp/sysvol") + + # Scripts are not executable + for script in createdScripts: + os.chmod(script, 0o666) + hooks.runHook(hooks.Type.Login) calls = _getFirstArgugemtOfAllCalls(mockSubprocessCall) for call in calls: @@ -128,7 +149,7 @@ def _createLocalHookScripts(hook, basedir="/tmp/hooks"): for i in range(2): thisFile = f"{basedir}/on{hook.name}.d/script-{i}" with open(thisFile, "w") as file: - file.write("#!/bin/bash\necho \"test-{i}\"") + file.write(f"#!/bin/bash\necho \"test-{i}\"\necho \"$(env)\" > {thisFile}-env") os.chmod(thisFile, 0o777) createdFiles.append(thisFile) @@ -147,7 +168,7 @@ def _createRemoteHookScripts(hook, sysvolPath="/tmp/sysvol", domain="linuxmuster Path(hookDir).mkdir(parents=True, exist_ok=True) with open(hookScriptPath, "w") as file: - file.write("#!/bin/bash\necho \"test-{i}\"") + file.write(f"#!/bin/bash\necho \"test-{hookKind}\"\necho \"$(env)\" > {hookScriptPath}-env") os.chmod(hookScriptPath, 0o777) createdFiles.append(hookScriptPath) From 78ab45c62505c9d3235a70e22466ad32ad7ef3e6 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 09:37:29 +0200 Subject: [PATCH 37/57] Tests: Enshure execution order in hooks --- .../linuxmusterLinuxclient7/tests/test_hooks.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py index feccc89..5abbaa0 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py @@ -1,4 +1,3 @@ -from email.mime import base from pathlib import Path from unittest import mock from .. import hooks, fileHelper @@ -23,6 +22,7 @@ def test_runHook(mockSharesGetLocalSysvolPath, mockComputerAttributes, mockUserA hooks.runHook(hook) + lastExecutionTimestamp = 0 for script in hookScripts: with open(f"{script}-env", "r") as file: env = file.read() @@ -37,6 +37,13 @@ def test_runHook(mockSharesGetLocalSysvolPath, mockComputerAttributes, mockUserA assert env.index("Computer_sophomorixSchoolname=default-school") != -1 assert env.index("Network_domain=linuxmuster.lan") != -1 + if script.startswith("/tmp/hooks"): + # enshure execution order + with open(f"{script}-date", "r") as file: + date = int(file.read()) + assert date > lastExecutionTimestamp + lastExecutionTimestamp = date + fileHelper.deleteDirectory("/tmp/hooks") fileHelper.deleteDirectory("/tmp/sysvol") @@ -149,7 +156,7 @@ def _createLocalHookScripts(hook, basedir="/tmp/hooks"): for i in range(2): thisFile = f"{basedir}/on{hook.name}.d/script-{i}" with open(thisFile, "w") as file: - file.write(f"#!/bin/bash\necho \"test-{i}\"\necho \"$(env)\" > {thisFile}-env") + file.write(f"#!/bin/bash\necho \"test-{i}\"\necho \"$(env)\" > {thisFile}-env\necho \"$(date +%s%N)\" > {thisFile}-date") os.chmod(thisFile, 0o777) createdFiles.append(thisFile) From a56e5043d19abea80ab03a1fbcada8c803e692aa Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 09:40:46 +0200 Subject: [PATCH 38/57] Tests: test with more files --- .../dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py index 5abbaa0..4aadb3e 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py @@ -153,7 +153,7 @@ def _createLocalHookScripts(hook, basedir="/tmp/hooks"): pass createdFiles = [] - for i in range(2): + for i in range(10): thisFile = f"{basedir}/on{hook.name}.d/script-{i}" with open(thisFile, "w") as file: file.write(f"#!/bin/bash\necho \"test-{i}\"\necho \"$(env)\" > {thisFile}-env\necho \"$(date +%s%N)\" > {thisFile}-date") From a579be52258fb028d604a4f89ae53b641df68732 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 09:45:17 +0200 Subject: [PATCH 39/57] Tests: Add basic test for template --- .../tests/files/templates/01.conf | 20 +++++++ .../tests/files/templates/02 | 3 + .../tests/files/templates/03.xml | 7 +++ .../tests/test_templates.py | 56 +++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/01.conf create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/02 create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/03.xml create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/01.conf b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/01.conf new file mode 100644 index 0000000..8b361a1 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/01.conf @@ -0,0 +1,20 @@ +# /tmp/templates/01-applied.conf + +# serverHostname +serverHostname="@@serverHostname@@" +# domain +domain="@@domain@@" +# realm +realm="@@realm@@" +# userTemplateDir +userTemplateDir="@@userTemplateDir@@" +# hiddenShareMountBasepath +hiddenShareMountBasepath="@@hiddenShareMountBasepath@@" +# hookScriptBoot +hookScriptBoot="@@hookScriptBoot@@" +# hookScriptShutdown +hookScriptShutdown="@@hookScriptShutdown@@" +# hookScriptLoginLogoutAsRoot +hookScriptLoginLogoutAsRoot="@@hookScriptLoginLogoutAsRoot@@" +# hookScriptSessionStarted +hookScriptSessionStarted="@@hookScriptSessionStarted@@" diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/02 b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/02 new file mode 100644 index 0000000..f47537f --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/02 @@ -0,0 +1,3 @@ +# /tmp/templates/02-applied + +@@userTemplateDir@@@@@hookScriptBoot@@@@hookScriptSessionStarted@@@@@@ @@@ diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/03.xml b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/03.xml new file mode 100644 index 0000000..a25d17f --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/03.xml @@ -0,0 +1,7 @@ +# /tmp/templates/03-applied.xml +# this comment should be removed + + @@serverHostname@@ + @@userTemplateDir@@ + @@hookScriptShutdown@@ + diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py new file mode 100644 index 0000000..4f64258 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py @@ -0,0 +1,56 @@ +from pathlib import Path +from unittest import mock +from .. import templates, fileHelper +import pytest, os + +@mock.patch("subprocess.call") +@mock.patch("linuxmusterLinuxclient7.hooks.config.network") +@mock.patch("linuxmusterLinuxclient7.templates.constants.configFileTemplateDir", f"{os.path.dirname(os.path.realpath(__file__))}/files/templates") +def test_applyAll(mockConfigNetwork, mockSubprocessCall): + mockConfigNetwork.return_value = (True, {"serverHostname": "linuxmuster.lan", "domain": "linuxmuster.lan", "realm": "LINUXMUSTER"}) + mockSubprocessCall.return_value = 0 + + templates.applyAll() + + assert sorted(os.listdir("/tmp/templates")) == ["01-applied.conf", "02-applied", "03-applied.xml"] + + # open files and check content + with open("/tmp/templates/01-applied.conf", "r") as f: + content = f.read() + assert content == """ + +# serverHostname +serverHostname="linuxmuster.lan" +# domain +domain="linuxmuster.lan" +# realm +realm="LINUXMUSTER" +# userTemplateDir +userTemplateDir="/home/linuxadmin" +# hiddenShareMountBasepath +hiddenShareMountBasepath="/srv/samba/%(USER)" +# hookScriptBoot +hookScriptBoot="/usr/share/linuxmuster-linuxclient7/scripts/onBoot" +# hookScriptShutdown +hookScriptShutdown="/usr/share/linuxmuster-linuxclient7/scripts/onShutdown" +# hookScriptLoginLogoutAsRoot +hookScriptLoginLogoutAsRoot="/usr/share/linuxmuster-linuxclient7/scripts/onLoginLogoutAsRoot" +# hookScriptSessionStarted +hookScriptSessionStarted="/usr/share/linuxmuster-linuxclient7/scripts/onSessionStarted" +""" + + with open("/tmp/templates/02-applied", "r") as f: + content = f.read() + assert content == """ + +/home/linuxadmin@/usr/share/linuxmuster-linuxclient7/scripts/onBoot/usr/share/linuxmuster-linuxclient7/scripts/onSessionStarted@@@@ @@@ +""" + + with open("/tmp/templates/03-applied.xml", "r") as f: + content = f.read() + assert content == """ + linuxmuster.lan + /home/linuxadmin + /usr/share/linuxmuster-linuxclient7/scripts/onShutdown + +""" \ No newline at end of file From 8494d4d50bb2fa3ddd6483b800fb5c51723e93df Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 10:01:39 +0200 Subject: [PATCH 40/57] Tests: more tests for templates --- .../linuxmusterLinuxclient7/templates.py | 22 ++----------- .../tests/files/templates/04.forbidden | 2 ++ .../tests/test_templates.py | 32 +++++++++++++++++-- 3 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/04.forbidden diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/templates.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/templates.py index b634fd2..971c80f 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/templates.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/templates.py @@ -35,12 +35,8 @@ def applyAll(): def _apply(templatePath): try: - # read template file - rc, fileData = _readTextfile(templatePath) - - if not rc: - logging.error('Failed!') - return False + with open(templatePath, "r") as f: + fileData = f.read() fileData = _resolveVariables(fileData) @@ -100,20 +96,6 @@ def _resolveVariables(fileData): return fileData -# read textfile in variable -def _readTextfile(filePath): - if not os.path.isfile(filePath): - return False, None - try: - infile = codecs.open(filePath ,'r', encoding='utf-8', errors='ignore') - content = infile.read() - infile.close() - return True, content - except Exception as e: - logging.info('Cannot read ' + filePath + '!') - logging.exception(e) - return False, None - # remove lines beginning with # def _stripComment(fileData): filedata_stripped = '' diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/04.forbidden b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/04.forbidden new file mode 100644 index 0000000..32180f8 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/templates/04.forbidden @@ -0,0 +1,2 @@ +# /etc/sssd/sssd.conf +This file is not allowed to be templated! \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py index 4f64258..3b13bd9 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py @@ -10,7 +10,7 @@ def test_applyAll(mockConfigNetwork, mockSubprocessCall): mockConfigNetwork.return_value = (True, {"serverHostname": "linuxmuster.lan", "domain": "linuxmuster.lan", "realm": "LINUXMUSTER"}) mockSubprocessCall.return_value = 0 - templates.applyAll() + assert templates.applyAll() assert sorted(os.listdir("/tmp/templates")) == ["01-applied.conf", "02-applied", "03-applied.xml"] @@ -53,4 +53,32 @@ def test_applyAll(mockConfigNetwork, mockSubprocessCall): /home/linuxadmin /usr/share/linuxmuster-linuxclient7/scripts/onShutdown -""" \ No newline at end of file +""" + systemctlFound = False + for call_args in mockSubprocessCall.call_args_list: + if call_args[0][0] == ["systemctl", "daemon-reload"]: + systemctlFound = True + break + + assert systemctlFound + + fileHelper.deleteDirectory("/tmp/templates") + +@mock.patch("subprocess.call") +@mock.patch("linuxmusterLinuxclient7.hooks.config.network") +@mock.patch("linuxmusterLinuxclient7.templates.constants.configFileTemplateDir", f"{os.path.dirname(os.path.realpath(__file__))}/files/templates") +def test_applyAllError(mockConfigNetwork, mockSubprocessCall): + mockConfigNetwork.return_value = (False, None) + mockSubprocessCall.return_value = 0 + assert not templates.applyAll() + + + mockConfigNetwork.return_value = (True, {"serverHostname": "linuxmuster.lan", "domain": "linuxmuster.lan", "realm": "LINUXMUSTER"}) + mockSubprocessCall.return_value = 1 + assert not templates.applyAll() + + mockConfigNetwork.return_value = (True, {"serverHostname": "linuxmuster.lan", "domain": "linuxmuster.lan"}) + mockSubprocessCall.return_value = 0 + assert not templates.applyAll() + + fileHelper.deleteDirectory("/tmp/templates") \ No newline at end of file From 26c6a40e36bd75f9ccd1b3c47789ea49c9d83ff6 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 10:25:23 +0200 Subject: [PATCH 41/57] Chore: remove unused imports --- .../linuxmusterLinuxclient7/tests/test_templates.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py index 3b13bd9..4f240fd 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py @@ -1,7 +1,6 @@ -from pathlib import Path from unittest import mock from .. import templates, fileHelper -import pytest, os +import os @mock.patch("subprocess.call") @mock.patch("linuxmusterLinuxclient7.hooks.config.network") From 2192579bac5eb8352ec9cf55f160a72509fb75dc Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 10:25:52 +0200 Subject: [PATCH 42/57] Tests: add test for primters.translateSambaToIpp --- .../dist-packages/linuxmusterLinuxclient7/printers.py | 4 ++-- .../linuxmusterLinuxclient7/tests/test_printers.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py index d4e42a1..adc2741 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py @@ -49,7 +49,7 @@ def uninstallAllPrintersOfUser(username): def translateSambaToIpp(networkPath): """ - Translates a samba url, like `\\server\PRINTER-01`, to an ipp url like `ipp://server/printers/PRINTER-01`. + Translates a samba url, like `\\\\server\\PRINTER-01`, to an ipp url like `ipp://server/printers/PRINTER-01`. :param networkPath: The samba url :type networkPath: str @@ -58,7 +58,7 @@ def translateSambaToIpp(networkPath): """ networkPath = networkPath.replace("\\", "/") # path has to be translated: \\server\EW-FARBLASER -> ipp://server/printers/EW-farblaser - pattern = re.compile("\\/\\/([^/]+)\\/(.*)") + pattern = re.compile("\\/\\/([^/]+)\\/(.+)") result = pattern.findall(networkPath) if len(result) != 1 or len(result[0]) != 2: diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py new file mode 100644 index 0000000..6a10873 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py @@ -0,0 +1,10 @@ +from pathlib import Path +from unittest import mock +from .. import printers +import pytest, os + +def test_translateSambaToIpp(): + assert printers.translateSambaToIpp("\\\\linuxmuster.lan\\printer1") == (True, "ipp://linuxmuster.lan/printers/printer1") + assert printers.translateSambaToIpp("\\\\linuxmuster.lan\\") == (False, None) + assert printers.translateSambaToIpp("\\\\linuxmuster.lan") == (False, None) + assert printers.translateSambaToIpp("\\\\\\printer1") == (False, None) \ No newline at end of file From b8ccbd7f8d3281a97700ce646f37ead8084b155c Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 11:06:56 +0200 Subject: [PATCH 43/57] Tests: Add test for printers.installPrinter --- .../linuxmusterLinuxclient7/printers.py | 7 ++-- .../tests/test_printers.py | 40 ++++++++++++++++++- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py index adc2741..5ac9f62 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py @@ -5,7 +5,7 @@ def installPrinter(networkPath, name=None, username=None): """ Installs a networked printer for a user - :param networkPath: The network path of the printer + :param networkPath: The network path of the printer in format `ipp://server/printers/PRINTER-01` :type networkPath: str :param name: The name for the printer, defaults to None :type name: str, optional @@ -17,14 +17,15 @@ def installPrinter(networkPath, name=None, username=None): if username == None: username = user.username() + if name == None: + name = networkPath.split("/")[-1] + if user.isRoot(): return _installPrinter(username, networkPath, name) else: # This will call installPrinter() again with root privileges return _installPrinterWithoutRoot(networkPath, name) - pass - def uninstallAllPrintersOfUser(username): """ Uninstalls all printers of a given user diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py index 6a10873..897caab 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py @@ -7,4 +7,42 @@ def test_translateSambaToIpp(): assert printers.translateSambaToIpp("\\\\linuxmuster.lan\\printer1") == (True, "ipp://linuxmuster.lan/printers/printer1") assert printers.translateSambaToIpp("\\\\linuxmuster.lan\\") == (False, None) assert printers.translateSambaToIpp("\\\\linuxmuster.lan") == (False, None) - assert printers.translateSambaToIpp("\\\\\\printer1") == (False, None) \ No newline at end of file + assert printers.translateSambaToIpp("\\\\\\printer1") == (False, None) + + +@mock.patch("subprocess.call") +@mock.patch("linuxmusterLinuxclient7.hooks.user.isRoot") +@mock.patch("linuxmusterLinuxclient7.hooks.user.username") +def test_installPrinter(mockUserUsername, mockUserIsRoot, mockSubprocessCall): + mockSubprocessCall.return_value = 0 + mockUserIsRoot.return_value = True + mockUserUsername.return_value = "user1" + + printers.installPrinter("ipp://linuxmuster.lan/printers/printer1") + assert _getCallsTo(mockSubprocessCall, "timeout")[-1] == ["timeout", "10", "lpadmin", "-p", "printer1", "-E", "-v", "ipp://linuxmuster.lan/printers/printer1", "-m", "everywhere", "-u", "allow:user1"] + + printers.installPrinter("ipp://linuxmuster.lan/printers/printer1", "printer2") + assert _getCallsTo(mockSubprocessCall, "timeout")[-1] == ["timeout", "10", "lpadmin", "-p", "printer2", "-E", "-v", "ipp://linuxmuster.lan/printers/printer1", "-m", "everywhere", "-u", "allow:user1"] + + printers.installPrinter("ipp://linuxmuster.lan/printers/printer1", "printer2", "user2") + assert _getCallsTo(mockSubprocessCall, "timeout")[-1] == ["timeout", "10", "lpadmin", "-p", "printer2", "-E", "-v", "ipp://linuxmuster.lan/printers/printer1", "-m", "everywhere", "-u", "allow:user2"] + + mockUserIsRoot.return_value = False + printers.installPrinter("ipp://linuxmuster.lan/printers/printer1") + assert _getCallsTo(mockSubprocessCall, "sudo")[-1] == ['sudo', '/usr/share/linuxmuster-linuxclient7/scripts/sudoTools', 'install-printer', '--path', 'ipp://linuxmuster.lan/printers/printer1', '--name', 'printer1'] + + printers.installPrinter("ipp://linuxmuster.lan/printers/printer1", "printer2") + assert _getCallsTo(mockSubprocessCall, "sudo")[-1] == ['sudo', '/usr/share/linuxmuster-linuxclient7/scripts/sudoTools', 'install-printer', '--path', 'ipp://linuxmuster.lan/printers/printer1', '--name', 'printer2'] + +# -------------------- +# - Helper functions - +# -------------------- + +def _getCallsTo(mockSubprocessCall, program): + calls = [] + for call_args in mockSubprocessCall.call_args_list: + args = call_args.args[0] + print(args) + if args[0] == program: + calls.append(args) + return calls \ No newline at end of file From b322caa2e3986af1633bb717183475e8d3c6bdfa Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 11:26:16 +0200 Subject: [PATCH 44/57] Tests: add tests for printers.uninstallAllPrintersOfUser --- .../linuxmusterLinuxclient7/printers.py | 10 +++----- .../tests/test_printers.py | 25 ++++++++++++++----- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py index 5ac9f62..b1f3c6b 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py @@ -36,11 +36,7 @@ def uninstallAllPrintersOfUser(username): :rtype: bool """ logging.info("Uninstalling all printers of {}".format(username)) - rc, installedPrinters = _getInstalledPrintersOfUser(username) - - if not rc: - logging.error("Error getting printers!") - return False + installedPrinters = _getInstalledPrintersOfUser(username) for installedPrinter in installedPrinters: if not _uninstallPrinter(installedPrinter): @@ -99,7 +95,7 @@ def _getInstalledPrintersOfUser(username): if not result.returncode == 0: logging.info("No Printers installed.") - return True, [] + return [] rawInstalledPrinters = list(filter(None, result.stdout.split("\n"))) installedPrinters = [] @@ -113,7 +109,7 @@ def _getInstalledPrintersOfUser(username): installedPrinter = rawInstalledPrinterList[1] installedPrinters.append(installedPrinter) - return True, installedPrinters + return installedPrinters def _uninstallPrinter(name): logging.info("Uninstall Printer {}".format(name)) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py index 897caab..ebf404b 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py @@ -1,4 +1,5 @@ from pathlib import Path +from subprocess import CompletedProcess from unittest import mock from .. import printers import pytest, os @@ -18,22 +19,34 @@ def test_installPrinter(mockUserUsername, mockUserIsRoot, mockSubprocessCall): mockUserIsRoot.return_value = True mockUserUsername.return_value = "user1" - printers.installPrinter("ipp://linuxmuster.lan/printers/printer1") + assert printers.installPrinter("ipp://linuxmuster.lan/printers/printer1") assert _getCallsTo(mockSubprocessCall, "timeout")[-1] == ["timeout", "10", "lpadmin", "-p", "printer1", "-E", "-v", "ipp://linuxmuster.lan/printers/printer1", "-m", "everywhere", "-u", "allow:user1"] - printers.installPrinter("ipp://linuxmuster.lan/printers/printer1", "printer2") + assert printers.installPrinter("ipp://linuxmuster.lan/printers/printer1", "printer2") assert _getCallsTo(mockSubprocessCall, "timeout")[-1] == ["timeout", "10", "lpadmin", "-p", "printer2", "-E", "-v", "ipp://linuxmuster.lan/printers/printer1", "-m", "everywhere", "-u", "allow:user1"] - printers.installPrinter("ipp://linuxmuster.lan/printers/printer1", "printer2", "user2") + assert printers.installPrinter("ipp://linuxmuster.lan/printers/printer1", "printer2", "user2") assert _getCallsTo(mockSubprocessCall, "timeout")[-1] == ["timeout", "10", "lpadmin", "-p", "printer2", "-E", "-v", "ipp://linuxmuster.lan/printers/printer1", "-m", "everywhere", "-u", "allow:user2"] mockUserIsRoot.return_value = False - printers.installPrinter("ipp://linuxmuster.lan/printers/printer1") + assert printers.installPrinter("ipp://linuxmuster.lan/printers/printer1") assert _getCallsTo(mockSubprocessCall, "sudo")[-1] == ['sudo', '/usr/share/linuxmuster-linuxclient7/scripts/sudoTools', 'install-printer', '--path', 'ipp://linuxmuster.lan/printers/printer1', '--name', 'printer1'] - printers.installPrinter("ipp://linuxmuster.lan/printers/printer1", "printer2") + assert printers.installPrinter("ipp://linuxmuster.lan/printers/printer1", "printer2") assert _getCallsTo(mockSubprocessCall, "sudo")[-1] == ['sudo', '/usr/share/linuxmuster-linuxclient7/scripts/sudoTools', 'install-printer', '--path', 'ipp://linuxmuster.lan/printers/printer1', '--name', 'printer2'] +@mock.patch("subprocess.call") +@mock.patch("subprocess.run") +def test_uninstallAllPrintersOfUser(mockSubprocessRun, mockSubprocessCall): + mockSubprocessCall.return_value = 0 + lpstatStdout = """printer printer1 is idle. enabled since Sat 02 Jul 2022 06:07:39 PM CEST +printer printer2 is idle. enabled since Sat 09 Jul 2022 08:04:43 PM CEST""" + mockSubprocessRun.return_value = CompletedProcess(args=["lpstat", "-U", "user1", "-p"], returncode=0, stdout=lpstatStdout) + + assert printers.uninstallAllPrintersOfUser("user1") + assert _getCallsTo(mockSubprocessRun, "lpstat")[-1] == "lpstat -U user1 -p" + assert _getCallsTo(mockSubprocessCall, "timeout") == [["timeout", "10", "lpadmin", "-x", "printer1"], ["timeout", "10", "lpadmin", "-x", "printer2"]] + # -------------------- # - Helper functions - # -------------------- @@ -43,6 +56,6 @@ def _getCallsTo(mockSubprocessCall, program): for call_args in mockSubprocessCall.call_args_list: args = call_args.args[0] print(args) - if args[0] == program: + if (type(args) == list and args[0] == program) or (type(args) == str and args.startswith(f"{program} ")): calls.append(args) return calls \ No newline at end of file From 3c669ff1e43f17195c50bf93a7dadba33ddbef06 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 11:33:55 +0200 Subject: [PATCH 45/57] Tests: improove tests for printers.uninstallAllPrintersOfUser --- .../tests/test_printers.py | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py index ebf404b..e7c30cc 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py @@ -35,18 +35,49 @@ def test_installPrinter(mockUserUsername, mockUserIsRoot, mockSubprocessCall): assert printers.installPrinter("ipp://linuxmuster.lan/printers/printer1", "printer2") assert _getCallsTo(mockSubprocessCall, "sudo")[-1] == ['sudo', '/usr/share/linuxmuster-linuxclient7/scripts/sudoTools', 'install-printer', '--path', 'ipp://linuxmuster.lan/printers/printer1', '--name', 'printer2'] +@mock.patch("subprocess.call") +@mock.patch("linuxmusterLinuxclient7.hooks.user.isRoot") +@mock.patch("linuxmusterLinuxclient7.hooks.user.username") +def test_installPrinterError(mockUserUsername, mockUserIsRoot, mockSubprocessCall): + mockSubprocessCall.return_value = 124 + mockUserIsRoot.return_value = True + mockUserUsername.return_value = "user1" + + assert not printers.installPrinter("ipp://linuxmuster.lan/printers/printer1") + + mockSubprocessCall.return_value = 1 + assert not printers.installPrinter("ipp://linuxmuster.lan/printers/printer1") + + @mock.patch("subprocess.call") @mock.patch("subprocess.run") def test_uninstallAllPrintersOfUser(mockSubprocessRun, mockSubprocessCall): mockSubprocessCall.return_value = 0 lpstatStdout = """printer printer1 is idle. enabled since Sat 02 Jul 2022 06:07:39 PM CEST -printer printer2 is idle. enabled since Sat 09 Jul 2022 08:04:43 PM CEST""" +printer printer2 is idle. enabled since Sat 09 Jul 2022 08:04:43 PM CEST +invalid""" mockSubprocessRun.return_value = CompletedProcess(args=["lpstat", "-U", "user1", "-p"], returncode=0, stdout=lpstatStdout) assert printers.uninstallAllPrintersOfUser("user1") assert _getCallsTo(mockSubprocessRun, "lpstat")[-1] == "lpstat -U user1 -p" assert _getCallsTo(mockSubprocessCall, "timeout") == [["timeout", "10", "lpadmin", "-x", "printer1"], ["timeout", "10", "lpadmin", "-x", "printer2"]] + mockSubprocessRun.return_value = CompletedProcess(args=["lpstat", "-U", "user1", "-p"], returncode=1) + assert printers.uninstallAllPrintersOfUser("user1") + +@mock.patch("subprocess.call") +@mock.patch("subprocess.run") +def test_uninstallAllPrintersOfUser(mockSubprocessRun, mockSubprocessCall): + lpstatStdout = """printer printer1 is idle. enabled since Sat 02 Jul 2022 06:07:39 PM CEST +printer printer2 is idle. enabled since Sat 09 Jul 2022 08:04:43 PM CEST +invalid""" + mockSubprocessRun.return_value = CompletedProcess(args=["lpstat", "-U", "user1", "-p"], returncode=0, stdout=lpstatStdout) + mockSubprocessCall.return_value = 1 + assert not printers.uninstallAllPrintersOfUser("user1") + + mockSubprocessRun.return_value = CompletedProcess(args=["lpstat", "-U", "user1", "-p"], returncode=0, stdout=lpstatStdout) + mockSubprocessCall.return_value = 124 + assert not printers.uninstallAllPrintersOfUser("user1") # -------------------- # - Helper functions - # -------------------- From 04b4c22e6369fd5fd82de31a7fed0f6cce3bfbd2 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 11:38:05 +0200 Subject: [PATCH 46/57] Fix: rename test --- .../linuxmusterLinuxclient7/tests/test_printers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py index e7c30cc..eae78ff 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py @@ -67,7 +67,7 @@ def test_uninstallAllPrintersOfUser(mockSubprocessRun, mockSubprocessCall): @mock.patch("subprocess.call") @mock.patch("subprocess.run") -def test_uninstallAllPrintersOfUser(mockSubprocessRun, mockSubprocessCall): +def test_uninstallAllPrintersOfUserError(mockSubprocessRun, mockSubprocessCall): lpstatStdout = """printer printer1 is idle. enabled since Sat 02 Jul 2022 06:07:39 PM CEST printer printer2 is idle. enabled since Sat 09 Jul 2022 08:04:43 PM CEST invalid""" From 3dbdf531a6180fdc3ad4505226c91b5e67472588 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 11:42:02 +0200 Subject: [PATCH 47/57] Chore: remove unused imports --- .../linuxmusterLinuxclient7/tests/test_printers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py index eae78ff..6108905 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py @@ -1,8 +1,6 @@ -from pathlib import Path from subprocess import CompletedProcess from unittest import mock from .. import printers -import pytest, os def test_translateSambaToIpp(): assert printers.translateSambaToIpp("\\\\linuxmuster.lan\\printer1") == (True, "ipp://linuxmuster.lan/printers/printer1") From bd58f4c4263977b6052461cc550766175d2c6ad2 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 12:25:41 +0200 Subject: [PATCH 48/57] Tests: add tests for logging --- Makefile | 2 +- .../linuxmusterLinuxclient7/logging.py | 9 ++-- .../tests/files/logging/syslog | 53 +++++++++++++++++++ .../tests/test_logging.py | 52 ++++++++++++++++++ 4 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/logging/syslog create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_logging.py diff --git a/Makefile b/Makefile index 6da5c0b..658b1a2 100644 --- a/Makefile +++ b/Makefile @@ -16,4 +16,4 @@ clean: rm -r public tests: - pytest -v --cov=./ --cov-report=xml \ No newline at end of file + pytest -vv --cov=./ --cov-report=xml \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/logging.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/logging.py index f73cb68..8ed2813 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/logging.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/logging.py @@ -82,12 +82,14 @@ def printLogs(compact=False,anonymize=False): (rc, networkConfig) = config.network() if rc: domain = networkConfig["domain"] + tld = domain.split(".")[-1] + host = domain.split(".")[-2] serverHostname = networkConfig["serverHostname"] - realm= networkConfig["realm"] + realm = networkConfig["realm"] with open("/var/log/syslog") as logfile: - startPattern = re.compile("^.*linuxmuster-linuxclient7[^>]+======$") - endPattern = re.compile("^.*linuxmuster-linuxclient7.*======>.*$") + startPattern = re.compile("^.*[^>]+started ======$") + endPattern = re.compile("^.*======>.*end ======$") currentlyInsideOfLinuxmusterLinuxclient7Log = False @@ -105,6 +107,7 @@ def printLogs(compact=False,anonymize=False): line = re.sub(serverHostname, "server.linuxmuster.lan", line) line = re.sub(domain, "linuxmuster.lan", line) line = re.sub(realm, "LINUXMUSTER.LAN", line) + line = re.sub(f"DC={host},DC={tld}", "DC=linuxmuster,DC=lan", line) print(line) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/logging/syslog b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/logging/syslog new file mode 100644 index 0000000..da19002 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/logging/syslog @@ -0,0 +1,53 @@ +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1998]: THIS SHOULD NOT BE PRINTED +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] ====== onLogin started ====== +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] Cleaning linuxadmin gtk bookmarks +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Calculating mountpoint of //server/default-school/students/mlm/testnutzer +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Saving export 'SERVERHOME=/home/testnutzer/media/testnutzer' to tmp file +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Calculating mountpoint of //server01.internal-domain.lan/sysvol +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Calculating mountpoint of //server01.internal-domain.lan/sysvol +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Saving export 'SYSVOL=/srv/samba/testnutzer/sysvol' to tmp file +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] === Parsing policy [CN={828A96FB-D73E-4FB2-B886-0F6061F797F4},CN=Policies,CN=System,DC=internal-domain,DC=lan;0] === +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Calculating mountpoint of //internal-domain.lan/sysvol +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Calculating mountpoint of //internal-domain.lan/sysvol +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] == Parsing a drive policy! == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] Found shares: +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] * Labs | L | \\server\default-school\share\projects | 1 +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Calculating mountpoint of //server/default-school/share/projects +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] ==> Successfully parsed a drive policy! == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] == Parsing a printer policy! == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'ele-drucker1', 'bool': 'AND', 'userContext': '1', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'ele-drucker1', 'bool': 'OR', 'userContext': '0', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'ele-drucker2', 'bool': 'AND', 'userContext': '1', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'ele-drucker2', 'bool': 'OR', 'userContext': '0', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'las-drucker1', 'bool': 'AND', 'userContext': '1', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'las-drucker1', 'bool': 'OR', 'userContext': '0', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'sid-drucker1', 'bool': 'AND', 'userContext': '1', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'sid-drucker1', 'bool': 'OR', 'userContext': '0', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'sid-drucker2', 'bool': 'AND', 'userContext': '1', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'sid-drucker2', 'bool': 'OR', 'userContext': '0', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'tex-drucker1', 'bool': 'AND', 'userContext': '1', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Testing filter: {'name': 'tex-drucker1', 'bool': 'OR', 'userContext': '0', 'type': 'FilterGroup'} +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] Found printers: +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] * ELE-DRUCKER1#011#011| ipp://SERVER/printers/ELE-DRUCKER1#011| [{'name': 'ele-drucker1', 'bool': 'AND', 'userContext': '1', 'type': 'FilterGroup'}, {'name': 'ele-drucker1', 'bool': 'OR', 'userContext': '0', 'type': 'FilterGroup'}] +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] * LAS-DRUCKER1#011#011| ipp://SERVER/printers/LAS-DRUCKER1#011| [{'name': 'las-drucker1', 'bool': 'AND', 'userContext': '1', 'type': 'FilterGroup'}, {'name': 'las-drucker1', 'bool': 'OR', 'userContext': '0', 'type': 'FilterGroup'}] +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] * SID-DRUCKER1#011#011| ipp://SERVER/printers/SID-DRUCKER1#011| [{'name': 'sid-drucker1', 'bool': 'AND', 'userContext': '1', 'type': 'FilterGroup'}, {'name': 'sid-drucker1', 'bool': 'OR', 'userContext': '0', 'type': 'FilterGroup'}] +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] * SID-DRUCKER2#011#011| ipp://SERVER/printers/SID-DRUCKER2#011| [{'name': 'sid-drucker2', 'bool': 'AND', 'userContext': '1', 'type': 'FilterGroup'}, {'name': 'sid-drucker2', 'bool': 'OR', 'userContext': '0', 'type': 'FilterGroup'}] +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] * TEX-DRUCKER1#011#011| ipp://SERVER/printers/TEX-DRUCKER1#011| [{'name': 'tex-drucker1', 'bool': 'AND', 'userContext': '1', 'type': 'FilterGroup'}, {'name': 'tex-drucker1', 'bool': 'OR', 'userContext': '0', 'type': 'FilterGroup'}] +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] ==> Successfully parsed a printer policy! == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] ===> Parsed policy [CN={828A96FB-D73E-4FB2-B886-0F6061F797F4},CN=Policies,CN=System,DC=internal-domain,DC=lan;0] === +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] === Running local hook onLogin === +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] == Executing script /etc/linuxmuster-linuxclient7/onLogin.d/02_wallpaper.py == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] ==> Script /etc/linuxmuster-linuxclient7/onLogin.d/02_wallpaper.py finished with exit code 0 == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] == Executing script /etc/linuxmuster-linuxclient7/onLogin.d/00_example.sh == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] ==> Script /etc/linuxmuster-linuxclient7/onLogin.d/00_example.sh finished with exit code 0 == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] ===> Finished running local hook onLogin === +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] === Running remote hook onLogin === +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Calculating mountpoint of //server01.internal-domain.lan/sysvol +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [DEBUG] Calculating mountpoint of //server01.internal-domain.lan/sysvol +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] == Executing script /srv/samba/testnutzer/sysvol/internal-domain.lan/scripts/default-school/lmn/linux/logon.sh == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] ==> Script /srv/samba/testnutzer/sysvol/internal-domain.lan/scripts/default-school/lmn/linux/logon.sh finished with exit code 0 == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] == Executing script /srv/samba/testnutzer/sysvol/internal-domain.lan/scripts/default-school/custom/linux/logon.sh == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] ==> Script /srv/samba/testnutzer/sysvol/internal-domain.lan/scripts/default-school/custom/linux/logon.sh finished with exit code 0 == +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] ===> Finished running remote hook onLogin === +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[1735]: [INFO] ======> onLogin end ====== +Aug 19 11:55:52 server-test1 /usr/lib/gdm3/gdm-x-session[2014]: THIS SHOULD NOT BE PRINTED \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_logging.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_logging.py new file mode 100644 index 0000000..2111666 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_logging.py @@ -0,0 +1,52 @@ +from subprocess import CompletedProcess +from unittest import mock +from .. import logging +import os + +@mock.patch("subprocess.call") +def test_forAllLevels(mockSubprocessCall): + logging.debug("debug") + logging.info("info") + logging.warning("warning") + logging.error("error") + logging.fatal("fatal") + logging.exception(Exception("exception")) + + logs = _getLoggedLogs(mockSubprocessCall) + assert logs == ["[DEBUG] debug", "[INFO] info", "[WARNING] warning", "[ERROR] error", "[FATAL] fatal", "[ERROR] === An exception occurred ===", "[ERROR] exception", "[ERROR] === end exception ==="] + +@mock.patch("linuxmusterLinuxclient7.logging.print") +@mock.patch("linuxmusterLinuxclient7.logging.open") +@mock.patch("linuxmusterLinuxclient7.config.network") +def test_printLogs(mockConfigNetwork, mockOpen, mockPrint): + syslogFile = f"{os.path.dirname(os.path.realpath(__file__))}/files/logging/syslog" + mockOpen.return_value = open(syslogFile) + mockConfigNetwork.return_value = (True, {"domain": "internal-domain.lan", "serverHostname": "server01.internal-domain.lan", "realm": "INTERNAL-DOMAIN"}) + + logging.printLogs(True, True) + lines = _getPrintedLines(mockPrint) + text = "\n".join(lines) + assert "[INFO] ====== onLogin started ======" in text + assert "[INFO] ======> onLogin end ======" in text + assert "THIS SHOULD NOT BE PRINTED" not in text + assert "internal-domain" not in text + assert "Aug 19 11:55:52" not in text + +# -------------------- +# - Helper functions - +# -------------------- + +def _getLoggedLogs(mockSubprocessCall): + logs = [] + for call_args in mockSubprocessCall.call_args_list: + args = call_args.args[0] + print(args) + if type(args) == list and args[0] == "logger": + logs.append(args[3]) + return logs + +def _getPrintedLines(mockPrint): + lines = [] + for call_args in mockPrint.call_args_list: + lines.append(call_args.args[0]) + return lines \ No newline at end of file From 770a4fa90704b9bcee2f111095d6ce6f481867ac Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 12:28:26 +0200 Subject: [PATCH 49/57] Chore: remove unused imports --- .../python3/dist-packages/linuxmusterLinuxclient7/logging.py | 4 ++-- .../linuxmusterLinuxclient7/tests/test_logging.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/logging.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/logging.py index 8ed2813..4260166 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/logging.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/logging.py @@ -1,6 +1,6 @@ -import logging, os, traceback, re, sys, subprocess +import traceback, re, sys, subprocess from enum import Enum -from linuxmusterLinuxclient7 import user, config +from linuxmusterLinuxclient7 import config class Level(Enum): DEBUG = 0 diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_logging.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_logging.py index 2111666..8107372 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_logging.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_logging.py @@ -1,4 +1,3 @@ -from subprocess import CompletedProcess from unittest import mock from .. import logging import os From 08ccae20d976942ef53ac794a325f694556cc805 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 14:37:41 +0200 Subject: [PATCH 50/57] Tests: Add tests for realm --- .../tests/test_realm.py | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py new file mode 100644 index 0000000..2315634 --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py @@ -0,0 +1,126 @@ +from subprocess import CompletedProcess +from unittest import mock +from .. import realm +import os + +@mock.patch("subprocess.call") +def test_join(mockSubprocessCall): + mockSubprocessCall.return_value = 0 + assert realm.join("linuxmuster.lan", "global-admin") + calls = _getCallsTo(mockSubprocessCall, "realm") + assert len(calls) == 1 + assert ["realm", "join", "-v", "linuxmuster.lan", "-U", "global-admin"] in calls + + mockSubprocessCall.return_value = 1 + assert not realm.join("linuxmuster.lan", "global-admin") + +@mock.patch("subprocess.call") +def test_leave(mockSubprocessCall): + mockSubprocessCall.return_value = 0 + + assert realm.leave("linuxmuster.lan") + calls = _getCallsTo(mockSubprocessCall, "realm") + assert len(calls) == 1 + assert ["realm", "leave", "linuxmuster.lan"] in calls + + mockSubprocessCall.return_value = 1 + assert not realm.leave("linuxmuster.lan") + +@mock.patch("subprocess.run") +def test_getJoinedDomains(mockSubprocessRun): + realmListStdout = """linuxmuster.lan +windowsmuster.lan""" + mockSubprocessRun.return_value = CompletedProcess(args=["realm", "list", "--name-only"], returncode=0, stdout=realmListStdout) + + assert realm.getJoinedDomains() == (True, ["linuxmuster.lan", "windowsmuster.lan"]) + calls = _getCallsTo(mockSubprocessRun, "realm") + assert len(calls) == 1 + assert "realm list --name-only" in calls + + mockSubprocessRun.return_value = CompletedProcess(args=["realm", "list", "--name-only"], returncode=1, stdout="") + assert realm.getJoinedDomains() == (False, None) + +@mock.patch("linuxmusterLinuxclient7.realm.leave") +@mock.patch("linuxmusterLinuxclient7.realm.getJoinedDomains") +def test_leaveAll(mockGetJoinedDomains, mockLeave): + mockGetJoinedDomains.return_value = (True, ["linuxmuster.lan", "windowsmuster.lan"]) + mockLeave.return_value = True + assert realm.leaveAll() + calls = _getCalls(mockLeave) + assert len(calls) == 2 + assert "linuxmuster.lan" in calls + assert "windowsmuster.lan" in calls + + mockLeave.return_value = False + assert not realm.leaveAll() + + mockLeave.return_value = True + mockGetJoinedDomains.return_value = (False, None) + assert not realm.leaveAll() + + +@mock.patch("linuxmusterLinuxclient7.realm.getJoinedDomains") +def test_isJoined(mockGetJoinedDomains): + mockGetJoinedDomains.return_value = (True, ["linuxmuster.lan", "windowsmuster.lan"]) + assert realm.isJoined() + mockGetJoinedDomains.return_value = (False, None) + assert not realm.isJoined() + mockGetJoinedDomains.return_value = (True, ["linuxmuster.lan"]) + assert realm.isJoined() + +@mock.patch("subprocess.call") +@mock.patch("linuxmusterLinuxclient7.computer.krbHostName", lambda : "linuxmuster.lan") +def test_pullKerberosTicketForComputerAccount(mockSubprocessCall): + mockSubprocessCall.return_value = 0 + assert realm.pullKerberosTicketForComputerAccount() + calls = _getCallsTo(mockSubprocessCall, "kinit") + assert len(calls) == 1 + assert ["kinit", "-k", "linuxmuster.lan"] in calls + + mockSubprocessCall.return_value = 1 + assert not realm.pullKerberosTicketForComputerAccount() + +@mock.patch("subprocess.call") +@mock.patch("linuxmusterLinuxclient7.realm.isJoined") +@mock.patch("linuxmusterLinuxclient7.realm.pullKerberosTicketForComputerAccount") +def test_verifyDomainJoin(mockPullKerberosTicketForComputerAccount, mockIsJoined, mockSubprocessCall): + mockPullKerberosTicketForComputerAccount.return_value = True + mockIsJoined.return_value = True + mockSubprocessCall.return_value = 0 + + assert realm.verifyDomainJoin() + calls = _getCallsTo(mockSubprocessCall, "getent") + assert len(calls) == 1 + assert ["getent", "group", "domain users"] in calls + + mockIsJoined.return_value = False + assert not realm.verifyDomainJoin() + + mockIsJoined.return_value = True + mockSubprocessCall.return_value = 1 + assert not realm.verifyDomainJoin() + + mockSubprocessCall.return_value = 0 + mockPullKerberosTicketForComputerAccount.return_value = False + assert not realm.verifyDomainJoin() + + + +# -------------------- +# - Helper functions - +# -------------------- + +def _getCalls(mock): + calls = [] + for call_args in mock.call_args_list: + calls.append(call_args.args[0]) + return calls + +def _getCallsTo(mockSubprocessCall, program): + calls = [] + for call_args in mockSubprocessCall.call_args_list: + args = call_args.args[0] + print(args) + if (type(args) == list and args[0] == program) or (type(args) == str and args.startswith(f"{program} ")): + calls.append(args) + return calls \ No newline at end of file From 9a8adc2adbc9f968e8b22b5afe93788abdf5f8ec Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 19 Aug 2022 19:49:01 +0200 Subject: [PATCH 51/57] Tests: Add more tests for realm --- .../linuxmusterLinuxclient7/realm.py | 17 ++---- .../tests/test_realm.py | 58 +++++++++++++++++++ 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/realm.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/realm.py index e578f11..ce2eac6 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/realm.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/realm.py @@ -156,23 +156,17 @@ def getDomainConfig(domain): return False, None rawConfig = _readConfigFromString(result.stdout) - try: - rawDomainConfig = rawConfig["domain"] - except KeyError: - logging.error("Error when reading domain details") - return False, None - - domainConfig = {} try: + rawDomainConfig = rawConfig["domain"] + domainConfig = {} domainConfig["domain-controller"] = rawDomainConfig["domain-controller"] domainConfig["domain-name"] = rawDomainConfig["domain-name"] + return True, domainConfig except KeyError: logging.error("Error when reading domain details (2)") return False, None - return True, domainConfig - def clearUserCache(): """ Clears the local user cache @@ -182,14 +176,13 @@ def clearUserCache(): """ # clean sssd cache logging.info("Cleaning sssd cache.") - subprocess.call(["sssctl", "cache-remove", "--stop", "--start", "--override"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - return True + return subprocess.call(["sssctl", "cache-remove", "--stop", "--start", "--override"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 # -------------------- # - Helper functions - # -------------------- -def _readConfigFromStyring(string): +def _readConfigFromString(string): configParser = configparser.ConfigParser() configParser.read_string(string) return configParser \ No newline at end of file diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py index 2315634..cf59cf7 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py @@ -104,7 +104,65 @@ def test_verifyDomainJoin(mockPullKerberosTicketForComputerAccount, mockIsJoined mockPullKerberosTicketForComputerAccount.return_value = False assert not realm.verifyDomainJoin() +@mock.patch("subprocess.run") +def test_discoverDomains(mockSubprocessRun): + realmListStdout = """linuxmuster.lan +windowsmuster.lan""" + mockSubprocessRun.return_value = CompletedProcess(args=["realm", "discover", "--name-only"], returncode=0, stdout=realmListStdout) + assert realm.discoverDomains() == (True, ["linuxmuster.lan", "windowsmuster.lan"]) + calls = _getCallsTo(mockSubprocessRun, "realm") + assert len(calls) == 1 + assert "realm discover --name-only" in calls + + mockSubprocessRun.return_value = CompletedProcess(args=["realm", "discover", "--name-only"], returncode=1, stdout="") + assert realm.discoverDomains() == (False, None) + +@mock.patch("subprocess.run") +def test_getDomainConfig(mockSubprocessRun): + adcliInfoStdout = """[domain] +domain-name = linuxmuster.lan +domain-short = LINUXMUSTER +domain-forest = linuxmuster.lan +domain-controller = server.linuxmuster.lan +domain-controller-site = Default-First-Site-Name +domain-controller-flags = pdc gc ldap ds kdc timeserv closest writable good-timeserv full-secret +domain-controller-usable = yes +domain-controllers = server.linuxmuster.lan +[computer] +computer-site = Default-First-Site-Name""" + mockSubprocessRun.return_value = CompletedProcess(args=["adcli", "info", "linuxmuster.lan"], returncode=0, stdout=adcliInfoStdout) + + assert realm.getDomainConfig("linuxmuster.lan") == (True, {"domain-controller": "server.linuxmuster.lan", "domain-name": "linuxmuster.lan"}) + calls = _getCallsTo(mockSubprocessRun, "adcli") + assert len(calls) == 1 + assert "adcli info 'linuxmuster.lan'" in calls + + mockSubprocessRun.return_value = CompletedProcess(args=["adcli", "info", "linuxmuster.lan"], returncode=1, stdout="") + assert realm.getDomainConfig("linuxmuster.lan") == (False, None) + + adcliInfoStdout = """[domain] +domain-short = LINUXMUSTER +domain-forest = linuxmuster.lan +domain-controller = server.linuxmuster.lan +domain-controller-site = Default-First-Site-Name +domain-controller-flags = pdc gc ldap ds kdc timeserv closest writable good-timeserv full-secret +domain-controller-usable = yes +domain-controllers = server.linuxmuster.lan +[computer] +computer-site = Default-First-Site-Name""" + mockSubprocessRun.return_value = CompletedProcess(args=["adcli", "info", "linuxmuster.lan"], returncode=0, stdout=adcliInfoStdout) + assert realm.getDomainConfig("linuxmuster.lan") == (False, None) +@mock.patch("subprocess.call") +def test_clearUserCache(mockSubprocessCall): + mockSubprocessCall.return_value = 0 + assert realm.clearUserCache() + calls = _getCallsTo(mockSubprocessCall, "sssctl") + assert len(calls) == 1 + assert ["sssctl", "cache-remove", "--stop", "--start", "--override"] in calls + + mockSubprocessCall.return_value = 1 + assert not realm.clearUserCache() # -------------------- # - Helper functions - From 8cad4fea63440aa4f97a25741c78220ea8e40b87 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Wed, 14 Sep 2022 16:50:15 +0200 Subject: [PATCH 52/57] Chore: remove duplicate code in imageHelper --- .../linuxmusterLinuxclient7/imageHelper.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/imageHelper.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/imageHelper.py index e0924b7..a7a6c66 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/imageHelper.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/imageHelper.py @@ -73,20 +73,19 @@ def _upgradeSystem(unattended=False): # Perform an update logging.info("Updating this computer now...") - if subprocess.call(["apt", "update"]) != 0: - logging.error("apt update failed!") - return False - - if subprocess.call(["apt", "dist-upgrade", "-y"]) != 0: - logging.error("apt dist-upgrade failed!") - return False - - if subprocess.call(["apt", "autoremove", "-y"]) != 0: - logging.error("apt autoremove failed!") - return False - - if subprocess.call(["apt", "clean", "-y"]) != 0: - logging.error("apt clean failed!") + for action in ["update", "dist-upgrade", "autoremove", "clean"]: + if not _executeAptAction(action): + return False + + return True + +def _executeAptAction(action): + args = ["apt", action] + if action != "update": + args.append("-y") + + if subprocess.call(args) != 0: + logging.error(f"apt {action} failed!") return False return True From f2c05d2dc92510f617bc2e055a562629fc98ec5f Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Wed, 14 Sep 2022 17:44:27 +0200 Subject: [PATCH 53/57] Chore: handle invalid case --- Makefile | 3 +++ .../python3/dist-packages/linuxmusterLinuxclient7/shares.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 658b1a2..d437db5 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,9 @@ help: @echo " deb Build debian package" @echo " docs Build Sphinx documentation" @echo " clean Remove built files" + @echo " tests Run tests" + @echo " help Show this help" + deb: dpkg-buildpackage -rfakeroot -tc -sa -us -uc -I".directory" -I".git" -I"buildpackage.sh" diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/shares.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/shares.py index 64a879f..2528288 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/shares.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/shares.py @@ -46,6 +46,11 @@ def getMountpointOfRemotePath(remoteFilePath, hiddenShare = False, username = No :return: Tuple: (success, mountpoint) :rtype: tuple """ + + if username == None and user.isRoot() and not hiddenShare: + logging.error("root can only mount hidden shares!") + return False, None + remoteFilePath = remoteFilePath.replace("\\", "/") username = _getDefaultUsername(username) From b6cfb282edbbc913f6d7757c5b5565169a8cc164 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Wed, 14 Sep 2022 17:44:43 +0200 Subject: [PATCH 54/57] Tests: Add test to shares.getMountpointOfRemotePath --- .../tests/test_shares.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_shares.py diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_shares.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_shares.py new file mode 100644 index 0000000..9b6a44e --- /dev/null +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_shares.py @@ -0,0 +1,55 @@ +from subprocess import CompletedProcess +from unittest import mock +from .. import shares + +@mock.patch("linuxmusterLinuxclient7.shares.mountShare") +@mock.patch("linuxmusterLinuxclient7.shares.computer.hostname") +@mock.patch("linuxmusterLinuxclient7.shares.user.isRoot") +@mock.patch("linuxmusterLinuxclient7.shares.user.username") +def test_getMountpointOfRemotePath(mockUserUsername, mockUserIsRoot, mockComputerHostname, mockSharesMountShare): + mockUserIsRoot.return_value = False + mockUserUsername.return_value = "user2" + mockComputerHostname.return_value = "COMPUTER1" + mockSharesMountShare.return_value = (True, "/srv/samba/user1/sysvol") + + res = shares.getMountpointOfRemotePath("//server/sysvol/linuxmuster.lan/Policies", hiddenShare=False, username="user1", autoMount=False) + assert res == (True, "/home/user1/media/sysvol/linuxmuster.lan/Policies") + + res = shares.getMountpointOfRemotePath("//server/sysvol/linuxmuster.lan/Policies", hiddenShare=True, username="user1", autoMount=False) + assert res == (True, "/srv/samba/user1/sysvol/linuxmuster.lan/Policies") + + res = shares.getMountpointOfRemotePath("//server/", hiddenShare=True, username="user1", autoMount=False) + assert res == (False, None) + + # test with default username + res = shares.getMountpointOfRemotePath("//server/sysvol/linuxmuster.lan/Policies", hiddenShare=False, autoMount=False) + assert res == (True, "/home/user2/media/sysvol/linuxmuster.lan/Policies") + + res = shares.getMountpointOfRemotePath("//server/sysvol/linuxmuster.lan/Policies", hiddenShare=True, autoMount=False) + assert res == (True, "/srv/samba/user2/sysvol/linuxmuster.lan/Policies") + + # test with root + mockUserIsRoot.return_value = True + res = shares.getMountpointOfRemotePath("//server/sysvol/linuxmuster.lan/Policies", hiddenShare=False, autoMount=False) + assert res == (False, None) + + res = shares.getMountpointOfRemotePath("//server/sysvol/linuxmuster.lan/Policies", hiddenShare=True, autoMount=False) + assert res == (True, "/srv/samba/COMPUTER1$/sysvol/linuxmuster.lan/Policies") + + # test with autoMount + res = shares.getMountpointOfRemotePath("//server/sysvol/linuxmuster.lan/Policies", hiddenShare=False, username="user1", autoMount=True) + assert res == (True, "/home/user1/media/sysvol/linuxmuster.lan/Policies") + assert mockSharesMountShare.call_count == 1 + assert mockSharesMountShare.call_args == mock.call('//server/sysvol', hiddenShare=False, username='user1') + + res = shares.getMountpointOfRemotePath("//server/sysvol/linuxmuster.lan/Policies", hiddenShare=True, username="user1", autoMount=True) + assert res == (True, "/srv/samba/user1/sysvol/linuxmuster.lan/Policies") + assert mockSharesMountShare.call_count == 2 + assert mockSharesMountShare.call_args == mock.call('//server/sysvol', hiddenShare=True, username='user1') + + + mockSharesMountShare.return_value = (False, None) + res = shares.getMountpointOfRemotePath("//server/sysvol/linuxmuster.lan/Policies", hiddenShare=False, username="user1", autoMount=True) + assert res == (False, None) + assert mockSharesMountShare.call_count == 3 + assert mockSharesMountShare.call_args == mock.call('//server/sysvol', hiddenShare=False, username='user1') \ No newline at end of file From e075faf8cb45d2f752ec776f2d6a307ffe15a70a Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 29 Sep 2023 10:58:35 +0200 Subject: [PATCH 55/57] Chore: trigger CI From 4e13bf1641bce0622060dea75aee945f8c15ee1c Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Thu, 13 Nov 2025 20:29:54 +0100 Subject: [PATCH 56/57] Fix: realm test --- .../linuxmusterLinuxclient7/tests/test_realm.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py index cf59cf7..067ecb5 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py @@ -111,12 +111,17 @@ def test_discoverDomains(mockSubprocessRun): mockSubprocessRun.return_value = CompletedProcess(args=["realm", "discover", "--name-only"], returncode=0, stdout=realmListStdout) assert realm.discoverDomains() == (True, ["linuxmuster.lan", "windowsmuster.lan"]) calls = _getCallsTo(mockSubprocessRun, "realm") - assert len(calls) == 1 - assert "realm discover --name-only" in calls + assert ['realm', 'discover', '--name-only'] == calls[-1] mockSubprocessRun.return_value = CompletedProcess(args=["realm", "discover", "--name-only"], returncode=1, stdout="") assert realm.discoverDomains() == (False, None) + mockSubprocessRun.return_value = CompletedProcess(args=["realm", "discover", "--name-only"], returncode=0, stdout="linuxmuster.lan") + assert realm.discoverDomains("linuxmuster.lan") == (True, ["linuxmuster.lan"]) + calls = _getCallsTo(mockSubprocessRun, "realm") + assert len(calls) == 3 + assert ['realm', 'discover', '--name-only', "linuxmuster.lan"] == calls[-1] + @mock.patch("subprocess.run") def test_getDomainConfig(mockSubprocessRun): adcliInfoStdout = """[domain] From 7e151220339d9d49e2fa3c9cf33e18b0c4d9aad5 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Thu, 13 Nov 2025 20:49:27 +0100 Subject: [PATCH 57/57] Fix: return value --- usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py index 8c0688a..7d00f4c 100644 --- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py +++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py @@ -29,7 +29,7 @@ def processAllPolicies(): # - Helper functions - # -------------------- """ -Currently unused. May be useful for solving issue +Currently unused. May be useful for solving issue https://github.com/linuxmuster/linuxmuster-linuxclient7/issues/1 def _parseGplinkSring(string): # a gPLink strink looks like this: # [LDAP://;][LDAP://;][...] @@ -114,7 +114,7 @@ def _parsePolicy(policyDn): # parse drives allSuccessfull = _processDrivesPolicy(localPolicyPath) # parse printers - _processPrintersPolicy(localPolicyPath) and allSuccessfull + allSuccessfull = _processPrintersPolicy(localPolicyPath) and allSuccessfull except Exception as e: logging.error("An error occured when parsing policy!") logging.exception(e)