action_utils: Implement a utility to run a command as different user

- To be used to run specific command as another user.

Tests:

- Unit tests.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Joseph Nuthalapati <njoseph@riseup.net>
This commit is contained in:
Sunil Mohan Adapa 2025-08-11 14:02:52 -07:00 committed by Joseph Nuthalapati
parent 46c3ac90f3
commit de1070df35
No known key found for this signature in database
GPG Key ID: 5398F00A2FA43C35
2 changed files with 26 additions and 1 deletions

View File

@ -788,3 +788,13 @@ def move_uploaded_file(source: str | pathlib.Path,
shutil.move(source, destination)
shutil.chown(destination, user, group)
destination.chmod(permissions)
def run_as_user(command, username, **kwargs):
"""Run a command as another user.
Uses 'runuser' which is similar to 'su'. Creates PAM session unlike
setpriv. Sets real/effective uid/gid and resets the environment.
"""
command = ['runuser', '--user', username, '--'] + command
return subprocess.run(command, **kwargs)

View File

@ -12,7 +12,7 @@ import pytest
from plinth.action_utils import (get_addresses, get_hostname,
is_systemd_running, move_uploaded_file,
service_action, service_disable,
run_as_user, service_action, service_disable,
service_enable, service_is_enabled,
service_is_running, service_reload,
service_restart, service_start, service_stop,
@ -229,3 +229,18 @@ def test_move_uploaded_file(tmp_path, upload_dir):
assert destination_file.stat().st_mode & 0o777 == 0o600
assert destination_file.read_text() == 'x-contents-2'
assert not source.exists()
@patch('subprocess.run')
def test_run_as_user(run):
"""Test running a command as another user works."""
run.return_value = 'test-return-value'
return_value = run_as_user(['command', 'arg1', '--foo'],
username='foouser', stdout=subprocess.PIPE,
check=True)
assert return_value == 'test-return-value'
assert run.mock_calls == [
call(
['runuser', '--user', 'foouser', '--', 'command', 'arg1', '--foo'],
stdout=subprocess.PIPE, check=True)
]