Closes #22411:Enforce token write ability when executing custom scripts via the REST API
This commit is contained in:
parent
9f905cf842
commit
0b192cf588
|
|
@ -21,6 +21,7 @@ from netbox.api.metadata import ContentTypeMetadata
|
|||
from netbox.api.renderers import TextRenderer
|
||||
from netbox.api.viewsets import BaseViewSet, NetBoxModelViewSet
|
||||
from netbox.api.viewsets.mixins import ObjectValidationMixin
|
||||
from users.models import Token
|
||||
from utilities.exceptions import RQWorkerNotRunningException
|
||||
from utilities.request import copy_safe_request
|
||||
from utilities.rqworker import any_workers_for_queue
|
||||
|
|
@ -332,6 +333,12 @@ class ScriptViewSet(ModelViewSet):
|
|||
Run a Script identified by its numeric PK or module & name and return the pending Job as the result
|
||||
"""
|
||||
|
||||
# Running a script is a state-changing operation. If token authentication is in use, enforce the token's
|
||||
# write ability before performing any object lookup. Session-authenticated requests are unaffected
|
||||
# (request.auth is not a Token).
|
||||
if isinstance(request.auth, Token) and not request.auth.write_enabled:
|
||||
raise PermissionDenied("This token does not permit write operations.")
|
||||
|
||||
script = self._get_script(pk)
|
||||
|
||||
if not request.user.has_perm('extras.run_script', obj=script):
|
||||
|
|
|
|||
|
|
@ -1239,6 +1239,45 @@ class ScriptTestCase(APITestCase):
|
|||
# Restore the original setting for other tests
|
||||
self.TestScriptClass.Meta.scheduling_enabled = original
|
||||
|
||||
def test_run_token_write_enabled(self):
|
||||
"""
|
||||
Running a script is an unsafe (state-changing) action and must be rejected when the calling token has
|
||||
write_enabled=False, even if the user holds the run_script permission.
|
||||
"""
|
||||
self.add_permissions('extras.run_script')
|
||||
payload = {
|
||||
'data': {'var1': 'hello', 'var2': 1, 'var3': False},
|
||||
'commit': True,
|
||||
}
|
||||
|
||||
# A token with write_enabled=False should be rejected
|
||||
token = Token.objects.create(version=2, user=self.user, write_enabled=False)
|
||||
token_header = f'Bearer {TOKEN_PREFIX}{token.key}.{token.token}'
|
||||
response = self.client.post(self.url, payload, format='json', HTTP_AUTHORIZATION=token_header)
|
||||
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
|
||||
|
||||
# Enabling write ability on the token should allow the script to run
|
||||
token.write_enabled = True
|
||||
token.save()
|
||||
response = self.client.post(self.url, payload, format='json', HTTP_AUTHORIZATION=token_header)
|
||||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||
|
||||
def test_run_session_auth(self):
|
||||
"""
|
||||
The token write-ability check applies only to token authentication. Session-authenticated requests
|
||||
(where request.auth is not a Token) must still be allowed to run scripts.
|
||||
"""
|
||||
self.add_permissions('extras.run_script')
|
||||
payload = {
|
||||
'data': {'var1': 'hello', 'var2': 1, 'var3': False},
|
||||
'commit': True,
|
||||
}
|
||||
|
||||
# Authenticate via session rather than a token; request.auth is None
|
||||
self.client.force_authenticate(user=self.user)
|
||||
response = self.client.post(self.url, payload, format='json')
|
||||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||
|
||||
|
||||
class CreatedUpdatedFilterTestCase(APITestCase):
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue