Merge pull request #1425 from mabashian/169-credentials

Added add/replace credential validation on jt launch and schedule
This commit is contained in:
Michael Abashian
2018-03-08 10:57:27 -05:00
committed by GitHub
16 changed files with 273 additions and 67 deletions

View File

@@ -121,5 +121,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

@@ -56,6 +56,7 @@ function TemplatesStrings (BaseString) {
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 = {
@@ -86,7 +87,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(); const 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(','); const 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(','); const 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
const credentialsToPost = promptData.prompts.credentials.value.filter(function(credFromPrompt) {
const defaultCreds = promptData.launchConf.defaults.credentials ? promptData.launchConf.defaults.credentials : [];
return !defaultCreds.some(function(defaultCred) {
return credFromPrompt.id === defaultCred.id;
});
});
const promises = [];
const 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')){
const credentialsNotInPriorCredentials = promptData.prompts.credentials.value.filter(function(credFromPrompt) {
const defaultCreds = promptData.launchConf.defaults.credentials ? promptData.launchConf.defaults.credentials : [];
return !defaultCreds.some(function(defaultCred) {
return credFromPrompt.id === defaultCred.id;
});
});
const credentialsToAdd = credentialsNotInPriorCredentials.filter(function(credNotInPrior) {
return !priorCredentials.some(function(priorCred) {
return credNotInPrior.id === priorCred.id;
});
});
const credentialsToRemove = priorCredentials.filter(function(priorCred) {
return !credentialsNotInPriorCredentials.some(function(credNotInPrior) {
return priorCred.id === credNotInPrior.id;
});
});
const promises = [];
const 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,33 @@ 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 = [];
const credentialHasScheduleOverride = (templateDefaultCred) => {
let credentialHasOverride = false;
scheduleCredentials.forEach((scheduleCred) => {
if(templateDefaultCred.credential_type === scheduleCred.credential_type) {
if(
(!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) ||
(templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id)
) {
credentialHasOverride = true;
}
}
});
return credentialHasOverride;
};
if(_.has(launchConf, 'defaults.credentials')) {
launchConf.defaults.credentials.forEach((defaultCred) => {
if(!credentialHasScheduleOverride(defaultCred)) {
defaultCredsWithoutOverrides.push(defaultCred);
}
});
}
prompts.credentials.value = defaultCredsWithoutOverrides.concat(scheduleCredentials);
if(!launchConf.ask_variables_on_launch) { if(!launchConf.ask_variables_on_launch) {
$scope.noVars = true; $scope.noVars = true;

View File

@@ -565,7 +565,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,17 @@ export default
if(!uncheck) { if(!uncheck) {
scope.promptData.prompts.credentials.value.push(cred); scope.promptData.prompts.credentials.value.push(cred);
updateNeededPasswords(cred); updateNeededPasswords(cred);
_.remove(scope.promptData.credentialTypeMissing, (missingCredType) => {
return (
missingCredType.credential_type === cred.credential_type &&
_.get(cred, 'inputs.vault_id') === _.get(missingCredType, 'vault_id')
);
});
} else {
if(scope.promptData.launchConf.defaults.credentials && scope.promptData.launchConf.defaults.credentials.length > 0) {
checkMissingCredType(cred);
}
} }
}; };
@@ -200,6 +248,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 +264,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 +293,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>