Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAS-132001 / 25.04 / Fix __job_by_credential_and_id crashing for non-full-admins and internally ran jobs #14790

Merged
merged 2 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/middlewared/middlewared/service/core_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ def __job_by_credential_and_id(self, credential, job_id):

job = self.middleware.jobs[job_id]

if job.credentials is None:
raise CallError(
'Only users with full administrative privileges can access internally ran jobs', errno.EPERM,
)

if job.credentials.user['username'] == credential.user['username']:
return job

Expand Down
53 changes: 42 additions & 11 deletions tests/api2/test_job_logs.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,58 @@
import errno

import pytest
import requests

from middlewared.service_exception import CallError
from middlewared.test.integration.assets.account import unprivileged_user_client
from middlewared.test.integration.utils import mock, url
from middlewared.test.integration.utils import call, mock, url


@pytest.fixture(scope="module")
def c():
with unprivileged_user_client(allowlist=[{"method": "CALL", "resource": "test.test1"}]) as c:
yield c


def test_job_download_logs():
def test_job_download_logs(c):
with mock("test.test1", """
from middlewared.service import job

@job(logs=True)
def mock(self, job, *args):
job.logs_fd.write(b'Job logs')
"""):
with unprivileged_user_client(allowlist=[{"method": "CALL", "resource": "test.test1"}]) as c:
jid = c.call("test.test1")
jid = c.call("test.test1")

c.call("core.job_wait", jid, job=True)

path = c.call("core.job_download_logs", jid, 'logs.txt')

c.call("core.job_wait", jid, job=True)
r = requests.get(f"{url()}{path}")
r.raise_for_status()

assert r.headers["Content-Disposition"] == "attachment; filename=\"logs.txt\""
assert r.headers["Content-Type"] == "application/octet-stream"
assert r.text == "Job logs"


def test_job_download_logs_unprivileged_downloads_internal_logs(c):
with mock("test.test1", """
def mock(self, *args):
job = self.middleware.call_sync("test.test2")
job.wait_sync(raise_error=True)
return job.id
"""):
with mock("test.test2", """
from middlewared.service import job

path = c.call("core.job_download_logs", jid, 'logs.txt')
@job(logs=True)
def mock(self, job, *args):
job.logs_fd.write(b'Job logs')
"""):
jid = call("test.test1")

r = requests.get(f"{url()}{path}")
r.raise_for_status()
with pytest.raises(CallError) as ve:
c.call("core.job_download_logs", jid, 'logs.txt')

assert r.headers["Content-Disposition"] == "attachment; filename=\"logs.txt\""
assert r.headers["Content-Type"] == "application/octet-stream"
assert r.text == "Job logs"
assert ve.value.errno == errno.EPERM
Loading