From 68ac23dd469f6dfc580a8e1bcc53bc42a5470c96 Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 31 May 2018 13:19:32 -0400 Subject: [PATCH] Fixed various bugs with preventing prompting for credential passwords on schedules and workflow nodes --- .../features/templates/templates.strings.js | 5 +- .../src/templates/prompt/prompt.controller.js | 114 ++++++++++-------- .../workflow-maker.controller.js | 66 +++++++++- .../workflow-maker.partial.html | 10 +- 4 files changed, 135 insertions(+), 60 deletions(-) diff --git a/awx/ui/client/features/templates/templates.strings.js b/awx/ui/client/features/templates/templates.strings.js index 3bb2d38b66..21c7121d46 100644 --- a/awx/ui/client/features/templates/templates.strings.js +++ b/awx/ui/client/features/templates/templates.strings.js @@ -33,7 +33,7 @@ function TemplatesStrings (BaseString) { NO_INVENTORY_SELECTED: t.s('No inventory selected'), REVERT: t.s('REVERT'), CREDENTIAL_TYPE: t.s('Credential Type'), - CREDENTIAL_PASSWORD_WARNING: t.s('Credentials that require passwords on launch are not permitted for template schedules and workflow nodes. The following credentials must be removed or replaced to proceed:'), + CREDENTIAL_PASSWORD_WARNING: t.s('Credentials that require passwords on launch are not permitted for template schedules and workflow nodes. The following credentials must be replaced to proceed:'), PASSWORDS_REQUIRED_HELP: t.s('Launching this job requires the passwords listed below. Enter and confirm each password before continuing.'), PLEASE_ENTER_PASSWORD: t.s('Please enter a password.'), credential_passwords: { @@ -93,7 +93,8 @@ function TemplatesStrings (BaseString) { }; ns.workflows = { - INVALID_JOB_TEMPLATE: t.s('This Job Template is missing a default inventory or project. This must be addressed in the Job Template form before this node can be saved.') + INVALID_JOB_TEMPLATE: t.s('This Job Template is missing a default inventory or project. This must be addressed in the Job Template form before this node can be saved.'), + CREDENTIAL_WITH_PASS: t.s('This Job Template has a credential that requires a password. Credentials requiring passwords on launch are not permitted on workflow nodes.') }; } diff --git a/awx/ui/client/src/templates/prompt/prompt.controller.js b/awx/ui/client/src/templates/prompt/prompt.controller.js index 16c1b61a91..f0a3bd2bf6 100644 --- a/awx/ui/client/src/templates/prompt/prompt.controller.js +++ b/awx/ui/client/src/templates/prompt/prompt.controller.js @@ -73,61 +73,65 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel', vm.promptDataClone.prompts.credentials.passwords = {}; - if(vm.promptDataClone.launchConf.passwords_needed_to_start) { - let machineCredPassObj = null; - vm.promptDataClone.launchConf.passwords_needed_to_start.forEach((passwordNeeded) => { - if (passwordNeeded === "ssh_password" || - passwordNeeded === "become_password" || - passwordNeeded === "ssh_key_unlock" - ) { - if (!machineCredPassObj) { - vm.promptDataClone.prompts.credentials.value.forEach((defaultCredential) => { - if (defaultCredential.kind && defaultCredential.kind === "ssh") { - machineCredPassObj = { - id: defaultCredential.id, - name: defaultCredential.name - }; - } else if (defaultCredential.passwords_needed) { - defaultCredential.passwords_needed.forEach((neededPassword) => { - if (neededPassword === passwordNeeded) { - machineCredPassObj = { - id: defaultCredential.id, - name: defaultCredential.name - }; - } - }); - } - }); - } - - vm.promptDataClone.prompts.credentials.passwords[passwordNeeded] = angular.copy(machineCredPassObj); - } else if (passwordNeeded.startsWith("vault_password")) { - let vault_id = null; - if (passwordNeeded.includes('.')) { - vault_id = passwordNeeded.split(/\.(.+)/)[1]; - } - - if (!vm.promptDataClone.prompts.credentials.passwords.vault) { + vm.promptDataClone.prompts.credentials.value.forEach((credential) => { + if(credential.inputs) { + if(credential.inputs.password && credential.inputs.password === "ASK") { + vm.promptDataClone.prompts.credentials.passwords.ssh_password = { + id: credential.id, + name: credential.name + }; + } + if(credential.inputs.become_password && credential.inputs.become_password === "ASK") { + vm.promptDataClone.prompts.credentials.passwords.become_password = { + id: credential.id, + name: credential.name + }; + } + if(credential.inputs.ssh_key_unlock && credential.inputs.ssh_key_unlock === "ASK") { + vm.promptDataClone.prompts.credentials.passwords.ssh_key_unlock = { + id: credential.id, + name: credential.name + }; + } + if(credential.inputs.vault_password && credential.inputs.vault_password === "ASK") { + if(!vm.promptDataClone.prompts.credentials.passwords.vault) { vm.promptDataClone.prompts.credentials.passwords.vault = []; } - - // Loop across the default credentials to find the name of the - // credential that requires a password - vm.promptDataClone.prompts.credentials.value.forEach((defaultCredential) => { - if (vm.promptDataClone.prompts.credentials.credentialTypes[defaultCredential.credential_type] === "vault") { - let defaultCredVaultId = defaultCredential.vault_id || _.get(defaultCredential, 'inputs.vault_id') || null; - if (defaultCredVaultId === vault_id) { - vm.promptDataClone.prompts.credentials.passwords.vault.push({ - id: defaultCredential.id, - name: defaultCredential.name, - vault_id: defaultCredVaultId - }); - } - } + vm.promptDataClone.prompts.credentials.passwords.vault.push({ + id: credential.id, + name: credential.name, + vault_id: credential.inputs.vault_id }); } - }); - } + } else if(credential.passwords_needed && credential.passwords_needed.length > 0) { + credential.passwords_needed.forEach((passwordNeeded) => { + if (passwordNeeded === "ssh_password" || + passwordNeeded === "become_password" || + passwordNeeded === "ssh_key_unlock" + ) { + vm.promptDataClone.prompts.credentials.passwords[passwordNeeded] = { + id: credential.id, + name: credential.name + }; + } else if (passwordNeeded.startsWith("vault_password")) { + let vault_id = null; + if (passwordNeeded.includes('.')) { + vault_id = passwordNeeded.split(/\.(.+)/)[1]; + } + + if (!vm.promptDataClone.prompts.credentials.passwords.vault) { + vm.promptDataClone.prompts.credentials.passwords.vault = []; + } + + vm.promptDataClone.prompts.credentials.passwords.vault.push({ + id: credential.id, + name: credential.name, + vault_id: vault_id + }); + } + }); + } + }); vm.promptDataClone.credentialTypeMissing = []; @@ -141,7 +145,13 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel', }; order++; } - if(vm.promptDataClone.launchConf.ask_credential_on_launch || (vm.promptDataClone.launchConf.passwords_needed_to_start && vm.promptDataClone.launchConf.passwords_needed_to_start.length > 0)) { + if (vm.promptDataClone.launchConf.ask_credential_on_launch || + (_.has(vm, 'promptDataClone.prompts.credentials.passwords.vault') && + vm.promptDataClone.prompts.credentials.passwords.vault.length > 0) || + _.has(vm.promptDataClone.prompts.credentials.passwords.ssh_key_unlock) || + _.has(vm.promptDataClone.prompts.credentials.passwords.become_password) || + _.has(vm.promptDataClone.prompts.credentials.passwords.ssh_password) + ) { vm.steps.credential.includeStep = true; vm.steps.credential.tab = { _active: order === 1 ? true : false, diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js index 0665098a11..97b2007223 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js @@ -11,7 +11,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $state, ProcessErrors, CreateSelect2, $q, JobTemplate, Empty, PromptService, Rest, TemplatesStrings, $timeout) { - let promptWatcher, surveyQuestionWatcher; + let promptWatcher, surveyQuestionWatcher, credentialsWatcher; $scope.strings = TemplatesStrings; $scope.preventCredsWithPasswords = true; @@ -341,6 +341,20 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', }); }; + let checkCredentialsForRequiredPasswords = () => { + let credentialRequiresPassword = false; + $scope.promptData.prompts.credentials.value.forEach((credential) => { + if ((credential.passwords_needed && + credential.passwords_needed.length > 0) || + (_.has(credential, 'inputs.vault_password') && + credential.inputs.vault_password === "ASK") + ) { + credentialRequiresPassword = true; + } + }); + $scope.credentialRequiresPassword = credentialRequiresPassword; + }; + let watchForPromptChanges = () => { let promptDataToWatch = [ 'promptData.prompts.inventory.value', @@ -357,6 +371,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } $scope.promptModalMissingReqFields = missingPromptValue; }); + + if ($scope.promptData.launchConf.ask_credential_on_launch && $scope.credentialRequiresPassword) { + credentialsWatcher = $scope.$watch('promptData.prompts.credentials', () => { + checkCredentialsForRequiredPasswords(); + }); + } }; $scope.closeWorkflowMaker = function() { @@ -537,6 +557,10 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', surveyQuestionWatcher(); } + if (credentialsWatcher) { + credentialsWatcher(); + } + $scope.promptData = null; // Reset the edgeConflict flag @@ -564,6 +588,10 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', surveyQuestionWatcher(); } + if (credentialsWatcher) { + credentialsWatcher(); + } + $scope.promptData = null; $scope.selectedTemplateInvalid = false; $scope.showPromptButton = false; @@ -671,6 +699,24 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.selectedTemplateInvalid = false; } + let credentialRequiresPassword = false; + + prompts.credentials.value.forEach((credential) => { + if(credential.inputs) { + if ((credential.inputs.password && credential.inputs.password === "ASK") || + (credential.inputs.become_password && credential.inputs.become_password === "ASK") || + (credential.inputs.ssh_key_unlock && credential.inputs.ssh_key_unlock === "ASK") || + (credential.inputs.vault_password && credential.inputs.vault_password === "ASK") + ) { + credentialRequiresPassword = true; + } + } else if (credential.passwords_needed && credential.passwords_needed.length > 0) { + credentialRequiresPassword = true; + } + }); + + $scope.credentialRequiresPassword = credentialRequiresPassword; + if (!launchConf.survey_enabled && !launchConf.ask_inventory_on_launch && !launchConf.ask_credential_on_launch && @@ -682,7 +728,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', !launchConf.ask_diff_mode_on_launch && !launchConf.survey_enabled && !launchConf.credential_needed_to_start && - launchConf.passwords_needed_to_start.length === 0 && launchConf.variables_needed_to_start.length === 0) { $scope.showPromptButton = false; $scope.promptModalMissingReqFields = false; @@ -727,6 +772,8 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.missingSurveyValue = missingSurveyValue; }, true); + checkCredentialsForRequiredPasswords(); + watchForPromptChanges(); }); } else { @@ -736,6 +783,9 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', prompts: prompts, template: $scope.nodeBeingEdited.unifiedJobTemplate.id }; + + checkCredentialsForRequiredPasswords(); + watchForPromptChanges(); } } @@ -980,6 +1030,10 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', surveyQuestionWatcher(); } + if (credentialsWatcher) { + credentialsWatcher(); + } + if (selectedTemplate.type === "job_template") { let jobTemplate = new JobTemplate(); @@ -993,6 +1047,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', $scope.selectedTemplateInvalid = false; } + if (launchConf.passwords_needed_to_start && launchConf.passwords_needed_to_start.length > 0) { + $scope.credentialRequiresPassword = true; + } else { + $scope.credentialRequiresPassword = false; + } + $scope.selectedTemplate = angular.copy(selectedTemplate); if (!launchConf.survey_enabled && @@ -1006,7 +1066,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', !launchConf.ask_diff_mode_on_launch && !launchConf.survey_enabled && !launchConf.credential_needed_to_start && - launchConf.passwords_needed_to_start.length === 0 && launchConf.variables_needed_to_start.length === 0) { $scope.showPromptButton = false; $scope.promptModalMissingReqFields = false; @@ -1069,7 +1128,6 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService', } }); } else { - // TODO - clear out prompt data? $scope.selectedTemplate = angular.copy(selectedTemplate); $scope.selectedTemplateInvalid = false; $scope.showPromptButton = false; diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html index 6d92841eb0..9580b72bfc 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html @@ -99,7 +99,13 @@ {{:: strings.get('workflows.INVALID_JOB_TEMPLATE') }} -
+
+
+ + {{:: strings.get('workflows.CREDENTIAL_WITH_PASS') }} +
+
+