diff --git a/.github/.codecov.yml b/.github/.codecov.yml
new file mode 100644
index 0000000..b6b0ec2
--- /dev/null
+++ b/.github/.codecov.yml
@@ -0,0 +1,3 @@
+ignore:
+ - "**/test_*.py"
+ - "usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests"
\ No newline at end of file
diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml
new file mode 100644
index 0000000..0646678
--- /dev/null
+++ b/.github/workflows/unittests.yml
@@ -0,0 +1,37 @@
+name: Unittests
+on: [push]
+jobs:
+ run:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest]
+ env:
+ OS: ${{ matrix.os }}
+ PYTHON: '3.10'
+ steps:
+ - uses: actions/checkout@master
+ - name: Setup Python
+ uses: actions/setup-python@master
+ with:
+ python-version: '3.10'
+ - name: Generate coverage report
+ run: |
+ sudo apt-get update
+ sudo 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
+ 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 }}
+ fail_ci_if_error: true
+ files: ./usr/lib/python3/dist-packages/linuxmusterLinuxclient7/coverage.xml
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index d8c2f40..c705f86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,7 @@ debian/linuxmuster-linuxclient7.substvars
debian/linuxmuster-linuxclient7/
__pycache__
/public
+venv
+.coverage
+coverage.xml
+.vscode/*.log
\ No newline at end of file
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/Makefile b/Makefile
index b6ee235..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"
@@ -13,4 +16,7 @@ docs:
cd docs && sphinx-build . ../public
clean:
- rm -r public
\ No newline at end of file
+ rm -r public
+
+tests:
+ pytest -vv --cov=./ --cov-report=xml
\ No newline at end of file
diff --git a/README.md b/README.md
index e7a49d0..5d72681 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,11 @@
-
-
+
+
+
+
+
@@ -24,9 +27,9 @@
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 | ❌ NO*
:---: | :---:
@@ -36,7 +39,7 @@ Maintainer organisation | Linuxmuster.net
Primary maintainer | dorian@itsblue.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 +48,16 @@ Primary maintainer | dorian@itsblue.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`
+2. with coverage: `pytest --cov=./ --cov-report=xml`
\ No newline at end of file
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/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])
diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/fileHelper.py
index ad9b55e..3fccd1f 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
"""
@@ -48,12 +48,12 @@ 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:
if os.path.exists(filePath):
- os.unlink(filePath)
+ os.remove(filePath)
return True
except Exception as e:
logging.error("Failed!")
@@ -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("/"):
@@ -105,11 +105,11 @@ 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
- :return: True on success, False otherwise
+ :return: True on success or if the directory does not exist, False otherwise
:rtype: bool
"""
diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py
index 20f10ba..7d00f4c 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 https://github.com/linuxmuster/linuxmuster-linuxclient7/issues/1
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)
+ allSuccessfull = _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
diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/hooks.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/hooks.py
index 52430c3..9e9ea3f 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 sorted(os.listdir(hookDir)):
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
diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/logging.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/logging.py
index f73cb68..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
@@ -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/printers.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/printers.py
index d4e42a1..b1f3c6b 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
@@ -35,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):
@@ -49,7 +46,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 +55,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:
@@ -98,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 = []
@@ -112,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/realm.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/realm.py
index ea176cc..eb77f62 100644
--- a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/realm.py
+++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/realm.py
@@ -162,23 +162,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
@@ -188,8 +182,7 @@ 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 -
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..a274df9
--- /dev/null
+++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/requirements.txt
@@ -0,0 +1,13 @@
+attrs==21.4.0
+coverage==6.3.2
+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
+pytest-cov==3.0.0
+python-ldap==3.4.0
+tomli==2.0.1
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)
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/__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/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/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/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/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_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
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
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
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
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..4aadb3e
--- /dev/null
+++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_hooks.py
@@ -0,0 +1,182 @@
+from pathlib import Path
+from unittest import mock
+from .. import hooks, fileHelper
+import pytest, os
+
+@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, hook):
+
+ # mock ennvironment
+ mockConfigNetwork.return_value = (True, {"domain": "linuxmuster.lan"})
+ mockUserAttributes.return_value = (True, {"sophomorixSchoolname": "default-school", "sophomorxCustomMulti1": ["test1", "test2"]})
+ mockComputerAttributes.return_value = (True, {"sophomorixSchoolname": "default-school"})
+ mockSharesGetLocalSysvolPath.return_value = (True, "/tmp/sysvol")
+
+ hookScripts = _createLocalHookScripts(hook)
+ hookScripts = hookScripts + _createRemoteHookScripts(hook)
+
+ hooks.runHook(hook)
+
+ lastExecutionTimestamp = 0
+ for script in hookScripts:
+ 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
+
+ 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")
+
+@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()
+
+@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)
+ 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")
+
+ 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:
+ 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)
+ except FileExistsError:
+ pass
+
+ try:
+ os.mkdir(f"{basedir}/on{hook.name}.d")
+ except FileExistsError:
+ pass
+
+ createdFiles = []
+ 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")
+ 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(f"#!/bin/bash\necho \"test-{hookKind}\"\necho \"$(env)\" > {hookScriptPath}-env")
+ os.chmod(hookScriptPath, 0o777)
+ createdFiles.append(hookScriptPath)
+
+ return createdFiles
\ No newline at end of file
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
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..8107372
--- /dev/null
+++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_logging.py
@@ -0,0 +1,51 @@
+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
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..6108905
--- /dev/null
+++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_printers.py
@@ -0,0 +1,90 @@
+from subprocess import CompletedProcess
+from unittest import mock
+from .. import printers
+
+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)
+
+
+@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"
+
+ 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"]
+
+ 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"]
+
+ 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
+ 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']
+
+ 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
+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_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"""
+ 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 -
+# --------------------
+
+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
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..067ecb5
--- /dev/null
+++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_realm.py
@@ -0,0 +1,189 @@
+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()
+
+@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 ['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]
+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 -
+# --------------------
+
+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
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
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..4f240fd
--- /dev/null
+++ b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_templates.py
@@ -0,0 +1,83 @@
+from unittest import mock
+from .. import templates, fileHelper
+import 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
+
+ assert 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
+
+"""
+ 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
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..266004f
--- /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_cleanTemplateUserGtkBookmarks_unwritableFile():
+ 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')
+
diff --git a/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py b/usr/lib/python3/dist-packages/linuxmusterLinuxclient7/user.py
index aa838f0..3deae5d 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():
"""
@@ -152,7 +151,8 @@ def _getHomeShareName(userAttributes=None):
return True, shareName
except Exception as e:
- logging.error("Could not mount home dir of user")
+ # This happens when userAttributes does not contain homeDrive
+ logging.error("Could not find home dir of user.")
logging.exception(e)
return False, None
\ No newline at end of file