Skip to content

Commit

Permalink
NAS-129798 / 24.10 / New plugin that checks for the vendor sentinel f…
Browse files Browse the repository at this point in the history
…ile (#13970)

* Create `vendor.py`

* removed potential TOCTOU vulnerability

* add `private` option to `api_method` decorator

* shift `vendor.py` to use new api style

* remove proposed common models for version release safety

* deserialize the file

* catch `JSONDecodeError`
  • Loading branch information
creatorcary authored Jul 3, 2024
1 parent 7687364 commit 5215f5a
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/middlewared/middlewared/api/base/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ def api_method(
audit_callback: bool = False,
audit_extended: Callable[..., str] | None = None,
roles: list[str] | None = None,
private: bool = False,
):
"""
Mark a `Service` class method as a public API method.
Mark a `Service` class method as an API method.
`accepts` and `returns` are classes derived from `BaseModel` that correspond to the method's call arguments and
return value.
Expand All @@ -33,6 +34,8 @@ def api_method(
that will be appended to the audit message to be logged.
`roles` is a list of user roles that will gain access to this method.
`private` is `True` when the method should not be exposed in the public API. By default, the method is public.
"""
if list(returns.model_fields.keys()) != ["result"]:
raise TypeError("`returns` model must only have one field called `result`")
Expand Down Expand Up @@ -61,6 +64,7 @@ def wrapped(*args):
wrapped.audit_callback = audit_callback
wrapped.audit_extended = audit_extended
wrapped.roles = roles or []
wrapped._private = private

# FIXME: This is only here for backwards compatibility and should be removed eventually
wrapped.accepts = []
Expand Down
1 change: 1 addition & 0 deletions src/middlewared/middlewared/api/v25_04_0/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from .cloud_sync import * # noqa
from .common import * # noqa
from .user import * # noqa
from .vendor import * # noqa
17 changes: 17 additions & 0 deletions src/middlewared/middlewared/api/v25_04_0/vendor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from middlewared.api.base import BaseModel


class VendorNameArgs(BaseModel):
pass


class VendorNameResult(BaseModel):
result: str | None


class UnvendorArgs(BaseModel):
pass


class UnvendorResult(BaseModel):
result: None
28 changes: 28 additions & 0 deletions src/middlewared/middlewared/plugins/system/vendor/vendor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import json
import os

from middlewared.api import api_method
from middlewared.api.current import VendorNameArgs, VendorNameResult, UnvendorArgs, UnvendorResult
from middlewared.service import Service


SENTINEL_FILE_PATH = '/data/.vendor'


class VendorService(Service):

@api_method(VendorNameArgs, VendorNameResult, private=True)
def name(self) -> str | None:
try:
with open(SENTINEL_FILE_PATH, 'r') as file:
if contents := file.read():
return json.loads(contents).get('name')
except (FileNotFoundError, json.JSONDecodeError):
return None

@api_method(UnvendorArgs, UnvendorResult, private=True)
def unvendor(self):
try:
os.remove(SENTINEL_FILE_PATH)
except FileNotFoundError:
return None

0 comments on commit 5215f5a

Please sign in to comment.