Implemented the ability to specify credentials when creating a scheduled job run. Added validation for removing but not replacing default credentials.

This commit is contained in:
mabashian
2018-03-01 15:24:50 -05:00
parent 61a48996ee
commit a5043029c1
16 changed files with 264 additions and 67 deletions

View File

@@ -118,5 +118,5 @@
query-set="querySet"> query-set="querySet">
</paginate> </paginate>
</at-panel-body> </at-panel-body>
<prompt prompt-data="promptData" open-modal="openModal" on-finish="launchJob()"></launch> <prompt prompt-data="promptData" on-finish="launchJob()"></launch>
</at-panel> </at-panel>

View File

@@ -55,7 +55,8 @@ function TemplatesStrings (BaseString) {
VALID_DECIMAL: t.s('Please enter an answer that is a decimal number.'), VALID_DECIMAL: t.s('Please enter an answer that is a decimal number.'),
PLAYBOOK_RUN: t.s('Playbook Run'), PLAYBOOK_RUN: t.s('Playbook Run'),
CHECK: t.s('Check'), CHECK: t.s('Check'),
NO_CREDS_MATCHING_TYPE: t.s('No Credentials Matching This Type Have Been Created'), NO_CREDS_MATCHING_TYPE: t.s('No Credentials Matching This Type Have Been Created'),
CREDENTIAL_TYPE_MISSING: typeLabel => t.s('This job template has a default {{typeLabel}} credential which must be included or replaced before proceeding.', { typeLabel })
}; };
ns.alert = { ns.alert = {
@@ -85,7 +86,7 @@ function TemplatesStrings (BaseString) {
ns.warnings = { ns.warnings = {
WORKFLOW_RESTRICTED_COPY: t.s('You do not have access to all resources used by this workflow. Resources that you don\'t have access to will not be copied and will result in an incomplete workflow.') WORKFLOW_RESTRICTED_COPY: t.s('You do not have access to all resources used by this workflow. Resources that you don\'t have access to will not be copied and will result in an incomplete workflow.')
} };
} }
TemplatesStrings.$inject = ['BaseStringService']; TemplatesStrings.$inject = ['BaseStringService'];

View File

@@ -0,0 +1,38 @@
let Base;
let $http;
function postCredential (params) {
const req = {
method: 'POST',
url: `${this.path}${params.id}/credentials/`
};
if (params.data) {
req.data = params.data;
}
return $http(req);
}
function ScheduleModel (method, resource, config) {
Base.call(this, 'schedules');
this.Constructor = ScheduleModel;
this.postCredential = postCredential.bind(this);
return this.create(method, resource, config);
}
function ScheduleModelLoader (BaseModel, _$http_) {
Base = BaseModel;
$http = _$http_;
return ScheduleModel;
}
ScheduleModelLoader.$inject = [
'BaseModel',
'$http'
];
export default ScheduleModelLoader;

View File

@@ -16,6 +16,7 @@ import ModelsStrings from '~models/models.strings';
import NotificationTemplate from '~models/NotificationTemplate'; import NotificationTemplate from '~models/NotificationTemplate';
import Organization from '~models/Organization'; import Organization from '~models/Organization';
import Project from '~models/Project'; import Project from '~models/Project';
import Schedule from '~models/Schedule';
import UnifiedJobTemplate from '~models/UnifiedJobTemplate'; import UnifiedJobTemplate from '~models/UnifiedJobTemplate';
import WorkflowJob from '~models/WorkflowJob'; import WorkflowJob from '~models/WorkflowJob';
import WorkflowJobTemplate from '~models/WorkflowJobTemplate'; import WorkflowJobTemplate from '~models/WorkflowJobTemplate';
@@ -43,6 +44,7 @@ angular
.service('NotificationTemplate', NotificationTemplate) .service('NotificationTemplate', NotificationTemplate)
.service('OrganizationModel', Organization) .service('OrganizationModel', Organization)
.service('ProjectModel', Project) .service('ProjectModel', Project)
.service('ScheduleModel', Schedule)
.service('UnifiedJobTemplateModel', UnifiedJobTemplate) .service('UnifiedJobTemplateModel', UnifiedJobTemplate)
.service('WorkflowJobModel', WorkflowJob) .service('WorkflowJobModel', WorkflowJob)
.service('WorkflowJobTemplateModel', WorkflowJobTemplate) .service('WorkflowJobTemplateModel', WorkflowJobTemplate)

View File

@@ -1,36 +1,37 @@
export default export default
function SchedulePost(Rest, ProcessErrors, RRuleToAPI, Wait, $q) { function SchedulePost(Rest, ProcessErrors, RRuleToAPI, Wait, $q, Schedule) {
return function(params) { return function(params) {
var scope = params.scope, var scope = params.scope,
url = params.url, url = params.url,
scheduler = params.scheduler, scheduler = params.scheduler,
mode = params.mode, mode = params.mode,
schedule = (params.schedule) ? params.schedule : {}, scheduleData = (params.schedule) ? params.schedule : {},
promptData = params.promptData, promptData = params.promptData,
priorCredentials = params.priorCredentials ? params.priorCredentials : [],
newSchedule, rrule, extra_vars; newSchedule, rrule, extra_vars;
let deferred = $q.defer(); let deferred = $q.defer();
if (scheduler.isValid()) { if (scheduler.isValid()) {
Wait('start'); Wait('start');
newSchedule = scheduler.getValue(); newSchedule = scheduler.getValue();
rrule = scheduler.getRRule(); rrule = scheduler.getRRule();
schedule.name = newSchedule.name; scheduleData.name = newSchedule.name;
schedule.rrule = RRuleToAPI(rrule.toString(), scope); scheduleData.rrule = RRuleToAPI(rrule.toString(), scope);
schedule.description = (/error/.test(rrule.toText())) ? '' : rrule.toText(); scheduleData.description = (/error/.test(rrule.toText())) ? '' : rrule.toText();
if (scope.isFactCleanup) { if (scope.isFactCleanup) {
extra_vars = { extra_vars = {
"older_than": scope.scheduler_form.keep_amount.$viewValue + scope.scheduler_form.keep_unit.$viewValue.value, "older_than": scope.scheduler_form.keep_amount.$viewValue + scope.scheduler_form.keep_unit.$viewValue.value,
"granularity": scope.scheduler_form.granularity_keep_amount.$viewValue + scope.scheduler_form.granularity_keep_unit.$viewValue.value "granularity": scope.scheduler_form.granularity_keep_amount.$viewValue + scope.scheduler_form.granularity_keep_unit.$viewValue.value
}; };
schedule.extra_data = JSON.stringify(extra_vars); scheduleData.extra_data = JSON.stringify(extra_vars);
} else if (scope.cleanupJob) { } else if (scope.cleanupJob) {
extra_vars = { extra_vars = {
"days" : scope.scheduler_form.schedulerPurgeDays.$viewValue "days" : scope.scheduler_form.schedulerPurgeDays.$viewValue
}; };
schedule.extra_data = JSON.stringify(extra_vars); scheduleData.extra_data = JSON.stringify(extra_vars);
} }
else if(scope.extraVars){ else if(scope.extraVars){
schedule.extra_data = scope.parseType === 'yaml' ? scheduleData.extra_data = scope.parseType === 'yaml' ?
(scope.extraVars === '---' ? "" : jsyaml.safeLoad(scope.extraVars)) : scope.extraVars; (scope.extraVars === '---' ? "" : jsyaml.safeLoad(scope.extraVars)) : scope.extraVars;
} }
@@ -40,10 +41,10 @@ export default
var fld = promptData.surveyQuestions[i].variable; var fld = promptData.surveyQuestions[i].variable;
// grab all survey questions that have answers // grab all survey questions that have answers
if(promptData.surveyQuestions[i].required || (promptData.surveyQuestions[i].required === false && promptData.surveyQuestions[i].model.toString()!=="")) { if(promptData.surveyQuestions[i].required || (promptData.surveyQuestions[i].required === false && promptData.surveyQuestions[i].model.toString()!=="")) {
if(!schedule.extra_data) { if(!scheduleData.extra_data) {
schedule.extra_data = {}; scheduleData.extra_data = {};
} }
schedule.extra_data[fld] = promptData.surveyQuestions[i].model; scheduleData.extra_data[fld] = promptData.surveyQuestions[i].model;
} }
if(promptData.surveyQuestions[i].required === false && _.isEmpty(promptData.surveyQuestions[i].model)) { if(promptData.surveyQuestions[i].required === false && _.isEmpty(promptData.surveyQuestions[i].model)) {
@@ -55,7 +56,7 @@ export default
case "text": case "text":
case "textarea": case "textarea":
if (promptData.surveyQuestions[i].min === 0) { if (promptData.surveyQuestions[i].min === 0) {
schedule.extra_data[fld] = ""; scheduleData.extra_data[fld] = "";
} }
break; break;
} }
@@ -64,43 +65,65 @@ export default
} }
if(_.has(promptData, 'prompts.jobType.value.value') && _.get(promptData, 'launchConf.ask_job_type_on_launch')) { if(_.has(promptData, 'prompts.jobType.value.value') && _.get(promptData, 'launchConf.ask_job_type_on_launch')) {
schedule.job_type = promptData.prompts.jobType.templateDefault === promptData.prompts.jobType.value.value ? null : promptData.prompts.jobType.value.value; scheduleData.job_type = promptData.launchConf.defaults.job_type && promptData.launchConf.defaults.job_type === promptData.prompts.jobType.value.value ? null : promptData.prompts.jobType.value.value;
} }
if(_.has(promptData, 'prompts.tags.value') && _.get(promptData, 'launchConf.ask_tags_on_launch')){ if(_.has(promptData, 'prompts.tags.value') && _.get(promptData, 'launchConf.ask_tags_on_launch')){
let templateDefaultJobTags = promptData.prompts.tags.templateDefault.split(','); let templateDefaultJobTags = promptData.launchConf.defaults.job_tags.split(',');
schedule.job_tags = (_.isEqual(templateDefaultJobTags.sort(), promptData.prompts.tags.value.map(a => a.value).sort())) ? null : promptData.prompts.tags.value.map(a => a.value).join(); scheduleData.job_tags = (_.isEqual(templateDefaultJobTags.sort(), promptData.prompts.tags.value.map(a => a.value).sort())) ? null : promptData.prompts.tags.value.map(a => a.value).join();
} }
if(_.has(promptData, 'prompts.skipTags.value') && _.get(promptData, 'launchConf.ask_skip_tags_on_launch')){ if(_.has(promptData, 'prompts.skipTags.value') && _.get(promptData, 'launchConf.ask_skip_tags_on_launch')){
let templateDefaultSkipTags = promptData.prompts.skipTags.templateDefault.split(','); let templateDefaultSkipTags = promptData.launchConf.defaults.skip_tags.split(',');
schedule.skip_tags = (_.isEqual(templateDefaultSkipTags.sort(), promptData.prompts.skipTags.value.map(a => a.value).sort())) ? null : promptData.prompts.skipTags.value.map(a => a.value).join(); scheduleData.skip_tags = (_.isEqual(templateDefaultSkipTags.sort(), promptData.prompts.skipTags.value.map(a => a.value).sort())) ? null : promptData.prompts.skipTags.value.map(a => a.value).join();
} }
if(_.has(promptData, 'prompts.limit.value') && _.get(promptData, 'launchConf.ask_limit_on_launch')){ if(_.has(promptData, 'prompts.limit.value') && _.get(promptData, 'launchConf.ask_limit_on_launch')){
schedule.limit = promptData.prompts.limit.templateDefault === promptData.prompts.limit.value ? null : promptData.prompts.limit.value; scheduleData.limit = promptData.launchConf.defaults.limit && promptData.launchConf.defaults.limit === promptData.prompts.limit.value ? null : promptData.prompts.limit.value;
} }
if(_.has(promptData, 'prompts.verbosity.value.value') && _.get(promptData, 'launchConf.ask_verbosity_on_launch')){ if(_.has(promptData, 'prompts.verbosity.value.value') && _.get(promptData, 'launchConf.ask_verbosity_on_launch')){
schedule.verbosity = promptData.prompts.verbosity.templateDefault === promptData.prompts.verbosity.value.value ? null : promptData.prompts.verbosity.value.value; scheduleData.verbosity = promptData.launchConf.defaults.verbosity && promptData.launchConf.defaults.verbosity === promptData.prompts.verbosity.value.value ? null : promptData.prompts.verbosity.value.value;
} }
if(_.has(promptData, 'prompts.inventory.value') && _.get(promptData, 'launchConf.ask_inventory_on_launch')){ if(_.has(promptData, 'prompts.inventory.value') && _.get(promptData, 'launchConf.ask_inventory_on_launch')){
schedule.inventory = promptData.prompts.inventory.templateDefault.id === promptData.prompts.inventory.value.id ? null : promptData.prompts.inventory.value.id; scheduleData.inventory = promptData.launchConf.defaults.inventory && promptData.launchConf.defaults.inventory.id === promptData.prompts.inventory.value.id ? null : promptData.prompts.inventory.value.id;
} }
if(_.has(promptData, 'prompts.diffMode.value') && _.get(promptData, 'launchConf.ask_diff_mode_on_launch')){ if(_.has(promptData, 'prompts.diffMode.value') && _.get(promptData, 'launchConf.ask_diff_mode_on_launch')){
schedule.diff_mode = promptData.prompts.diffMode.templateDefault === promptData.prompts.diffMode.value ? null : promptData.prompts.diffMode.value; scheduleData.diff_mode = promptData.launchConf.defaults.diff_mode && promptData.launchConf.defaults.diff_mode === promptData.prompts.diffMode.value ? null : promptData.prompts.diffMode.value;
} }
// Credentials gets POST'd to a separate endpoint
// if($scope.promptData.launchConf.ask_credential_on_launch){
// jobLaunchData.credentials = [];
// promptData.credentials.value.forEach((credential) => {
// jobLaunchData.credentials.push(credential.id);
// });
// }
} }
Rest.setUrl(url); Rest.setUrl(url);
if (mode === 'add') { if (mode === 'add') {
Rest.post(schedule) Rest.post(scheduleData)
.then(() => { .then(({data}) => {
Wait('stop'); if(_.get(promptData, 'launchConf.ask_credential_on_launch')){
deferred.resolve(); // This finds the credentials that were selected in the prompt but don't occur
// in the template defaults
let credentialsToPost = promptData.prompts.credentials.value.filter(function(credFromPrompt) {
let defaultCreds = promptData.launchConf.defaults.credentials ? promptData.launchConf.defaults.credentials : [];
return !defaultCreds.some(function(defaultCred) {
return credFromPrompt.id === defaultCred.id;
});
});
let promises = [];
let schedule = new Schedule();
credentialsToPost.forEach((credentialToPost) => {
promises.push(schedule.postCredential({
id: data.id,
data: {
id: credentialToPost.id
}
}));
});
$q.all(promises)
.then(() => {
Wait('stop');
deferred.resolve();
});
} else {
Wait('stop');
deferred.resolve();
}
}) })
.catch(({data, status}) => { .catch(({data, status}) => {
ProcessErrors(scope, data, status, null, { hdr: 'Error!', ProcessErrors(scope, data, status, null, { hdr: 'Error!',
@@ -110,10 +133,62 @@ export default
}); });
} }
else { else {
Rest.put(schedule) Rest.put(scheduleData)
.then(() => { .then(({data}) => {
if(_.get(promptData, 'launchConf.ask_credential_on_launch')){
let credentialsNotInPriorCredentials = promptData.prompts.credentials.value.filter(function(credFromPrompt) {
let defaultCreds = promptData.launchConf.defaults.credentials ? promptData.launchConf.defaults.credentials : [];
return !defaultCreds.some(function(defaultCred) {
return credFromPrompt.id === defaultCred.id;
});
});
let credentialsToAdd = credentialsNotInPriorCredentials.filter(function(credNotInPrior) {
return !priorCredentials.some(function(priorCred) {
return credNotInPrior.id === priorCred.id;
});
});
let credentialsToRemove = priorCredentials.filter(function(priorCred) {
return !credentialsNotInPriorCredentials.some(function(credNotInPrior) {
return priorCred.id === credNotInPrior.id;
});
});
let promises = [];
let schedule = new Schedule();
credentialsToAdd.forEach((credentialToAdd) => {
promises.push(schedule.postCredential({
id: data.id,
data: {
id: credentialToAdd.id
}
}));
});
credentialsToRemove.forEach((credentialToRemove) => {
promises.push(schedule.postCredential({
id: data.id,
data: {
id: credentialToRemove.id,
disassociate: true
}
}));
});
$q.all(promises)
.then(() => {
Wait('stop');
deferred.resolve();
});
} else {
Wait('stop');
deferred.resolve();
}
Wait('stop'); Wait('stop');
deferred.resolve(schedule); deferred.resolve(scheduleData);
}) })
.catch(({data, status}) => { .catch(({data, status}) => {
ProcessErrors(scope, data, status, null, { hdr: 'Error!', ProcessErrors(scope, data, status, null, { hdr: 'Error!',
@@ -136,5 +211,6 @@ SchedulePost.$inject =
'ProcessErrors', 'ProcessErrors',
'RRuleToAPI', 'RRuleToAPI',
'Wait', 'Wait',
'$q' '$q',
'ScheduleModel'
]; ];

View File

@@ -104,6 +104,7 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait',
let watchForPromptChanges = () => { let watchForPromptChanges = () => {
let promptValuesToWatch = [ let promptValuesToWatch = [
'promptData.prompts.inventory.value', 'promptData.prompts.inventory.value',
'promptData.prompts.jobType.value',
'promptData.prompts.verbosity.value', 'promptData.prompts.verbosity.value',
'missingSurveyValue' 'missingSurveyValue'
]; ];

View File

@@ -5,7 +5,7 @@ function($filter, $state, $stateParams, Wait, $scope, moment,
$rootScope, $http, CreateSelect2, ParseTypeChange, ParentObject, ProcessErrors, Rest, $rootScope, $http, CreateSelect2, ParseTypeChange, ParentObject, ProcessErrors, Rest,
GetBasePath, SchedulerInit, SchedulePost, JobTemplate, $q, Empty, PromptService, RRuleToAPI) { GetBasePath, SchedulerInit, SchedulePost, JobTemplate, $q, Empty, PromptService, RRuleToAPI) {
let schedule, scheduler; let schedule, scheduler, scheduleCredentials = [];
// initial end @ midnight values // initial end @ midnight values
$scope.schedulerEndHour = "00"; $scope.schedulerEndHour = "00";
@@ -63,7 +63,8 @@ function($filter, $state, $stateParams, Wait, $scope, moment,
scheduler: scheduler, scheduler: scheduler,
mode: 'edit', mode: 'edit',
schedule: schedule, schedule: schedule,
promptData: $scope.promptData promptData: $scope.promptData,
priorCredentials: scheduleCredentials
}).then(() => { }).then(() => {
Wait('stop'); Wait('stop');
$state.go("^", null, {reload: true}); $state.go("^", null, {reload: true});
@@ -254,14 +255,14 @@ function($filter, $state, $stateParams, Wait, $scope, moment,
$q.all([jobTemplate.optionsLaunch(ParentObject.id), jobTemplate.getLaunch(ParentObject.id), Rest.get()]) $q.all([jobTemplate.optionsLaunch(ParentObject.id), jobTemplate.getLaunch(ParentObject.id), Rest.get()])
.then((responses) => { .then((responses) => {
let launchOptions = responses[0].data, let launchOptions = responses[0].data,
launchConf = responses[1].data, launchConf = responses[1].data;
scheduleCredentials = responses[2].data;
scheduleCredentials = responses[2].data.results;
let watchForPromptChanges = () => { let watchForPromptChanges = () => {
let promptValuesToWatch = [ let promptValuesToWatch = [
// credential passwords...?
'promptData.prompts.inventory.value', 'promptData.prompts.inventory.value',
'promptData.prompts.jobType.value',
'promptData.prompts.verbosity.value', 'promptData.prompts.verbosity.value',
'missingSurveyValue' 'missingSurveyValue'
]; ];
@@ -283,7 +284,23 @@ function($filter, $state, $stateParams, Wait, $scope, moment,
currentValues: data currentValues: data
}); });
prompts.credentials.value = scheduleCredentials.results.length > 0 ? scheduleCredentials.results : prompts.credentials.value; let defaultCredsWithoutOverrides = [];
prompts.credentials.value.forEach((defaultCred) => {
let typeMatches = false;
scheduleCredentials.forEach((scheduleCred) => {
if(defaultCred.credential_type === scheduleCred.credential_type) {
if((!defaultCred.vault_id && !scheduleCred.inputs.vault_id) || (defaultCred.vault_id && scheduleCred.inputs.vault_id && defaultCred.vault_id === scheduleCred.inputs.vault_id)) {
typeMatches = true;
}
}
});
if(!typeMatches) {
defaultCredsWithoutOverrides.push(defaultCred);
}
});
prompts.credentials.value = scheduleCredentials.concat(defaultCredsWithoutOverrides);
if(!launchConf.ask_variables_on_launch) { if(!launchConf.ask_variables_on_launch) {
$scope.noVars = true; $scope.noVars = true;

View File

@@ -560,7 +560,13 @@ angular.module('GeneratorHelpers', [systemStatus.name])
} }
html += "\" "; html += "\" ";
html += field.columnNgClass ? " ng-class=\"" + field.columnNgClass + "\"": ""; html += field.columnNgClass ? " ng-class=\"" + field.columnNgClass + "\"": "";
html += (options.mode === 'lookup' || options.mode === 'select') ? " ng-click=\"toggle_row(" + list.iterator + ")\"" : ""; if(options.mode === 'lookup' || options.mode === 'select') {
if (options.input_type === "radio") {
html += " ng-click=\"toggle_row(" + list.iterator + ")\"";
} else {
html += " ng-click=\"toggle_" + list.iterator + "(" + list.iterator + ", true)\"";
}
}
html += (field.columnShow) ? Attr(field, 'columnShow') : ""; html += (field.columnShow) ? Attr(field, 'columnShow') : "";
html += (field.ngBindHtml) ? "ng-bind-html=\"" + field.ngBindHtml + "\" " : ""; html += (field.ngBindHtml) ? "ng-bind-html=\"" + field.ngBindHtml + "\" " : "";
html += (field.columnClick) ? "ng-click=\"" + field.columnClick + "\" " : ""; html += (field.columnClick) ? "ng-click=\"" + field.columnClick + "\" " : "";

View File

@@ -165,3 +165,7 @@
.Prompt-credentialSubSection .select2 { .Prompt-credentialSubSection .select2 {
width: 50% !important; width: 50% !important;
} }
.Prompt-credentialTypeMissing {
margin-bottom: 20px;
color: @default-err;
}

View File

@@ -65,8 +65,6 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel',
} }
})); }));
vm.promptData.prompts.inventory.templateDefault = _.has(vm, 'promptData.launchConf.defaults.inventory') ? vm.promptData.launchConf.defaults.inventory : null;
vm.promptData.prompts.credentials.templateDefault = _.has(vm, 'promptData.launchConf.defaults.credentials') ? angular.copy(vm.promptData.launchConf.defaults.credentials) : [];
vm.promptData.prompts.credentials.passwordsNeededToStart = vm.promptData.launchConf.passwords_needed_to_start; vm.promptData.prompts.credentials.passwordsNeededToStart = vm.promptData.launchConf.passwords_needed_to_start;
vm.promptData.prompts.credentials.passwords = {}; vm.promptData.prompts.credentials.passwords = {};
@@ -99,17 +97,12 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel',
vm.promptData.prompts.credentials.passwords.vault.push(credPassObj); vm.promptData.prompts.credentials.passwords.vault.push(credPassObj);
} }
}); });
} }
}); });
vm.promptData.credentialTypeMissing = [];
vm.promptData.prompts.variables.ignore = vm.promptData.launchConf.ignore_ask_variables; vm.promptData.prompts.variables.ignore = vm.promptData.launchConf.ignore_ask_variables;
vm.promptData.prompts.verbosity.templateDefault = vm.promptData.launchConf.defaults.verbosity;
vm.promptData.prompts.jobType.templateDefault = vm.promptData.launchConf.defaults.job_type;
vm.promptData.prompts.limit.templateDefault = vm.promptData.launchConf.defaults.limit;
vm.promptData.prompts.tags.templateDefault = vm.promptData.launchConf.defaults.job_tags;
vm.promptData.prompts.skipTags.templateDefault = vm.promptData.launchConf.defaults.skip_tags;
vm.promptData.prompts.diffMode.templateDefault = vm.promptData.launchConf.defaults.diff_mode;
if(vm.promptData.launchConf.ask_inventory_on_launch) { if(vm.promptData.launchConf.ask_inventory_on_launch) {
vm.steps.inventory.includeStep = true; vm.steps.inventory.includeStep = true;

View File

@@ -27,7 +27,7 @@
<div class="Prompt-footer"> <div class="Prompt-footer">
<button class="Prompt-defaultButton" ng-click="vm.cancel()">{{:: vm.strings.get('CANCEL') }}</button> <button class="Prompt-defaultButton" ng-click="vm.cancel()">{{:: vm.strings.get('CANCEL') }}</button>
<button class="Prompt-actionButton" ng-show="vm.steps.inventory.tab._active" ng-click="vm.next(vm.steps.inventory.tab)" ng-disabled="!vm.promptData.prompts.inventory.value.id">{{:: vm.strings.get('NEXT') }}</button> <button class="Prompt-actionButton" ng-show="vm.steps.inventory.tab._active" ng-click="vm.next(vm.steps.inventory.tab)" ng-disabled="!vm.promptData.prompts.inventory.value.id">{{:: vm.strings.get('NEXT') }}</button>
<button class="Prompt-actionButton" ng-show="vm.steps.credential.tab._active" ng-click="vm.next(vm.steps.credential.tab)" ng-disabled="!vm.forms.credentialPasswords.$valid">{{:: vm.strings.get('NEXT') }}</button> <button class="Prompt-actionButton" ng-show="vm.steps.credential.tab._active" ng-click="vm.next(vm.steps.credential.tab)" ng-disabled="!vm.forms.credentialPasswords.$valid || (vm.promptData.credentialTypeMissing && vm.promptData.credentialTypeMissing.length > 0)">{{:: vm.strings.get('NEXT') }}</button>
<button class="Prompt-actionButton" ng-show="vm.steps.other_prompts.tab._active" ng-click="vm.next(vm.steps.other_prompts.tab)" ng-disabled="!vm.forms.otherPrompts.$valid">{{:: vm.strings.get('NEXT') }}</button> <button class="Prompt-actionButton" ng-show="vm.steps.other_prompts.tab._active" ng-click="vm.next(vm.steps.other_prompts.tab)" ng-disabled="!vm.forms.otherPrompts.$valid">{{:: vm.strings.get('NEXT') }}</button>
<button class="Prompt-actionButton" ng-show="vm.steps.survey.tab._active" ng-click="vm.next(vm.steps.survey.tab)" ng-disabled="!vm.forms.survey.$valid">{{:: vm.strings.get('NEXT') }}</button> <button class="Prompt-actionButton" ng-show="vm.steps.survey.tab._active" ng-click="vm.next(vm.steps.survey.tab)" ng-disabled="!vm.forms.survey.$valid">{{:: vm.strings.get('NEXT') }}</button>
<button class="Prompt-actionButton" ng-show="vm.steps.preview.tab._active" ng-click="vm.finish()" ng-bind="vm.actionText"></button> <button class="Prompt-actionButton" ng-show="vm.steps.preview.tab._active" ng-click="vm.finish()" ng-bind="vm.actionText"></button>

View File

@@ -13,7 +13,7 @@ function PromptService (Empty, $filter) {
diffMode: {} diffMode: {}
}; };
prompts.credentials.value = _.has(params, 'launchConf.defaults.credentials') ? params.launchConf.defaults.credentials : []; prompts.credentials.value = _.has(params, 'launchConf.defaults.credentials') ? _.cloneDeep(params.launchConf.defaults.credentials) : [];
prompts.inventory.value = _.has(params, 'currentValues.summary_fields.inventory') ? params.currentValues.summary_fields.inventory : (_.has(params, 'launchConf.defaults.inventory') ? params.launchConf.defaults.inventory : null); prompts.inventory.value = _.has(params, 'currentValues.summary_fields.inventory') ? params.currentValues.summary_fields.inventory : (_.has(params, 'launchConf.defaults.inventory') ? params.launchConf.defaults.inventory : null);
let skipTags = _.has(params, 'currentValues.skip_tags') && params.currentValues.skip_tags ? params.currentValues.skip_tags : (_.has(params, 'launchConf.defaults.skip_tags') ? params.launchConf.defaults.skip_tags : ""); let skipTags = _.has(params, 'currentValues.skip_tags') && params.currentValues.skip_tags ? params.currentValues.skip_tags : (_.has(params, 'launchConf.defaults.skip_tags') ? params.launchConf.defaults.skip_tags : "");

View File

@@ -88,11 +88,41 @@ export default
} }
}; };
let checkMissingCredType = (cred) => {
scope.promptData.launchConf.defaults.credentials.forEach((defaultCred) => {
if(cred.credential_type === defaultCred.credential_type) {
let credTypeLabel = "";
scope.promptData.prompts.credentials.credentialTypeOptions.forEach((credTypeOption) => {
if(credTypeOption.value === defaultCred.credential_type) {
credTypeLabel = credTypeOption.name;
}
});
if(scope.promptData.prompts.credentials.credentialTypes[cred.credential_type] === "vault") {
if((_.get(cred, 'inputs.vault_id') ? _.get(cred, 'inputs.vault_id') : _.get(cred, 'vault_id')) === _.get(defaultCred, 'vault_id')) {
scope.promptData.credentialTypeMissing.push({
credential_type: defaultCred.credential_type,
vault_id: defaultCred.vault_id,
label: defaultCred.vault_id ? `${credTypeLabel} (id: ${defaultCred.vault_id})` : credTypeLabel
});
}
} else {
scope.promptData.credentialTypeMissing.push({
credential_type: defaultCred.credential_type,
label: credTypeLabel
});
}
}
});
};
vm.init = (_scope_, _launch_) => { vm.init = (_scope_, _launch_) => {
scope = _scope_; scope = _scope_;
launch = _launch_; launch = _launch_;
scope.toggle_row = (selectedRow) => { scope.toggle_row = (selectedRow) => {
let selectedCred = _.cloneDeep(selectedRow);
for (let i = scope.promptData.prompts.credentials.value.length - 1; i >= 0; i--) { for (let i = scope.promptData.prompts.credentials.value.length - 1; i >= 0; i--) {
if(scope.promptData.prompts.credentials.value[i].credential_type === parseInt(scope.promptData.prompts.credentials.credentialKind)) { if(scope.promptData.prompts.credentials.value[i].credential_type === parseInt(scope.promptData.prompts.credentials.credentialKind)) {
wipePasswords(scope.promptData.prompts.credentials.value[i]); wipePasswords(scope.promptData.prompts.credentials.value[i]);
@@ -100,8 +130,15 @@ export default
} }
} }
scope.promptData.prompts.credentials.value.push(_.cloneDeep(selectedRow)); scope.promptData.prompts.credentials.value.push(selectedCred);
updateNeededPasswords(selectedRow); updateNeededPasswords(selectedRow);
for (let i = scope.promptData.credentialTypeMissing.length - 1; i >= 0; i--) {
if(scope.promptData.credentialTypeMissing[i].credential_type === selectedRow.credential_type) {
scope.promptData.credentialTypeMissing.splice(i,1);
i = -1;
}
}
}; };
scope.toggle_credential = (cred) => { scope.toggle_credential = (cred) => {
@@ -146,6 +183,18 @@ export default
if(!uncheck) { if(!uncheck) {
scope.promptData.prompts.credentials.value.push(cred); scope.promptData.prompts.credentials.value.push(cred);
updateNeededPasswords(cred); updateNeededPasswords(cred);
for (let i = scope.promptData.credentialTypeMissing.length - 1; i >= 0; i--) {
if(scope.promptData.credentialTypeMissing[i].credential_type === cred.credential_type) {
if(_.get(cred, 'inputs.vault_id') === _.get(scope.promptData.credentialTypeMissing[i], 'vault_id')) {
scope.promptData.credentialTypeMissing.splice(i,1);
i = -1;
}
}
}
} else {
if(scope.promptData.launchConf.defaults.credentials && scope.promptData.launchConf.defaults.credentials.length > 0) {
checkMissingCredType(cred);
}
} }
}; };
@@ -200,6 +249,9 @@ export default
vm.deleteSelectedCredential = (credentialToDelete) => { vm.deleteSelectedCredential = (credentialToDelete) => {
for (let i = scope.promptData.prompts.credentials.value.length - 1; i >= 0; i--) { for (let i = scope.promptData.prompts.credentials.value.length - 1; i >= 0; i--) {
if(scope.promptData.prompts.credentials.value[i].id === credentialToDelete.id) { if(scope.promptData.prompts.credentials.value[i].id === credentialToDelete.id) {
if(scope.promptData.launchConf.defaults.credentials && scope.promptData.launchConf.defaults.credentials.length > 0) {
checkMissingCredType(credentialToDelete);
}
wipePasswords(credentialToDelete); wipePasswords(credentialToDelete);
scope.promptData.prompts.credentials.value.splice(i, 1); scope.promptData.prompts.credentials.value.splice(i, 1);
} }
@@ -213,7 +265,7 @@ export default
}; };
vm.revert = () => { vm.revert = () => {
scope.promptData.prompts.credentials.value = scope.promptData.prompts.credentials.templateDefault; scope.promptData.prompts.credentials.value = _.has(scope, 'promptData.launchConf.defaults.credentials') ? _.cloneDeep(scope.promptData.launchConf.defaults.credentials) : [];
scope.promptData.prompts.credentials.passwords = { scope.promptData.prompts.credentials.passwords = {
vault: [] vault: []
}; };
@@ -242,13 +294,15 @@ export default
} }
}); });
scope.promptData.credentialTypeMissing = [];
}; };
vm.showRevertCredentials = () => { vm.showRevertCredentials = () => {
if(scope.promptData.launchConf.ask_credential_on_launch) { if(scope.promptData.launchConf.ask_credential_on_launch) {
if(scope.promptData.prompts.credentials.value && scope.promptData.prompts.credentials.templateDefault && (scope.promptData.prompts.credentials.value.length === scope.promptData.prompts.credentials.templateDefault.length)) { if(scope.promptData.prompts.credentials.value && _.has(scope, 'promptData.launchConf.defaults.credentials') && (scope.promptData.prompts.credentials.value.length === scope.promptData.launchConf.defaults.credentials.length)) {
let selectedIds = scope.promptData.prompts.credentials.value.map((x) => { return x.id; }).sort(); let selectedIds = scope.promptData.prompts.credentials.value.map((x) => { return x.id; }).sort();
let defaultIds = scope.promptData.prompts.credentials.templateDefault.map((x) => { return x.id; }).sort(); let defaultIds = _.has(scope, 'promptData.launchConf.defaults.credentials') ? scope.promptData.launchConf.defaults.credentials.map((x) => { return x.id; }).sort() : [];
return !selectedIds.every((e, i) => { return defaultIds.indexOf(e) === i; }); return !selectedIds.every((e, i) => { return defaultIds.indexOf(e) === i; });
} else { } else {
return true; return true;

View File

@@ -1,11 +1,11 @@
<div> <div>
<div class="Prompt-selectedItem"> <div class="Prompt-selectedItem">
<div class="Prompt-selectedItemInfo" ng-hide="promptData.prompts.credentials.templateDefault.length === 0 && promptData.prompts.credentials.value.length === 0"> <div class="Prompt-selectedItemInfo" ng-hide="(!promptData.launchConf.defaults.credentials || promptData.launchConf.defaults.credentials.length === 0) && promptData.prompts.credentials.value.length === 0">
<div class="Prompt-selectedItemLabel"> <div class="Prompt-selectedItemLabel">
<span>{{:: vm.strings.get('prompt.SELECTED') }}</span> <span>{{:: vm.strings.get('prompt.SELECTED') }}</span>
</div> </div>
<div class="Prompt-previewTags--outer"> <div class="Prompt-previewTags--outer">
<div ng-show="promptData.prompts.credentials.templateDefault.length > 0 && promptData.prompts.credentials.value.length === 0" class="Prompt-noSelectedItem">{{:: vm.strings.get('prompt.NO_CREDENTIALS_SELECTED') }}</div> <div ng-show="promptData.launchConf.defaults.credentials && promptData.launchConf.defaults.credentials.length > 0 && promptData.prompts.credentials.value.length === 0" class="Prompt-noSelectedItem">{{:: vm.strings.get('prompt.NO_CREDENTIALS_SELECTED') }}</div>
<div class="Prompt-previewTags--inner"> <div class="Prompt-previewTags--inner">
<div class="MultiCredential-tagContainer" <div class="MultiCredential-tagContainer"
ng-class="{'MultiCredential-tagContainer--disabled': !promptData.launchConf.ask_credential_on_launch}" ng-class="{'MultiCredential-tagContainer--disabled': !promptData.launchConf.ask_credential_on_launch}"
@@ -39,6 +39,11 @@
</div> </div>
</div> </div>
</div> </div>
<div ng-if="promptData.credentialTypeMissing && promptData.credentialTypeMissing.length > 0" class="Prompt-credentialTypeMissing">
<div ng-repeat="missingCred in promptData.credentialTypeMissing">
<span class="fa fa-warning"></span>&nbsp;{{:: vm.strings.get('prompt.CREDENTIAL_TYPE_MISSING', missingCred.label) }}
</div>
</div>
<span ng-show="promptData.launchConf.ask_credential_on_launch"> <span ng-show="promptData.launchConf.ask_credential_on_launch">
<div class="Prompt-credentialSubSection"> <div class="Prompt-credentialSubSection">
<span class="Prompt-label">{{:: vm.strings.get('prompt.CREDENTIAL_TYPE') }}:</span> <span class="Prompt-label">{{:: vm.strings.get('prompt.CREDENTIAL_TYPE') }}:</span>

View File

@@ -31,7 +31,7 @@ export default
}; };
vm.revert = () => { vm.revert = () => {
scope.promptData.prompts.inventory.value = scope.promptData.prompts.inventory.templateDefault; scope.promptData.prompts.inventory.value = scope.promptData.launchConf.defaults.inventory;
}; };
} }
]; ];

View File

@@ -1,11 +1,11 @@
<div> <div>
<div class="Prompt-selectedItem"> <div class="Prompt-selectedItem">
<div class="Prompt-selectedItemInfo" ng-hide="!promptData.prompts.inventory.value.id && !promptData.prompts.inventory.templateDefault.id"> <div class="Prompt-selectedItemInfo" ng-hide="!promptData.prompts.inventory.value.id && !promptData.launchConf.defaults.inventory.id">
<div class="Prompt-selectedItemLabel"> <div class="Prompt-selectedItemLabel">
<span>{{:: vm.strings.get('prompt.SELECTED') }}</span> <span>{{:: vm.strings.get('prompt.SELECTED') }}</span>
</div> </div>
<div class="Prompt-previewTags--outer"> <div class="Prompt-previewTags--outer">
<div ng-show="promptData.prompts.inventory.templateDefault.id && !promptData.prompts.inventory.value.id" class="Prompt-noSelectedItem">{{:: vm.strings.get('prompt.NO_INVENTORY_SELECTED') }}</div> <div ng-show="promptData.launchConf.defaults.inventory.id && !promptData.prompts.inventory.value.id" class="Prompt-noSelectedItem">{{:: vm.strings.get('prompt.NO_INVENTORY_SELECTED') }}</div>
<div class="Prompt-previewTags--inner" ng-hide="!promptData.prompts.inventory.value.id"> <div class="Prompt-previewTags--inner" ng-hide="!promptData.prompts.inventory.value.id">
<div class="Prompt-previewTagContainer"> <div class="Prompt-previewTagContainer">
<div class="Prompt-previewTag Prompt-previewTag--deletable"> <div class="Prompt-previewTag Prompt-previewTag--deletable">
@@ -18,7 +18,7 @@
</div> </div>
</div> </div>
<div class="Prompt-previewTagRevert"> <div class="Prompt-previewTagRevert">
<a class="Prompt-revertLink" href="" ng-hide="promptData.prompts.inventory.value.id === promptData.prompts.inventory.templateDefault.id" ng-click="vm.revert()">{{:: vm.strings.get('prompt.REVERT') }}</a> <a class="Prompt-revertLink" href="" ng-hide="promptData.prompts.inventory.value.id === promptData.launchConf.defaults.inventory.id" ng-click="vm.revert()">{{:: vm.strings.get('prompt.REVERT') }}</a>
</div> </div>
</div> </div>
</div> </div>