Skip to content

Commit 8d706ad

Browse files
authored
Merge pull request #88 from linuxmuster/feat/customShareNameTemplate
Feat: allow user to define custom template for share name
2 parents 4939eb7 + 7b17b43 commit 8d706ad

File tree

11 files changed

+198
-67
lines changed

11 files changed

+198
-67
lines changed

usr/lib/python3/dist-packages/linuxmusterLinuxclient7/config.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,23 @@ def network():
1919

2020
return True, networkConfig
2121

22+
def shares():
23+
"""
24+
Get the shares configuration in `/etc/linuxmuster-linuxclient7/config.yml`
25+
26+
:return: Tuple (success, dict of keys)
27+
:rtype: tuple
28+
"""
29+
config = _readConfig()
30+
sharesConfig = {}
31+
if config is not None and "shares" in config:
32+
sharesConfig = config["shares"]
33+
34+
if not "letterTemplate" in sharesConfig:
35+
sharesConfig["letterTemplate"] = constants.defaultShareLetterTemplate
36+
37+
return sharesConfig
38+
2239
def writeNetworkConfig(newNetworkConfig):
2340
"""
2441
Write the network configuration in `/etc/linuxmuster-linuxclient7/config.yml`.
@@ -49,17 +66,19 @@ def upgrade():
4966
"""
5067
return _upgrade()
5168

52-
def delete():
69+
def deleteNetworkConfig():
5370
"""
5471
Delete the network configuration file.
5572
5673
:return: True or False
5774
:rtype: bool
5875
"""
59-
legacyNetworkConfigFleDeleted = fileHelper.deleteFile(constants.legacyNetworkConfigFilePath)
60-
configFileDeleted = fileHelper.deleteFile(constants.configFilePath)
61-
return legacyNetworkConfigFleDeleted and configFileDeleted
62-
76+
config = _readConfig()
77+
if config is None or "network" not in config:
78+
return True
79+
80+
del config["network"]
81+
return _writeConfig(config)
6382

6483
# --------------------
6584
# - Helper functions -

usr/lib/python3/dist-packages/linuxmusterLinuxclient7/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
shareMountBasepath = "/home/{}/media"
1111
hiddenShareMountBasepath = "/srv/samba/{}"
1212
machineAccountSysvolMountPath = "/var/lib/samba/sysvol"
13+
defaultShareLetterTemplate = " ({letter}:)"
1314

1415
etcBaseDir = "/etc/linuxmuster-linuxclient7"
1516
shareBaseDir = "/usr/share/linuxmuster-linuxclient7"

usr/lib/python3/dist-packages/linuxmusterLinuxclient7/gpo.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,8 @@ def _processDrivesPolicy(policyBasepath):
239239

240240
for drive in shareList:
241241
if drive["useLetter"] == "1":
242-
shareName = f"{drive['label']} ({drive['letter']}:)"
242+
formattedLetter = config.shares()["letterTemplate"].format(letter=drive['letter'])
243+
shareName = f"{drive['label']}{formattedLetter}"
243244
else:
244245
shareName = drive["label"]
245246

usr/lib/python3/dist-packages/linuxmusterLinuxclient7/setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,9 @@ def _cleanOldDomainJoins():
210210
if not fileHelper.deleteFilesWithExtension("/var/lib/samba/private/tls", ".pem"):
211211
return False
212212

213-
# remove configuration
214-
logging.info(f"Deleting configuration files if exist ...")
215-
if not config.delete():
213+
# remove network configuration
214+
logging.info(f"Deleting network configuration ...")
215+
if not config.deleteNetworkConfig():
216216
return False
217217

218218
return True
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
network:
2+
serverHostname: server.linuxmuster.lan
3+
domain: linuxmuster.lan
4+
realm: LINUXMUSTER.LAN

usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/files/config/config.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
shares:
2+
letterTemplate: "_{letter}"
3+
14
network:
25
serverHostname: server.linuxmuster.lan
36
domain: linuxmuster.lan

usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_config.py

Lines changed: 79 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
from unittest import mock
33
from .. import config
44

5-
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"{os.path.dirname(os.path.realpath(__file__))}/files/config/network.conf")
5+
MOCK_FILE_PATH = f"{os.path.dirname(os.path.realpath(__file__))}/files/config"
6+
7+
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"{MOCK_FILE_PATH}/network.conf")
68
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"/does/not/exist/config.yml")
79
def test_network_legacy():
810
rc, networkConfig = config.network()
@@ -12,16 +14,16 @@ def test_network_legacy():
1214
assert networkConfig["realm"] == "LINUXMUSTER.LEGACY"
1315

1416
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"/does/not/exist/config.yml")
15-
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{os.path.dirname(os.path.realpath(__file__))}/files/config/config.yml")
17+
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{MOCK_FILE_PATH}/config.yml")
1618
def test_network():
1719
rc, networkConfig = config.network()
1820
assert rc
1921
assert networkConfig["serverHostname"] == "server.linuxmuster.lan"
2022
assert networkConfig["domain"] == "linuxmuster.lan"
2123
assert networkConfig["realm"] == "LINUXMUSTER.LAN"
2224

23-
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"{os.path.dirname(os.path.realpath(__file__))}/files/config/network.conf")
24-
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{os.path.dirname(os.path.realpath(__file__))}/files/config/config.yml")
25+
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"{MOCK_FILE_PATH}/network.conf")
26+
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{MOCK_FILE_PATH}/config.yml")
2527
def test_network_both():
2628
rc, networkConfig = config.network()
2729
assert rc
@@ -38,27 +40,44 @@ def test_network_none():
3840
assert networkConfig is None
3941

4042
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"/does/not/exist/network.conf")
41-
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{os.path.dirname(os.path.realpath(__file__))}/files/config/config.invalid-network.yml")
43+
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{MOCK_FILE_PATH}/config.invalid-network.yml")
4244
def test_network_invalid():
4345
rc, networkConfig = config.network()
4446
assert not rc
4547
assert networkConfig is None
4648

4749
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"/does/not/exist/network.conf")
48-
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{os.path.dirname(os.path.realpath(__file__))}/files/config/config.invalid-syntax.yml")
50+
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{MOCK_FILE_PATH}/config.yml")
51+
def test_shares():
52+
sharesConfig = config.shares()
53+
assert "letterTemplate" in sharesConfig
54+
assert sharesConfig["letterTemplate"] == "_{letter}"
55+
56+
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"/does/not/exist/network.conf")
57+
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"/does/not/exist/config.yml")
58+
def test_shares_none():
59+
sharesConfig = config.shares()
60+
assert "letterTemplate" in sharesConfig
61+
assert sharesConfig["letterTemplate"] == config.constants.defaultShareLetterTemplate
62+
63+
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"/does/not/exist/network.conf")
64+
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{MOCK_FILE_PATH}/config.no-shares.yml")
65+
def test_shares_missing():
66+
sharesConfig = config.shares()
67+
assert "letterTemplate" in sharesConfig
68+
assert sharesConfig["letterTemplate"] == config.constants.defaultShareLetterTemplate
69+
70+
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"/does/not/exist/network.conf")
71+
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{MOCK_FILE_PATH}/config.invalid-syntax.yml")
4972
def test_syntax_invalid():
5073
rc, networkConfig = config.network()
5174
assert not rc
5275
assert networkConfig is None
5376

5477
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"/tmp/config.yml")
5578
def test_writeNetworkConfig():
56-
if os.path.exists("/tmp/config.yml"):
57-
os.remove("/tmp/config.yml")
58-
59-
with open(f"{os.path.dirname(os.path.realpath(__file__))}/files/config/config.yml", "r") as fsrc:
60-
with open("/tmp/config.yml", "w") as fdst:
61-
fdst.write(fsrc.read())
79+
_deleteFile("/tmp/config.yml")
80+
_copyFile(f"{MOCK_FILE_PATH}/config.yml", "/tmp/config.yml")
6281

6382
newNetworkConfig = {
6483
"serverHostname": "server.linuxmuster.new",
@@ -72,17 +91,13 @@ def test_writeNetworkConfig():
7291
assert networkConfig["serverHostname"] == "server.linuxmuster.new"
7392
assert networkConfig["domain"] == "linuxmuster.new"
7493
assert networkConfig["realm"] == "LINUXMUSTER.NEW"
94+
assert config.shares() == {"letterTemplate": "_{letter}"}
7595

76-
# TODO: once there are more config options, test that they are preserved
7796

7897
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"/tmp/config.yml")
7998
def test_writeNetworkConfig_invalid():
80-
if os.path.exists("/tmp/config.yml"):
81-
os.remove("/tmp/config.yml")
82-
83-
with open(f"{os.path.dirname(os.path.realpath(__file__))}/files/config/config.yml", "r") as fsrc:
84-
with open("/tmp/config.yml", "w") as fdst:
85-
fdst.write(fsrc.read())
99+
_deleteFile("/tmp/config.yml")
100+
_copyFile(f"{MOCK_FILE_PATH}/config.yml", "/tmp/config.yml")
86101

87102
newNetworkConfig = {
88103
"sserverHostname": "server.linuxmuster.new",
@@ -109,8 +124,7 @@ def test_writeNetworkConfig_invalidPath():
109124

110125
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"/tmp/config.yml")
111126
def test_writeNetworkConfig_empty():
112-
if os.path.exists("/tmp/config.yml"):
113-
os.remove("/tmp/config.yml")
127+
_deleteFile("/tmp/config.yml")
114128

115129
newNetworkConfig = {
116130
"serverHostname": "server.linuxmuster.new",
@@ -128,15 +142,9 @@ def test_writeNetworkConfig_empty():
128142
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"/tmp/network.conf")
129143
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"/tmp/config.yml")
130144
def test_upgrade():
131-
if os.path.exists("/tmp/config.yml"):
132-
os.remove("/tmp/config.yml")
133-
134-
if os.path.exists("/tmp/network.conf"):
135-
os.remove("/tmp/network.conf")
136-
137-
with open(f"{os.path.dirname(os.path.realpath(__file__))}/files/config/network.conf", "r") as fsrc:
138-
with open("/tmp/network.conf", "w") as fdst:
139-
fdst.write(fsrc.read())
145+
_deleteFile("/tmp/config.yml")
146+
_deleteFile("/tmp/network.conf")
147+
_copyFile(f"{MOCK_FILE_PATH}/network.conf", "/tmp/network.conf")
140148

141149
assert config.upgrade()
142150
assert not os.path.exists("/tmp/network.conf")
@@ -152,8 +160,8 @@ def test_upgrade():
152160
assert yamlContent["network"]["realm"] == "LINUXMUSTER.LEGACY"
153161

154162

155-
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"{os.path.dirname(os.path.realpath(__file__))}/files/config/network.conf")
156-
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{os.path.dirname(os.path.realpath(__file__))}/files/config/config.yml")
163+
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"{MOCK_FILE_PATH}/network.conf")
164+
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"{MOCK_FILE_PATH}/config.yml")
157165
def test_upgrade_alreadyUpToDate():
158166
assert config.upgrade()
159167

@@ -166,36 +174,54 @@ def test_upgrade_alreadyUpToDate():
166174
assert yamlContent["network"]["domain"] == "linuxmuster.lan"
167175
assert yamlContent["network"]["realm"] == "LINUXMUSTER.LAN"
168176

169-
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"{os.path.dirname(os.path.realpath(__file__))}/files/config/network.invalid.conf")
177+
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"{MOCK_FILE_PATH}/network.invalid.conf")
170178
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"/tmp/config.yml")
171179
def test_upgrade_invalid():
172-
if os.path.exists("/tmp/config.yml"):
173-
os.remove("/tmp/config.yml")
180+
_deleteFile("/tmp/config.yml")
181+
182+
assert not config.upgrade()
183+
assert os.path.exists(f"{MOCK_FILE_PATH}/network.invalid.conf")
184+
assert not os.path.exists("/tmp/config.yml")
174185

186+
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"{MOCK_FILE_PATH}/network.conf")
187+
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"/does/not/exist/config.yml")
188+
def test_upgrade_unwritable():
175189
assert not config.upgrade()
176-
assert os.path.exists(f"{os.path.dirname(os.path.realpath(__file__))}/files/config/network.invalid.conf")
190+
assert os.path.exists(f"{MOCK_FILE_PATH}/network.invalid.conf")
177191
assert not os.path.exists("/tmp/config.yml")
178192

179193
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"/does/not/exist/network.conf")
180194
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"/does/not/exist/config.yml")
181195
def test_upgrade_nonexistent():
182196
assert not config.upgrade()
183197

184-
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"/tmp/network.conf")
198+
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"/does/not/exist/network.conf")
185199
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"/tmp/config.yml")
186-
def test_delete():
187-
if os.path.exists("/tmp/network.conf"):
188-
os.remove("/tmp/network.conf")
189-
if os.path.exists("/tmp/config.yml"):
190-
os.remove("/tmp/config.yml")
191-
192-
with open(f"{os.path.dirname(os.path.realpath(__file__))}/files/config/network.conf", "r") as fsrc:
193-
with open("/tmp/network.conf", "w") as fdst:
194-
fdst.write(fsrc.read())
195-
with open(f"{os.path.dirname(os.path.realpath(__file__))}/files/config/config.yml", "r") as fsrc:
196-
with open("/tmp/config.yml", "w") as fdst:
197-
fdst.write(fsrc.read())
198-
199-
assert config.delete()
200-
assert not os.path.exists("/tmp/network.conf")
201-
assert not os.path.exists("/tmp/config.yml")
200+
def test_deleteNetworkConfig():
201+
_deleteFile("/tmp/network.conf")
202+
_deleteFile("/tmp/config.yml")
203+
204+
_copyFile(f"{MOCK_FILE_PATH}/config.yml", "/tmp/config.yml")
205+
206+
assert config.deleteNetworkConfig()
207+
assert os.path.exists("/tmp/config.yml")
208+
assert config.network() == (False, None)
209+
assert config.shares() == {"letterTemplate": "_{letter}"}
210+
211+
@mock.patch("linuxmusterLinuxclient7.config.constants.legacyNetworkConfigFilePath", f"/does/not/exist/network.conf")
212+
@mock.patch("linuxmusterLinuxclient7.config.constants.configFilePath", f"/does/not/exist/config.yml")
213+
def test_deleteNetworkConfig_nonexistent():
214+
assert config.deleteNetworkConfig()
215+
216+
# --------------------
217+
# - Helper functions -
218+
# --------------------
219+
220+
def _deleteFile(path):
221+
if os.path.exists(path):
222+
os.remove(path)
223+
224+
def _copyFile(src, dst):
225+
with open(src, "r") as fsrc:
226+
with open(dst, "w") as fdst:
227+
fdst.write(fsrc.read())

usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_gpo.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,47 @@ def test_allOkAllTrue(mockUserSchool, mockLdapHelperSearchOne, mockSharesGetMoun
4646
assert len(mockPrintersInstallPrinter.call_args_list) == 1
4747
assert mockPrintersInstallPrinter.call_args_list[0] == mock.call('ipp://SERVER/printers/PRINTER1', 'PRINTER1')
4848

49+
@mock.patch("linuxmusterLinuxclient7.gpo.config.shares")
50+
@mock.patch("linuxmusterLinuxclient7.gpo.computer.isInGroup")
51+
@mock.patch("linuxmusterLinuxclient7.gpo.user.isInGroup")
52+
@mock.patch("linuxmusterLinuxclient7.gpo.printers.installPrinter")
53+
@mock.patch("linuxmusterLinuxclient7.gpo.shares.mountShare")
54+
@mock.patch("linuxmusterLinuxclient7.gpo.shares.getMountpointOfRemotePath")
55+
@mock.patch("linuxmusterLinuxclient7.gpo.ldapHelper.searchOne")
56+
@mock.patch("linuxmusterLinuxclient7.gpo.user.school")
57+
def test_customShareLetterTemplate(mockUserSchool, mockLdapHelperSearchOne, mockSharesGetMountpointOfRemotePath, mockSharesmMountShare, mockPrintersInstallPrinter, mockUserIsInGroup, mockComputerIsInGroup, mockConfigShares):
58+
mockUserSchool.return_value = (True, "school1")
59+
mockLdapHelperSearchOne.return_value = (True, {
60+
"distinguishedName": "policy1",
61+
"gPCFileSysPath": "\\\\linuxmuster.lan\\sysvol\\linuxmuster.lan\\Policies\\policy1"
62+
})
63+
mockSharesGetMountpointOfRemotePath.return_value = (True, f"{os.path.dirname(os.path.realpath(__file__))}/files/policy1")
64+
mockSharesmMountShare.return_value = (True, "")
65+
mockPrintersInstallPrinter.return_value = True
66+
mockUserIsInGroup.return_value = True
67+
mockComputerIsInGroup.return_value = True
68+
mockConfigShares.return_value = {
69+
"letterTemplate": "_{letter}"
70+
}
71+
72+
assert gpo.processAllPolicies()
73+
assert len(mockSharesmMountShare.call_args_list) == 3
74+
assert mockSharesmMountShare.call_args_list[0] == mock.call('\\\\server\\default-school\\program', shareName='Programs_K')
75+
# Projects (P:) is disabled and should not be mounted
76+
assert mockSharesmMountShare.call_args_list[1] == mock.call('\\\\server\\default-school\\students', shareName='Students-Home_S')
77+
assert mockSharesmMountShare.call_args_list[2] == mock.call('\\\\server\\default-school\\share', shareName='Shares')
78+
79+
# Test without letter
80+
mockConfigShares.return_value = {
81+
"letterTemplate": ""
82+
}
83+
assert gpo.processAllPolicies()
84+
assert len(mockSharesmMountShare.call_args_list) == 6
85+
assert mockSharesmMountShare.call_args_list[3] == mock.call('\\\\server\\default-school\\program', shareName='Programs')
86+
# Projects (P:) is disabled and should not be mounted
87+
assert mockSharesmMountShare.call_args_list[4] == mock.call('\\\\server\\default-school\\students', shareName='Students-Home')
88+
assert mockSharesmMountShare.call_args_list[5] == mock.call('\\\\server\\default-school\\share', shareName='Shares')
89+
4990
@mock.patch("linuxmusterLinuxclient7.gpo.computer.isInGroup")
5091
@mock.patch("linuxmusterLinuxclient7.gpo.user.isInGroup")
5192
@mock.patch("linuxmusterLinuxclient7.gpo.printers.installPrinter")

usr/lib/python3/dist-packages/linuxmusterLinuxclient7/tests/test_user.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,29 @@ def test_getHomeShareMountpoint(mockUsername, mockReadAttributes):
135135
rc, homeShareMountpoint = user.getHomeShareMountpoint()
136136
assert rc
137137
assert homeShareMountpoint == "/home/user1/media/user1 (H:)"
138+
139+
140+
@mock.patch("linuxmusterLinuxclient7.gpo.config.shares")
141+
@mock.patch("linuxmusterLinuxclient7.user.readAttributes")
142+
@mock.patch("linuxmusterLinuxclient7.user.username")
143+
def test_getHomeShareMountpointCustomShareLetterTemplate(mockUsername, mockReadAttributes, mockConfigShares):
144+
mockUsername.return_value = "user1"
145+
mockReadAttributes.return_value = (True, {"homeDrive": "H:"})
146+
mockConfigShares.return_value = {
147+
"letterTemplate": "_{letter}"
148+
}
149+
150+
rc, homeShareMountpoint = user.getHomeShareMountpoint()
151+
assert rc
152+
assert homeShareMountpoint == "/home/user1/media/user1_H"
153+
154+
# Test without letter
155+
mockConfigShares.return_value = {
156+
"letterTemplate": ""
157+
}
158+
rc, homeShareMountpoint = user.getHomeShareMountpoint()
159+
assert rc
160+
assert homeShareMountpoint == "/home/user1/media/user1"
138161

139162
@mock.patch("linuxmusterLinuxclient7.user.shares.mountShare")
140163
@mock.patch("linuxmusterLinuxclient7.user.readAttributes")

0 commit comments

Comments
 (0)