From aec9a9ca568f8b62951144e0200bc22b26621fa4 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Thu, 3 Aug 2023 09:18:21 -0400 Subject: [PATCH 1/3] Fix rbac around credential access add button (#14290) --- .../ResourceAccessList/ResourceAccessList.js | 48 ++++--------------- .../ResourceAccessList.test.js | 18 ++++++- 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/awx/ui/src/components/ResourceAccessList/ResourceAccessList.js b/awx/ui/src/components/ResourceAccessList/ResourceAccessList.js index c4c37c057edf..fdadccf140c1 100644 --- a/awx/ui/src/components/ResourceAccessList/ResourceAccessList.js +++ b/awx/ui/src/components/ResourceAccessList/ResourceAccessList.js @@ -1,10 +1,10 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { t } from '@lingui/macro'; -import { RolesAPI, TeamsAPI, UsersAPI, OrganizationsAPI } from 'api'; +import { RolesAPI, TeamsAPI, UsersAPI } from 'api'; import { getQSConfig, parseQueryString } from 'util/qs'; import useRequest, { useDeleteItems } from 'hooks/useRequest'; -import { useUserProfile, useConfig } from 'contexts/Config'; +import { useUserProfile } from 'contexts/Config'; import AddResourceRole from '../AddRole/AddResourceRole'; import AlertModal from '../AlertModal'; import DataListToolbar from '../DataListToolbar'; @@ -25,8 +25,7 @@ const QS_CONFIG = getQSConfig('access', { }); function ResourceAccessList({ apiModel, resource }) { - const { isSuperUser, isOrgAdmin } = useUserProfile(); - const { me } = useConfig(); + const { isSuperUser } = useUserProfile(); const [submitError, setSubmitError] = useState(null); const [deletionRecord, setDeletionRecord] = useState(null); const [deletionRole, setDeletionRole] = useState(null); @@ -34,42 +33,15 @@ function ResourceAccessList({ apiModel, resource }) { const [showDeleteModal, setShowDeleteModal] = useState(false); const location = useLocation(); - const { - isLoading: isFetchingOrgAdmins, - error: errorFetchingOrgAdmins, - request: fetchOrgAdmins, - result: { isCredentialOrgAdmin }, - } = useRequest( - useCallback(async () => { - if ( - isSuperUser || - resource.type !== 'credential' || - !isOrgAdmin || - !resource?.organization - ) { - return false; - } - const { - data: { count }, - } = await OrganizationsAPI.readAdmins(resource.organization, { - id: me.id, - }); - return { isCredentialOrgAdmin: !!count }; - }, [me.id, isOrgAdmin, isSuperUser, resource.type, resource.organization]), - { - isCredentialOrgAdmin: false, - } - ); - - useEffect(() => { - fetchOrgAdmins(); - }, [fetchOrgAdmins]); - let canAddAdditionalControls = false; if (isSuperUser) { canAddAdditionalControls = true; } - if (resource.type === 'credential' && isOrgAdmin && isCredentialOrgAdmin) { + if ( + resource.type === 'credential' && + resource?.summary_fields?.user_capabilities?.edit && + resource?.organization + ) { canAddAdditionalControls = true; } if (resource.type !== 'credential') { @@ -195,8 +167,8 @@ function ResourceAccessList({ apiModel, resource }) { return ( <> ', () => { expect(wrapper.find('ToolbarAddButton').length).toEqual(1); }); - test('should not show add button for non system admin & non org admin', async () => { + test('should not show add button for a user without edit permissions on the credential', async () => { useUserProfile.mockImplementation(() => { return { isSuperUser: false, @@ -476,7 +476,21 @@ describe('', () => { let wrapper; await act(async () => { wrapper = mountWithContexts( - , + , { context: { router: { credentialHistory } } } ); }); From 9bb6786a58b0f4b417804cb901bf0403c5efd4c6 Mon Sep 17 00:00:00 2001 From: kialam Date: Thu, 3 Aug 2023 06:46:46 -0700 Subject: [PATCH 2/3] Wait for new label IDs before setting label prompt values. (#14283) --- awx/ui/src/components/LaunchPrompt/LaunchPrompt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/src/components/LaunchPrompt/LaunchPrompt.js b/awx/ui/src/components/LaunchPrompt/LaunchPrompt.js index dba9741bf3e3..cf9c886658a6 100644 --- a/awx/ui/src/components/LaunchPrompt/LaunchPrompt.js +++ b/awx/ui/src/components/LaunchPrompt/LaunchPrompt.js @@ -77,7 +77,7 @@ function PromptModalForm({ } if (launchConfig.ask_labels_on_launch) { - const { labelIds } = createNewLabels( + const { labelIds } = await createNewLabels( values.labels, resource.organization ); From abc56305cc7ab0feb38c5b8c786f015ee7ed8b82 Mon Sep 17 00:00:00 2001 From: Sean Sullivan Date: Thu, 3 Aug 2023 14:06:04 -0400 Subject: [PATCH 3/3] Add Request time out option for collection (#14157) Co-authored-by: Jessica Steurer <70719005+jay-steurer@users.noreply.github.com> --- awx_collection/plugins/doc_fragments/auth.py | 5 +++++ .../plugins/doc_fragments/auth_plugin.py | 8 ++++++++ .../plugins/module_utils/controller_api.py | 16 ++++++++++++++-- .../integration/targets/project/tasks/main.yml | 17 +++++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/awx_collection/plugins/doc_fragments/auth.py b/awx_collection/plugins/doc_fragments/auth.py index 3cab718a7d26..763fe94dcddf 100644 --- a/awx_collection/plugins/doc_fragments/auth.py +++ b/awx_collection/plugins/doc_fragments/auth.py @@ -50,6 +50,11 @@ class ModuleDocFragment(object): - If value not set, will try environment variable C(CONTROLLER_VERIFY_SSL) and then config files type: bool aliases: [ tower_verify_ssl ] + request_timeout: + description: + - Specify the timeout Ansible should use in requests to the controller host. + - Defaults to 10s, but this is handled by the shared module_utils code + type: float controller_config_file: description: - Path to the controller config file. diff --git a/awx_collection/plugins/doc_fragments/auth_plugin.py b/awx_collection/plugins/doc_fragments/auth_plugin.py index 5a3a12b0e006..b46eaf6bbf03 100644 --- a/awx_collection/plugins/doc_fragments/auth_plugin.py +++ b/awx_collection/plugins/doc_fragments/auth_plugin.py @@ -68,6 +68,14 @@ class ModuleDocFragment(object): why: Collection name change alternatives: 'CONTROLLER_VERIFY_SSL' aliases: [ validate_certs ] + request_timeout: + description: + - Specify the timeout Ansible should use in requests to the controller host. + - Defaults to 10 seconds + - This will not work with the export or import modules. + type: float + env: + - name: CONTROLLER_REQUEST_TIMEOUT notes: - If no I(config_file) is provided we will attempt to use the tower-cli library diff --git a/awx_collection/plugins/module_utils/controller_api.py b/awx_collection/plugins/module_utils/controller_api.py index 19ce49785280..0c5c19165585 100644 --- a/awx_collection/plugins/module_utils/controller_api.py +++ b/awx_collection/plugins/module_utils/controller_api.py @@ -51,6 +51,7 @@ class ControllerModule(AnsibleModule): controller_username=dict(required=False, aliases=['tower_username'], fallback=(env_fallback, ['CONTROLLER_USERNAME', 'TOWER_USERNAME'])), controller_password=dict(no_log=True, aliases=['tower_password'], required=False, fallback=(env_fallback, ['CONTROLLER_PASSWORD', 'TOWER_PASSWORD'])), validate_certs=dict(type='bool', aliases=['tower_verify_ssl'], required=False, fallback=(env_fallback, ['CONTROLLER_VERIFY_SSL', 'TOWER_VERIFY_SSL'])), + request_timeout=dict(type='float', required=False, fallback=(env_fallback, ['CONTROLLER_REQUEST_TIMEOUT'])), controller_oauthtoken=dict( type='raw', no_log=True, aliases=['tower_oauthtoken'], required=False, fallback=(env_fallback, ['CONTROLLER_OAUTH_TOKEN', 'TOWER_OAUTH_TOKEN']) ), @@ -63,12 +64,14 @@ class ControllerModule(AnsibleModule): 'username': 'controller_username', 'password': 'controller_password', 'verify_ssl': 'validate_certs', + 'request_timeout': 'request_timeout', 'oauth_token': 'controller_oauthtoken', } host = '127.0.0.1' username = None password = None verify_ssl = True + request_timeout = 10 oauth_token = None oauth_token_id = None authenticated = False @@ -304,7 +307,7 @@ def __init__(self, argument_spec, direct_params=None, error_callback=None, warn_ kwargs['supports_check_mode'] = True super().__init__(argument_spec=argument_spec, direct_params=direct_params, error_callback=error_callback, warn_callback=warn_callback, **kwargs) - self.session = Request(cookies=CookieJar(), validate_certs=self.verify_ssl) + self.session = Request(cookies=CookieJar(), timeout=self.request_timeout, validate_certs=self.verify_ssl) if 'update_secrets' in self.params: self.update_secrets = self.params.pop('update_secrets') @@ -500,7 +503,14 @@ def make_request(self, method, endpoint, *args, **kwargs): data = dumps(kwargs.get('data', {})) try: - response = self.session.open(method, url.geturl(), headers=headers, validate_certs=self.verify_ssl, follow_redirects=True, data=data) + response = self.session.open( + method, url.geturl(), + headers=headers, + timeout=self.request_timeout, + validate_certs=self.verify_ssl, + follow_redirects=True, + data=data + ) except (SSLValidationError) as ssl_err: self.fail_json(msg="Could not establish a secure connection to your host ({1}): {0}.".format(url.netloc, ssl_err)) except (ConnectionError) as con_err: @@ -612,6 +622,7 @@ def authenticate(self, **kwargs): 'POST', api_token_url, validate_certs=self.verify_ssl, + timeout=self.request_timeout, follow_redirects=True, force_basic_auth=True, url_username=self.username, @@ -988,6 +999,7 @@ def logout(self): 'DELETE', api_token_url, validate_certs=self.verify_ssl, + timeout=self.request_timeout, follow_redirects=True, force_basic_auth=True, url_username=self.username, diff --git a/awx_collection/tests/integration/targets/project/tasks/main.yml b/awx_collection/tests/integration/targets/project/tasks/main.yml index f622af8e6f20..407292ec23e9 100644 --- a/awx_collection/tests/integration/targets/project/tasks/main.yml +++ b/awx_collection/tests/integration/targets/project/tasks/main.yml @@ -53,6 +53,23 @@ that: - result is not changed + - name: Create a git project and wait with short request timeout. + project: + name: "{{ project_name1 }}" + organization: Default + scm_type: git + scm_url: https://github.com/ansible/test-playbooks + wait: true + state: exists + request_timeout: .001 + register: result + ignore_errors: true + + - assert: + that: + - result is failed + - "'timed out' in result.msg" + - name: Delete a git project without credentials and wait project: name: "{{ project_name1 }}"