mirror of
https://github.com/ansible/awx.git
synced 2026-02-25 15:06:02 -03:30
Fix awx CLI modify command for users with object-level permissions (#16276)
The awx CLI derives available fields for the `modify` command from the list endpoint's POST action schema. Users with object-level admin permissions (e.g., Project Admin) but no list-level POST permission see no field flags, making modify unusable despite having PUT access on the detail endpoint. Fall back to the detail endpoint's action schema when POST is not available on the list endpoint, and prefer PUT over POST when building modify arguments. Signed-off-by: Seth Foster <fosterbseth@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,24 @@ class ResourceOptionsParser(ResourceOptionsParser):
|
||||
self.allowed_options = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
|
||||
|
||||
|
||||
class NoPostResourceOptionsParser(ResourceOptionsParser):
|
||||
"""Simulates a user with object-level PUT but no list-level POST."""
|
||||
|
||||
detail_put_actions = {}
|
||||
|
||||
def get_allowed_options(self):
|
||||
self.allowed_options = ['GET', 'PUT', 'PATCH', 'DELETE']
|
||||
# Simulate the logic from the real get_allowed_options that
|
||||
# falls back to the detail endpoint's PUT schema when POST
|
||||
# is not available on the list endpoint.
|
||||
if 'POST' not in self.options and 'PUT' in self.allowed_options:
|
||||
if self.detail_put_actions:
|
||||
self.options['PUT'] = self.detail_put_actions
|
||||
|
||||
def handle_custom_actions(self):
|
||||
pass
|
||||
|
||||
|
||||
class OptionsPage(Page):
|
||||
def options(self):
|
||||
return self
|
||||
@@ -185,6 +203,30 @@ class TestOptions(unittest.TestCase):
|
||||
self.parser.choices[method].print_help(out)
|
||||
assert 'positional arguments:\n id' in out.getvalue()
|
||||
|
||||
def test_modify_without_list_post(self):
|
||||
"""User with object-level PUT but no list-level POST can still modify."""
|
||||
page = OptionsPage.from_json(
|
||||
{
|
||||
'actions': {
|
||||
'GET': {},
|
||||
}
|
||||
}
|
||||
)
|
||||
NoPostResourceOptionsParser.detail_put_actions = {
|
||||
'scm_branch': {'type': 'string', 'help_text': 'SCM branch'},
|
||||
'description': {'type': 'string', 'help_text': 'Description'},
|
||||
}
|
||||
options = NoPostResourceOptionsParser(None, page, 'projects', self.parser)
|
||||
|
||||
assert 'modify' in self.parser.choices
|
||||
assert 'create' not in self.parser.choices
|
||||
|
||||
options.build_query_arguments('modify', 'PUT')
|
||||
out = StringIO()
|
||||
self.parser.choices['modify'].print_help(out)
|
||||
assert '--scm_branch TEXT' in out.getvalue()
|
||||
assert '--description TEXT' in out.getvalue()
|
||||
|
||||
|
||||
class TestSettingsOptions(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
||||
Reference in New Issue
Block a user