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:
Seth Foster
2026-02-12 13:26:15 -05:00
committed by GitHub
parent c515b86fa6
commit 9700fb01f2
3 changed files with 65 additions and 1 deletions

View File

@@ -251,7 +251,13 @@ class CLI(object):
if self.resource != 'settings':
for method in ('list', 'modify', 'create'):
if method in parser.parser.choices:
parser.build_query_arguments(method, 'GET' if method == 'list' else 'POST')
if method == 'list':
http_method = 'GET'
elif method == 'modify' and 'PUT' in parser.options:
http_method = 'PUT'
else:
http_method = 'POST'
parser.build_query_arguments(method, http_method)
if from_sphinx:
parsed, extra = self.parser.parse_known_args(self.argv)
else:

View File

@@ -102,6 +102,18 @@ class ResourceOptionsParser(object):
if '299' in warning and 'deprecated' in warning:
self.deprecated = True
self.allowed_options = options.headers.get('Allow', '').split(', ')
# If the user can PUT on the detail endpoint but doesn't have
# POST on the list endpoint, use the detail endpoint's
# action schema so that 'modify' fields are populated.
if 'POST' not in self.options and 'PUT' in self.allowed_options:
try:
detail_actions = options.json().get('actions', {})
except Exception:
detail_actions = {}
if 'PUT' in detail_actions:
self.options['PUT'] = detail_actions['PUT']
elif 'GET' in detail_actions:
self.options['PUT'] = detail_actions['GET']
def build_list_actions(self):
action_map = {
@@ -109,6 +121,10 @@ class ResourceOptionsParser(object):
'POST': 'create',
}
for method, action in self.options.items():
# Skip 'PUT', which may be added by get_allowed_options
# and is handled separately by build_detail_actions
if method not in action_map:
continue
method = action_map[method]
parser = self.parser.add_parser(method, help='')
if method == 'list':