Skip to content

Commit 0328382

Browse files
committed
Did Hogjects#184, forgot to say it, plus fixed some bugs.
1 parent 1041fc0 commit 0328382

5 files changed

Lines changed: 73 additions & 42 deletions

File tree

src/lufus/browse_freely.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,23 @@ def open_url_non_root(url: str) -> None:
1818
if os.geteuid() == 0:
1919
target_user = None
2020
target_uid = None
21+
home_dir = None
2122

22-
if pkexec_uid:
23-
target_uid = pkexec_uid
24-
try:
25-
import pwd
23+
try:
24+
import pwd
2625

27-
target_user = pwd.getpwuid(int(pkexec_uid)).pw_name
28-
except Exception:
29-
pass
30-
elif sudo_user and sudo_user != "root":
31-
target_user = sudo_user
26+
if pkexec_uid:
27+
pw = pwd.getpwuid(int(pkexec_uid))
28+
target_user = pw.pw_name
29+
target_uid = pkexec_uid
30+
home_dir = pw.pw_dir
31+
elif sudo_user and sudo_user != "root":
32+
pw = pwd.getpwnam(sudo_user)
33+
target_user = sudo_user
34+
target_uid = str(pw.pw_uid)
35+
home_dir = pw.pw_dir
36+
except Exception as e:
37+
log.warning(f"Could not resolve target user info: {e}")
3238

3339
if target_user:
3440
try:
@@ -44,6 +50,8 @@ def open_url_non_root(url: str) -> None:
4450
),
4551
"WAYLAND_DISPLAY": os.environ.get("WAYLAND_DISPLAY", ""),
4652
"PATH": "/usr/local/bin:/usr/bin:/bin",
53+
# Ensure HOME is set so GUI apps use the target user's profile and config
54+
"HOME": home_dir or os.environ.get("HOME", ""),
4755
}
4856

4957
# Filter out empty values

src/lufus/gui/gui.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -119,19 +119,6 @@ def __init__(self, usb_devices=None, scale: Scale = None):
119119
self.current_language = state.language
120120
self._T = load_translations(self.current_language)
121121

122-
# restore theme from env when relaunched as root via pkexec :3
123-
env_theme = os.environ.get("LUFUS_THEME", "")
124-
if env_theme:
125-
state.theme = env_theme
126-
127-
# load persisted theme from config when not set via env :3
128-
if not getattr(state, "theme", ""):
129-
try:
130-
_theme_cfg = Path(user_config_dir("Lufus")) / "active_theme"
131-
state.theme = _theme_cfg.read_text(encoding="utf-8").strip()
132-
except Exception:
133-
pass
134-
135122
self.setWindowTitle(self._T.get("window_title", "lufus"))
136123

137124
# calculate window size based on screen dimensions

src/lufus/gui/start_gui.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@
1212

1313

1414
def _load_initial_theme():
15+
# Check environment variable first (passed during elevation)
16+
env_theme = os.environ.get("LUFUS_THEME")
17+
if env_theme:
18+
state.theme = env_theme
19+
return
20+
1521
# Load theme before elevation so it can be passed via env
16-
if getattr(state, "theme", ""):
22+
if getattr(state, "theme", "") and state.theme != "default":
1723
return
1824
try:
1925
_theme_cfg = Path(user_config_dir("Lufus")) / "active_theme"
@@ -24,6 +30,12 @@ def _load_initial_theme():
2430

2531

2632
def _load_initial_language():
33+
# If already set via environment (e.g. during elevation), prioritize it
34+
env_lang = os.environ.get("LUFUS_LANGUAGE")
35+
if env_lang:
36+
state.language = env_lang
37+
return
38+
2739
# Load persisted language preference; fall back to system locale detection
2840
try:
2941
_lang_cfg = Path(user_config_dir("Lufus")) / "active_language"
@@ -84,7 +96,11 @@ def launch_gui_with_usb_data() -> None:
8496
_show_root_warning()
8597
sys.exit(1)
8698

87-
_load_initial_language()
99+
# If we are root (either from start or after elevation), load/restore settings
100+
if os.geteuid() == 0:
101+
_load_initial_theme()
102+
_load_initial_language()
103+
88104
usb_devices = find_usb()
89105
log.info("Launching GUI with USB devices: %s", usb_devices)
90106

src/lufus/utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ def elevate_privileges() -> None:
2626
theme_val,
2727
)
2828

29+
if state.language:
30+
env["LUFUS_LANGUAGE"] = str(state.language)
31+
2932
# Preserve DISPLAY and XAUTHORITY for GUI apps under pkexec/sudo
3033
# Now also takes the detected XDG_DOWNLOAD_DIR of /src/lufus/user_paths.py to put it into LUFUS_DOWNLOAD_DIR
3134
env_vars = [
@@ -35,6 +38,7 @@ def elevate_privileges() -> None:
3538
"WAYLAND_DISPLAY",
3639
"PYTHONPATH",
3740
"LUFUS_THEME",
41+
"LUFUS_LANGUAGE",
3842
"LUFUS_DOWNLOAD_DIR",
3943
]
4044

tests/test_browse_freely.py

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,27 @@ def test_open_url_non_root_as_regular_user(self, mock_webbrowser, mock_geteuid):
2020
mock_webbrowser.assert_called_once_with(self.url)
2121

2222
@patch("os.geteuid")
23-
@patch("os.environ.get")
2423
@patch("subprocess.Popen")
2524
@patch("pwd.getpwuid")
26-
def test_open_url_as_root_via_pkexec(self, mock_getpwuid, mock_popen, mock_env_get, mock_geteuid):
25+
def test_open_url_as_root_via_pkexec(self, mock_getpwuid, mock_popen, mock_geteuid):
2726
# Simulate root (UID 0)
2827
mock_geteuid.return_value = 0
2928

3029
# Mock environment variables
31-
def side_effect(key, default=None):
32-
env = {"PKEXEC_UID": "1000", "DISPLAY": ":0", "XDG_RUNTIME_DIR": "/run/user/1000"}
33-
return env.get(key, default)
34-
35-
mock_env_get.side_effect = side_effect
30+
env_overrides = {
31+
"PKEXEC_UID": "1000",
32+
"DISPLAY": ":0",
33+
"XDG_RUNTIME_DIR": "/run/user/1000",
34+
}
3635

3736
# Mock pwd info
3837
mock_user = MagicMock()
3938
mock_user.pw_name = "raphael"
39+
mock_user.pw_dir = "/home/raphael"
4040
mock_getpwuid.return_value = mock_user
4141

42-
open_url_non_root(self.url)
42+
with patch.dict(os.environ, env_overrides, clear=False):
43+
open_url_non_root(self.url)
4344

4445
# Verify subprocess.Popen was called with runuser
4546
args, kwargs = mock_popen.call_args
@@ -53,30 +54,45 @@ def side_effect(key, default=None):
5354
env = kwargs.get("env", {})
5455
self.assertEqual(env.get("DISPLAY"), ":0")
5556
self.assertEqual(env.get("XDG_RUNTIME_DIR"), "/run/user/1000")
57+
self.assertEqual(env.get("HOME"), "/home/raphael")
5658

5759
@patch("os.geteuid")
58-
@patch("os.environ.get")
5960
@patch("subprocess.Popen")
60-
def test_open_url_as_root_via_sudo(self, mock_popen, mock_env_get, mock_geteuid):
61+
@patch("pwd.getpwnam")
62+
def test_open_url_as_root_via_sudo(self, mock_getpwnam, mock_popen, mock_geteuid):
6163
# Simulate root (UID 0)
6264
mock_geteuid.return_value = 0
6365

6466
# Mock environment variables (no PKEXEC_UID, but SUDO_USER)
65-
def side_effect(key, default=None):
66-
env = {"SUDO_USER": "raphael", "DISPLAY": ":0"}
67-
return env.get(key, default)
68-
69-
mock_env_get.side_effect = side_effect
70-
71-
open_url_non_root(self.url)
67+
env_overrides = {
68+
"SUDO_USER": "raphael",
69+
"DISPLAY": ":0",
70+
}
71+
# Ensure PKEXEC_UID is not in env if it was there
72+
with patch.dict(os.environ, env_overrides, clear=False):
73+
if "PKEXEC_UID" in os.environ:
74+
del os.environ["PKEXEC_UID"]
75+
76+
# Mock pwd info
77+
mock_user = MagicMock()
78+
mock_user.pw_uid = 1000
79+
mock_user.pw_dir = "/home/raphael"
80+
mock_getpwnam.return_value = mock_user
81+
82+
open_url_non_root(self.url)
7283

7384
# Verify subprocess.Popen was called with runuser
74-
args, _ = mock_popen.call_args
85+
args, kwargs = mock_popen.call_args
7586
cmd = args[0]
7687
self.assertEqual(cmd[0], "runuser")
7788
self.assertEqual(cmd[2], "raphael")
7889
self.assertIn(self.url, cmd)
7990

91+
# Verify env passing (XDG_RUNTIME_DIR should be derived from UID 1000)
92+
env = kwargs.get("env", {})
93+
self.assertEqual(env.get("XDG_RUNTIME_DIR"), "/run/user/1000")
94+
self.assertEqual(env.get("HOME"), "/home/raphael")
95+
8096

8197
if __name__ == "__main__":
8298
unittest.main()

0 commit comments

Comments
 (0)