Merge branch 'release_3.2.0' into devel

This commit is contained in:
Matthew Jones
2017-09-18 10:55:45 -04:00
48 changed files with 838 additions and 403 deletions

View File

@@ -100,7 +100,7 @@ export default ['i18n', function(i18n) {
},
save: {
ngClick: 'vm.formSave()',
ngDisabled: "license_type !== 'enterprise' || form.$invalid || form.$pending"
ngDisabled: "license_type !== 'enterprise' && license_type !== 'open' || configuration_ldap_template_form.$invalid || configuration_ldap_template_form.$pending"
}
}
};

View File

@@ -39,7 +39,7 @@ export default ['i18n', function(i18n) {
},
save: {
ngClick: 'vm.formSave()',
ngDisabled: "license_type !== 'enterprise' || form.$invalid || form.$pending"
ngDisabled: "license_type !== 'enterprise' && license_type !== 'open' || configuration_radius_template_form.$invalid || configuration_radius_template_form.$pending"
}
}
};

View File

@@ -92,7 +92,7 @@ export default ['i18n', function(i18n) {
},
save: {
ngClick: 'vm.formSave()',
ngDisabled: "license_type !== 'enterprise' || form.$invalid || form.$pending"
ngDisabled: "license_type !== 'enterprise' && license_type !== 'open' || configuration_saml_template_form.$invalid || configuration_saml_template_form.$pending"
}
}
};

View File

@@ -52,7 +52,7 @@ export default ['i18n', function(i18n) {
},
save: {
ngClick: 'vm.formSave()',
ngDisabled: "license_type !== 'enterprise' || form.$invalid || form.$pending"
ngDisabled: "license_type !== 'enterprise' && license_type !== 'open' || configuration_tacacs_template_form.$invalid || configuration_tacacs_template_form.$pending"
}
}
};

View File

@@ -95,7 +95,11 @@ export default [
} else {
if (key === "LICENSE") {
$scope.license_type = data[key].license_type;
if (_.isEmpty(data[key])) {
$scope.license_type = "open";
} else {
$scope.license_type = data[key].license_type;
}
}
//handle nested objects
if(ConfigurationUtils.isEmpty(data[key])) {

View File

@@ -19,7 +19,10 @@
</div>
<div class="Modal-body">
<div>
<div class="Prompt-bodyQuery"><span translate>Are you sure you want to disassociate the host below from</span> {{disassociateGroup.name}}?</div>
<div class="Prompt-bodyQuery">
<span translate>Are you sure you want to disassociate the host below from</span> {{disassociateGroup.name}}?<br /><br />
<span translate>Note that only hosts directly in this group can be disassociated. Hosts in sub-groups must be disassociated directly from the sub-group level that they belong.</span>
</div>
<div class="Prompt-bodyTarget">{{ host.name }}</div>
</div>
<div class="Modal-footer">

View File

@@ -81,9 +81,12 @@
<label class="JobResults-resultRowLabel" translate>
Explanation
</label>
<div class="JobResults-resultRowText" ng-show="!previousTaskFailed">
{{ job.job_explanation }}
</div>
<div class="JobResults-resultRowText">
{{task_detail | limitTo:explanationLimit}}
<span ng-show="explanationLimit && task_detail.length > explanationLimit">
<span ng-show="previousTaskFailed && explanationLimit && task_detail.length > explanationLimit">
<span>... </span>
<span class="JobResults-seeMoreLess" ng-click="explanationLimit=undefined">Show More</span>
</span>

View File

@@ -44,16 +44,7 @@ export default
Rest.get()
.success(function (data) {
if(params.updateAllSources) {
let userCanUpdateAllSources = true;
_.forEach(data, function(inventory_source){
if (!inventory_source.can_update) {
userCanUpdateAllSources = false;
}
});
if(userCanUpdateAllSources) {
scope.$emit('StartTheUpdate', {});
}
scope.$emit('StartTheUpdate', {});
}
else {
inventory_source = data;
@@ -67,7 +58,7 @@ export default
}
} else {
Wait('stop');
Alert('Permission Denied', 'You do not have access to run the inventory sync. Please contact your system administrator.',
Alert('Error Launching Sync', 'Unable to execute the inventory sync. Please contact your system administrator.',
'alert-danger');
}
}

View File

@@ -54,9 +54,16 @@ export default ['$scope', '$rootScope', '$location', '$stateParams',
.catch(({data, status}) => {
ProcessErrors($scope, data, status, form, {
hdr: 'Error!',
msg: 'Failed to add new organization. Post returned status: ' + status
msg: 'Failed to save instance groups. POST returned status: ' + status
});
});
})
.catch(({data, status}) => {
let explanation = _.has(data, "name") ? data.name[0] : "";
ProcessErrors($scope, data, status, OrganizationForm, {
hdr: 'Error!',
msg: `Failed to save organization. PUT status: ${status}. ${explanation}`
});
});
};

View File

@@ -120,6 +120,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
if (action) {
action();
}
$('.modal-backdrop').remove();
});
$('#alert-modal').on('shown.bs.modal', function() {
$('#alert_ok_btn').focus();

View File

@@ -29,10 +29,8 @@ export default ['templateUrl', function(templateUrl) {
$scope.init = function() {
let list = $scope.list;
if($state.params.selected) {
$scope.currentSelection = {
name: null,
id: parseInt($state.params.selected)
};
let selection = $scope[list.name].find(({id}) => id === parseInt($state.params.selected));
$scope.currentSelection = _.pick(selection, 'id', 'name');
}
$scope.$watch(list.name, function(){
selectRowIfPresent();

View File

@@ -51,7 +51,7 @@
.Paginate-total {
display: flex;
align-items: flex-end;
margin-bottom: -2px;
margin: 0 0 -2px 10px;
}
.Paginate-filterLabel{

View File

@@ -32,7 +32,7 @@
.SmartSearch-searchTermContainer {
flex: initial;
width: 100%;
flex-grow: 1;
border: 1px solid @b7grey;
border-radius: 4px;
display: flex;
@@ -167,7 +167,7 @@
color: @default-interface-txt;
border: 1px solid @b7grey;
cursor: pointer;
width: 70px;
min-width: 70px;
height: 34px;
line-height: 20px;
}
@@ -240,15 +240,6 @@
margin-right: 5px;
}
// Additional modal specific styles
.modal-body, #add-permissions-modal,
.JobResults {
.SmartSearch-searchTermContainer {
width: 100%;
}
}
@media (max-width: 700px) {
.SmartSearch-bar {
width: 100%;

View File

@@ -161,10 +161,15 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', '
terms = (terms) ? terms.trim() : "";
if(terms && terms !== '') {
// Split the terms up
let splitTerms = SmartSearchService.splitSearchIntoTerms(terms);
_.forEach(splitTerms, (term) => {
let splitTerms;
if ($scope.singleSearchParam === 'host_filter') {
splitTerms = SmartSearchService.splitFilterIntoTerms(terms);
} else {
splitTerms = SmartSearchService.splitSearchIntoTerms(terms);
}
_.forEach(splitTerms, (term) => {
let termParts = SmartSearchService.splitTermIntoParts(term);
function combineSameSearches(a,b){

View File

@@ -1,5 +1,45 @@
export default [function() {
return {
/**
* For the Smart Host Filter, values with spaces are wrapped with double quotes on input.
* To avoid having these quoted values split up and treated as terms themselves, some
* work is done to encode quotes in quoted values and the spaces within those quoted
* values before calling to `splitSearchIntoTerms`.
*/
splitFilterIntoTerms (searchString) {
if (!searchString) {
return null;
}
let groups = [];
let quoted;
searchString.split(' ').forEach(substring => {
if (/:"/g.test(substring)) {
if (/"$/.test(substring)) {
groups.push(this.encode(substring));
} else {
quoted = substring;
}
} else if (quoted) {
quoted += ` ${substring}`;
if (/"/g.test(substring)) {
groups.push(this.encode(quoted));
quoted = undefined;
}
} else {
groups.push(substring);
}
});
return this.splitSearchIntoTerms(groups.join(' '));
},
encode (string) {
string = string.replace(/'/g, '%27');
return string.replace(/("| )/g, match => encodeURIComponent(match));
},
splitSearchIntoTerms(searchString) {
return searchString.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g);
},

View File

@@ -35,6 +35,7 @@ export default
self.socket.onopen = function () {
$log.debug("Websocket connection opened.");
socketPromise.resolve();
console.log('promise resolved, and readyState: '+ self.readyState);
self.checkStatus();
if(needsResubscribing){
self.subscribe(self.getLast());
@@ -116,6 +117,8 @@ export default
disconnect: function(){
if(this.socket){
this.socket.close();
delete this.socket;
console.log("Socket deleted: "+this.socket);
}
},
subscribe: function(state){
@@ -186,14 +189,20 @@ export default
var self = this;
$log.debug('Sent to Websocket Server: ' + data);
socketPromise.promise.then(function(){
self.socket.send(data, function () {
var args = arguments;
self.scope.$apply(function () {
if (callback) {
callback.apply(self.socket, args);
}
console.log("socket readyState at emit: " + self.socket.readyState);
// if(self.socket.readyState === 0){
// self.subscribe(self.getLast());
// }
if(self.socket.readyState === 1){
self.socket.send(data, function () {
var args = arguments;
self.scope.$apply(function () {
if (callback) {
callback.apply(self.socket, args);
}
});
});
});
}
});
},
addStateResolve: function(state, id){

View File

@@ -37,6 +37,7 @@
$scope.mode = "add";
$scope.parseType = 'yaml';
$scope.credentialNotPresent = false;
$scope.canGetAllRelatedResources = true;
md5Setup({
scope: $scope,

View File

@@ -18,13 +18,15 @@ export default
'Empty', 'Prompt', 'ToJSON', 'GetChoices', 'CallbackHelpInit',
'InitiatePlaybookRun' , 'initSurvey', '$state', 'CreateSelect2',
'ToggleNotification','$q', 'InstanceGroupsService', 'InstanceGroupsData', 'MultiCredentialService', 'availableLabels',
'canGetProject', 'canGetInventory', 'jobTemplateData', 'ParseVariableString',
function(
$filter, $scope, $rootScope,
$location, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert,
ProcessErrors, GetBasePath, md5Setup,
ParseTypeChange, Wait, selectedLabels, i18n,
Empty, Prompt, ToJSON, GetChoices, CallbackHelpInit, InitiatePlaybookRun, SurveyControllerInit, $state,
CreateSelect2, ToggleNotification, $q, InstanceGroupsService, InstanceGroupsData, MultiCredentialService, availableLabels
CreateSelect2, ToggleNotification, $q, InstanceGroupsService, InstanceGroupsData, MultiCredentialService, availableLabels,
canGetProject, canGetInventory, jobTemplateData, ParseVariableString
) {
$scope.$watch('job_template_obj.summary_fields.user_capabilities.edit', function(val) {
@@ -47,6 +49,7 @@ export default
function init() {
CallbackHelpInit({ scope: $scope });
$scope.playbook_options = null;
$scope.playbook = null;
$scope.mode = 'edit';
@@ -253,7 +256,175 @@ export default
$scope.rmoveLoadJobs();
}
$scope.removeLoadJobs = $scope.$on('LoadJobs', function() {
$scope.fillJobTemplate();
$scope.job_template_obj = jobTemplateData;
$scope.name = jobTemplateData.name;
var fld, i;
for (fld in form.fields) {
if (fld !== 'variables' && fld !== 'survey' && fld !== 'forks' && jobTemplateData[fld] !== null && jobTemplateData[fld] !== undefined) {
if (form.fields[fld].type === 'select') {
if ($scope[fld + '_options'] && $scope[fld + '_options'].length > 0) {
for (i = 0; i < $scope[fld + '_options'].length; i++) {
if (jobTemplateData[fld] === $scope[fld + '_options'][i].value) {
$scope[fld] = $scope[fld + '_options'][i];
}
}
} else {
$scope[fld] = jobTemplateData[fld];
}
} else {
$scope[fld] = jobTemplateData[fld];
if(!Empty(jobTemplateData.summary_fields.survey)) {
$scope.survey_exists = true;
}
}
master[fld] = $scope[fld];
}
if (fld === 'forks') {
if (jobTemplateData[fld] !== 0) {
$scope[fld] = jobTemplateData[fld];
master[fld] = $scope[fld];
}
}
if (fld === 'variables') {
// Parse extra_vars, converting to YAML.
$scope.variables = ParseVariableString(jobTemplateData.extra_vars);
master.variables = $scope.variables;
}
if (form.fields[fld].type === 'lookup' && jobTemplateData.summary_fields[form.fields[fld].sourceModel]) {
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
jobTemplateData.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField];
}
if (form.fields[fld].type === 'checkbox_group') {
for(var j=0; j<form.fields[fld].fields.length; j++) {
$scope[form.fields[fld].fields[j].name] = jobTemplateData[form.fields[fld].fields[j].name];
}
}
}
Wait('stop');
$scope.url = jobTemplateData.url;
$scope.survey_enabled = jobTemplateData.survey_enabled;
$scope.ask_variables_on_launch = (jobTemplateData.ask_variables_on_launch) ? true : false;
master.ask_variables_on_launch = $scope.ask_variables_on_launch;
$scope.ask_verbosity_on_launch = (jobTemplateData.ask_verbosity_on_launch) ? true : false;
master.ask_verbosity_on_launch = $scope.ask_verbosity_on_launch;
$scope.ask_limit_on_launch = (jobTemplateData.ask_limit_on_launch) ? true : false;
master.ask_limit_on_launch = $scope.ask_limit_on_launch;
$scope.ask_tags_on_launch = (jobTemplateData.ask_tags_on_launch) ? true : false;
master.ask_tags_on_launch = $scope.ask_tags_on_launch;
$scope.ask_skip_tags_on_launch = (jobTemplateData.ask_skip_tags_on_launch) ? true : false;
master.ask_skip_tags_on_launch = $scope.ask_skip_tags_on_launch;
$scope.ask_diff_mode_on_launch = (jobTemplateData.ask_diff_mode_on_launch) ? true : false;
master.ask_diff_mode_on_launch = $scope.ask_diff_mode_on_launch;
$scope.job_tag_options = (jobTemplateData.job_tags) ? jobTemplateData.job_tags.split(',')
.map((i) => ({name: i, label: i, value: i})) : [];
$scope.job_tags = $scope.job_tag_options;
master.job_tags = $scope.job_tags;
$scope.skip_tag_options = (jobTemplateData.skip_tags) ? jobTemplateData.skip_tags.split(',')
.map((i) => ({name: i, label: i, value: i})) : [];
$scope.skip_tags = $scope.skip_tag_options;
master.skip_tags = $scope.skip_tags;
$scope.ask_job_type_on_launch = (jobTemplateData.ask_job_type_on_launch) ? true : false;
master.ask_job_type_on_launch = $scope.ask_job_type_on_launch;
$scope.ask_inventory_on_launch = (jobTemplateData.ask_inventory_on_launch) ? true : false;
master.ask_inventory_on_launch = $scope.ask_inventory_on_launch;
$scope.ask_credential_on_launch = (jobTemplateData.ask_credential_on_launch) ? true : false;
master.ask_credential_on_launch = $scope.ask_credential_on_launch;
if (jobTemplateData.host_config_key) {
$scope.example_config_key = jobTemplateData.host_config_key;
}
$scope.example_template_id = id;
$scope.setCallbackHelp();
$scope.callback_url = $scope.callback_server_path + ((jobTemplateData.related.callback) ? jobTemplateData.related.callback :
GetBasePath('job_templates') + id + '/callback/');
master.callback_url = $scope.callback_url;
$scope.can_edit = jobTemplateData.summary_fields.user_capabilities.edit;
if($scope.job_template_obj.summary_fields.user_capabilities.edit) {
MultiCredentialService.loadCredentials(jobTemplateData)
.then(([selectedCredentials, credTypes, credTypeOptions,
credTags, credentialGetPermissionDenied]) => {
$scope.canGetAllRelatedResources = canGetProject && canGetInventory && !credentialGetPermissionDenied ? true : false;
$scope.selectedCredentials = selectedCredentials;
$scope.credential_types = credTypes;
$scope.credentialTypeOptions = credTypeOptions;
$scope.credentialsToPost = credTags;
$scope.$emit('jobTemplateLoaded', master);
});
}
else {
if (jobTemplateData.summary_fields.credential) {
$scope.selectedCredentials.machine = jobTemplateData.summary_fields.credential;
}
if (jobTemplateData.summary_fields.vault_credential) {
$scope.selectedCredentials.vault = jobTemplateData.summary_fields.vault_credential;
}
if (jobTemplateData.summary_fields.extra_credentials) {
$scope.selectedCredentials.extra = jobTemplateData.summary_fields.extra_credentials;
}
MultiCredentialService.getCredentialTypes()
.then(({credential_types, credentialTypeOptions}) => {
let typesArray = Object.keys(credential_types).map(key => credential_types[key]);
let credTypeOptions = credentialTypeOptions;
let machineAndVaultCreds = [],
extraCreds = [];
if($scope.selectedCredentials.machine) {
machineAndVaultCreds.push($scope.selectedCredentials.machine);
}
if($scope.selectedCredentials.vault) {
machineAndVaultCreds.push($scope.selectedCredentials.vault);
}
machineAndVaultCreds.map(cred => ({
name: cred.name,
id: cred.id,
postType: cred.postType,
kind: typesArray
.filter(type => {
return cred.kind === type.kind || parseInt(cred.credential_type) === type.value;
})[0].name + ":"
}));
if($scope.selectedCredentials.extra && $scope.selectedCredentials.extra.length > 0) {
extraCreds = extraCreds.concat($scope.selectedCredentials.extra).map(cred => ({
name: cred.name,
id: cred.id,
postType: cred.postType,
kind: credTypeOptions
.filter(type => {
return parseInt(cred.credential_type_id) === type.value;
})[0].name + ":"
}));
}
$scope.credentialsToPost = machineAndVaultCreds.concat(extraCreds);
$scope.$emit('jobTemplateLoaded', master);
});
}
});
if ($scope.removeChoicesReady) {

View File

@@ -2,14 +2,7 @@ export default
function CallbackHelpInit($q, $location, GetBasePath, Rest, JobTemplateForm, GenerateForm, $stateParams, ProcessErrors,
ParseVariableString, Empty, Wait, MultiCredentialService, $rootScope) {
return function(params) {
var scope = params.scope,
defaultUrl = GetBasePath('job_templates'),
// generator = GenerateForm,
form = JobTemplateForm(),
// loadingFinishedCount = 0,
// base = $location.path().replace(/^\//, '').split('/')[0],
master = {},
id = $stateParams.job_template_id;
var scope = params.scope;
// checkSCMStatus, getPlaybooks, callback,
// choicesCount = 0;
@@ -44,211 +37,6 @@ export default
scope.example_config_key = '5a8ec154832b780b9bdef1061764ae5a';
scope.example_template_id = 'N';
scope.setCallbackHelp();
// this fills the job template form both on copy of the job template
// and on edit
scope.fillJobTemplate = function(){
// id = id || $rootScope.copy.id;
// Retrieve detail record and prepopulate the form
Rest.setUrl(defaultUrl + id);
Rest.get()
.success(function (data) {
scope.job_template_obj = data;
scope.name = data.name;
var fld, i;
for (fld in form.fields) {
if (fld !== 'variables' && fld !== 'survey' && fld !== 'forks' && data[fld] !== null && data[fld] !== undefined) {
if (form.fields[fld].type === 'select') {
if (scope[fld + '_options'] && scope[fld + '_options'].length > 0) {
for (i = 0; i < scope[fld + '_options'].length; i++) {
if (data[fld] === scope[fld + '_options'][i].value) {
scope[fld] = scope[fld + '_options'][i];
}
}
} else {
scope[fld] = data[fld];
}
} else {
scope[fld] = data[fld];
if(!Empty(data.summary_fields.survey)) {
scope.survey_exists = true;
}
}
master[fld] = scope[fld];
}
if (fld === 'forks') {
if (data[fld] !== 0) {
scope[fld] = data[fld];
master[fld] = scope[fld];
}
}
if (fld === 'variables') {
// Parse extra_vars, converting to YAML.
scope.variables = ParseVariableString(data.extra_vars);
master.variables = scope.variables;
}
if (form.fields[fld].type === 'lookup' && data.summary_fields[form.fields[fld].sourceModel]) {
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField];
}
if (form.fields[fld].type === 'checkbox_group') {
for(var j=0; j<form.fields[fld].fields.length; j++) {
scope[form.fields[fld].fields[j].name] = data[form.fields[fld].fields[j].name];
}
}
}
Wait('stop');
scope.url = data.url;
scope.survey_enabled = data.survey_enabled;
scope.ask_variables_on_launch = (data.ask_variables_on_launch) ? true : false;
master.ask_variables_on_launch = scope.ask_variables_on_launch;
scope.ask_verbosity_on_launch = (data.ask_verbosity_on_launch) ? true : false;
master.ask_verbosity_on_launch = scope.ask_verbosity_on_launch;
scope.ask_limit_on_launch = (data.ask_limit_on_launch) ? true : false;
master.ask_limit_on_launch = scope.ask_limit_on_launch;
scope.ask_tags_on_launch = (data.ask_tags_on_launch) ? true : false;
master.ask_tags_on_launch = scope.ask_tags_on_launch;
scope.ask_skip_tags_on_launch = (data.ask_skip_tags_on_launch) ? true : false;
master.ask_skip_tags_on_launch = scope.ask_skip_tags_on_launch;
scope.ask_diff_mode_on_launch = (data.ask_diff_mode_on_launch) ? true : false;
master.ask_diff_mode_on_launch = scope.ask_diff_mode_on_launch;
scope.job_tag_options = (data.job_tags) ? data.job_tags.split(',')
.map((i) => ({name: i, label: i, value: i})) : [];
scope.job_tags = scope.job_tag_options;
master.job_tags = scope.job_tags;
scope.skip_tag_options = (data.skip_tags) ? data.skip_tags.split(',')
.map((i) => ({name: i, label: i, value: i})) : [];
scope.skip_tags = scope.skip_tag_options;
master.skip_tags = scope.skip_tags;
scope.ask_job_type_on_launch = (data.ask_job_type_on_launch) ? true : false;
master.ask_job_type_on_launch = scope.ask_job_type_on_launch;
scope.ask_inventory_on_launch = (data.ask_inventory_on_launch) ? true : false;
master.ask_inventory_on_launch = scope.ask_inventory_on_launch;
scope.ask_credential_on_launch = (data.ask_credential_on_launch) ? true : false;
master.ask_credential_on_launch = scope.ask_credential_on_launch;
if (data.host_config_key) {
scope.example_config_key = data.host_config_key;
}
scope.example_template_id = id;
scope.setCallbackHelp();
scope.callback_url = scope.callback_server_path + ((data.related.callback) ? data.related.callback :
GetBasePath('job_templates') + id + '/callback/');
master.callback_url = scope.callback_url;
scope.can_edit = data.summary_fields.user_capabilities.edit;
if(scope.job_template_obj.summary_fields.user_capabilities.edit) {
MultiCredentialService.loadCredentials(data)
.then(([selectedCredentials, credTypes, credTypeOptions,
credTags]) => {
scope.selectedCredentials = selectedCredentials;
scope.credential_types = credTypes;
scope.credentialTypeOptions = credTypeOptions;
scope.credentialsToPost = credTags;
scope.$emit('jobTemplateLoaded', master);
});
}
else {
if (data.summary_fields.credential) {
scope.selectedCredentials.machine = data.summary_fields.credential;
}
if (data.summary_fields.vault_credential) {
scope.selectedCredentials.vault = data.summary_fields.vault_credential;
}
// Extra credentials are not included in summary_fields so we have to go
// out and get them ourselves.
let defers = [],
typesArray = [],
credTypeOptions;
Rest.setUrl(data.related.extra_credentials);
defers.push(Rest.get()
.then((data) => {
scope.selectedCredentials.extra = data.data.results;
})
.catch(({data, status}) => {
ProcessErrors(null, data, status, null,
{
hdr: 'Error!',
msg: 'Failed to get extra credentials. ' +
'Get returned status: ' +
status
});
}));
defers.push(MultiCredentialService.getCredentialTypes()
.then(({credential_types, credentialTypeOptions}) => {
typesArray = Object.keys(credential_types).map(key => credential_types[key]);
credTypeOptions = credentialTypeOptions;
})
);
return $q.all(defers).then(() => {
let machineAndVaultCreds = [],
extraCreds = [];
if(scope.selectedCredentials.machine) {
machineAndVaultCreds.push(scope.selectedCredentials.machine);
}
if(scope.selectedCredentials.vault) {
machineAndVaultCreds.push(scope.selectedCredentials.vault);
}
machineAndVaultCreds.map(cred => ({
name: cred.name,
id: cred.id,
postType: cred.postType,
kind: typesArray
.filter(type => {
return cred.kind === type.kind || parseInt(cred.credential_type) === type.value;
})[0].name + ":"
}));
extraCreds = extraCreds.concat(scope.selectedCredentials.extra).map(cred => ({
name: cred.name,
id: cred.id,
postType: cred.postType,
kind: credTypeOptions
.filter(type => {
return parseInt(cred.credential_type) === type.value;
})[0].name + ":"
}));
scope.credentialsToPost = machineAndVaultCreds.concat(extraCreds);
scope.$emit('jobTemplateLoaded', master);
});
}
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, {
hdr: 'Error!',
msg: 'Failed to retrieve job template: ' + $stateParams.id + '. GET status: ' + status
});
});
};
};
}

View File

@@ -85,7 +85,7 @@ function(NotificationsList, CompletedJobsList, i18n) {
ngChange: 'job_template_form.inventory_name.$validate()',
text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate) || !canGetAllRelatedResources'
},
project: {
label: i18n._('Project'),
@@ -100,13 +100,14 @@ function(NotificationsList, CompletedJobsList, i18n) {
dataTitle: i18n._('Project'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate) || !canGetAllRelatedResources',
awLookupWhen: 'canGetAllRelatedResources'
},
playbook: {
label: i18n._('Playbook'),
type:'select',
ngOptions: 'book for book in playbook_options track by book',
ngDisabled: "!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate) || disablePlaybookBecausePermissionDenied",
ngDisabled: "!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate) || !canGetAllRelatedResources",
id: 'playbook-select',
required: true,
column: 1,

View File

@@ -14,7 +14,7 @@ export default ['templateUrl', 'Rest', 'GetBasePath', 'generateList', '$compile'
.then(kinds => {
scope.credentialKinds = kinds;
scope.credentialKind = "" + kinds.Machine;
scope.credentialKind = scope.selectedCredentials.machine && scope.selectedCredentials.machine.readOnly ? (scope.selectedCredentials.vault && scope.selectedCredentials.vault.readOnly ? "" + kinds.Network : "" + kinds.Vault) : "" + kinds.Machine;
scope.showModal = function() {
$('#multi-credential-modal').modal('show');
@@ -50,6 +50,22 @@ export default ['templateUrl', 'Rest', 'GetBasePath', 'generateList', '$compile'
.then(({credential_types, credentialTypeOptions}) => {
scope.credential_types = credential_types;
scope.credentialTypeOptions = credentialTypeOptions;
scope.allCredentialTypeOptions = _.cloneDeep(credentialTypeOptions);
// We want to hide machine and vault dropdown options if a credential
// has already been selected for those types and the user interacting
// with the form doesn't have the ability to change them
for(let i=scope.credentialTypeOptions.length - 1; i >=0; i--) {
if((scope.selectedCredentials.machine &&
scope.selectedCredentials.machine.credential_type_id === scope.credentialTypeOptions[i].value &&
scope.selectedCredentials.machine.readOnly) ||
(scope.selectedCredentials.vault &&
scope.selectedCredentials.vault.credential_type_id === scope.credentialTypeOptions[i].value &&
scope.selectedCredentials.vault.readOnly)) {
scope.credentialTypeOptions.splice(i, 1);
}
}
scope.$emit('multiCredentialModalLinked');
});
});
@@ -70,7 +86,7 @@ export default ['templateUrl', 'Rest', 'GetBasePath', 'generateList', '$compile'
$scope.credTags = MultiCredentialService
.updateCredentialTags($scope.selectedCredentials,
$scope.credentialTypeOptions);
$scope.allCredentialTypeOptions);
};
let updateMachineCredentialList = function() {
@@ -85,7 +101,7 @@ export default ['templateUrl', 'Rest', 'GetBasePath', 'generateList', '$compile'
$scope.credTags = MultiCredentialService
.updateCredentialTags($scope.selectedCredentials,
$scope.credentialTypeOptions);
$scope.allCredentialTypeOptions);
};
let updateVaultCredentialList = function() {
@@ -100,7 +116,7 @@ export default ['templateUrl', 'Rest', 'GetBasePath', 'generateList', '$compile'
$scope.credTags = MultiCredentialService
.updateCredentialTags($scope.selectedCredentials,
$scope.credentialTypeOptions);
$scope.allCredentialTypeOptions);
};
let uncheckAllCredentials = function() {
@@ -110,7 +126,7 @@ export default ['templateUrl', 'Rest', 'GetBasePath', 'generateList', '$compile'
$scope.credTags = MultiCredentialService
.updateCredentialTags($scope.selectedCredentials,
$scope.credentialTypeOptions);
$scope.allCredentialTypeOptions);
};
let init = function() {

View File

@@ -27,13 +27,13 @@
class="MultiCredential-tagContainer ng-scope"
ng-repeat="tag in credTags track by $index">
<div class="MultiCredential-deleteContainer"
ng-click="removeCredential(tag.id)">
ng-click="removeCredential(tag.id)"
ng-if="!tag.readOnly">
<i class="fa fa-times
MultiCredential-tagDelete">
</i>
</div>
<div class="MultiCredential-tag
MultiCredential-tag--deletable">
<div class="MultiCredential-tag" ng-class="tag.readOnly ? 'MultiCredential-tag--disabled' : 'MultiCredential-tag--deletable'">
<span
class="MultiCredential-name--label
ng-binding">

View File

@@ -51,6 +51,10 @@
padding-left: 15px;
}
.MultiCredential-tag--disabled {
background-color: @default-icon;
}
.MultiCredential-tag--deletable {
margin-right: 0px;
border-top-left-radius: 0px;

View File

@@ -23,12 +23,12 @@
ng-repeat="tag in credentialsToPost track by $index">
<div class="MultiCredential-deleteContainer"
ng-click="removeCredential(tag.id)"
ng-hide="fieldIsDisabled">
ng-hide="fieldIsDisabled || tag.readOnly">
<i class="fa fa-times MultiCredential-tagDelete">
</i>
</div>
<div class="MultiCredential-tag"
ng-class="{'MultiCredential-tag--deletable': !fieldIsDisabled}">
ng-class="{'MultiCredential-tag--deletable': !fieldIsDisabled && !tag.readOnly, 'MultiCredential-tag--disabled': tag.readOnly}">
<span class="MultiCredential-name--label
ng-binding">
{{ tag.kind }}

View File

@@ -138,6 +138,7 @@ export default ['Rest', 'ProcessErrors', '$q', 'GetBasePath', function(Rest, Pro
name: cred.name,
id: cred.id,
postType: cred.postType,
readOnly: cred.readOnly ? true : false,
kind: typeOpts
.filter(type => {
return parseInt(cred.credential_type) === type.value;
@@ -178,6 +179,8 @@ export default ['Rest', 'ProcessErrors', '$q', 'GetBasePath', function(Rest, Pro
let credDefers = [];
let job_template_obj = data;
let credentialGetPermissionDenied = false;
// get machine credential
if (data.related.credential) {
Rest.setUrl(data.related.credential);
@@ -188,8 +191,10 @@ export default ['Rest', 'ProcessErrors', '$q', 'GetBasePath', function(Rest, Pro
.catch(({data, status}) => {
if (status === 403) {
/* User doesn't have read access to the machine credential, so use summary_fields */
credentialGetPermissionDenied = true;
selectedCredentials.machine = job_template_obj.summary_fields.credential;
selectedCredentials.machine.credential_type = job_template_obj.summary_fields.credential.credential_type_id;
selectedCredentials.machine.readOnly = true;
} else {
ProcessErrors(
null, data, status, null,
@@ -212,8 +217,10 @@ export default ['Rest', 'ProcessErrors', '$q', 'GetBasePath', function(Rest, Pro
.catch(({data, status}) => {
if (status === 403) {
/* User doesn't have read access to the vault credential, so use summary_fields */
credentialGetPermissionDenied = true;
selectedCredentials.vault = job_template_obj.summary_fields.vault_credential;
selectedCredentials.vault.credential_type = job_template_obj.summary_fields.vault_credential.credential_type_id;
selectedCredentials.vault.readOnly = true;
} else {
ProcessErrors(
null, data, status, null,
@@ -237,9 +244,11 @@ export default ['Rest', 'ProcessErrors', '$q', 'GetBasePath', function(Rest, Pro
.catch(({data, status}) => {
if (status === 403) {
/* User doesn't have read access to the extra credentials, so use summary_fields */
credentialGetPermissionDenied = true;
selectedCredentials.extra = job_template_obj.summary_fields.extra_credentials;
_.map(selectedCredentials.extra, (cred) => {
cred.credential_type = cred.credential_type_id;
cred.readOnly = true;
return cred;
});
} else {
@@ -267,7 +276,7 @@ export default ['Rest', 'ProcessErrors', '$q', 'GetBasePath', function(Rest, Pro
.updateCredentialTags(selectedCredentials, credTypeOptions);
return [selectedCredentials, credTypes, credTypeOptions,
credTags];
credTags, credentialGetPermissionDenied];
});
};

View File

@@ -137,6 +137,61 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplates.
},
resolve: {
edit: {
jobTemplateData: ['$stateParams', 'TemplatesService', 'ProcessErrors',
function($stateParams, TemplatesService, ProcessErrors) {
return TemplatesService.getJobTemplate($stateParams.job_template_id)
.then(function(res) {
return res.data;
}).catch(function(response){
ProcessErrors(null, response.data, response.status, null, {
hdr: 'Error!',
msg: 'Failed to get job template. GET returned status: ' +
response.status
});
});
}],
canGetProject: ['Rest', 'ProcessErrors', 'jobTemplateData',
function(Rest, ProcessErrors, jobTemplateData) {
Rest.setUrl(jobTemplateData.related.project);
return Rest.get()
.then(() => {
return true;
})
.catch(({data, status}) => {
if (status === 403) {
/* User doesn't have read access to the project, no problem. */
} else {
ProcessErrors(null, data, status, null, {
hdr: 'Error!',
msg: 'Failed to get project. GET returned ' +
'status: ' + status
});
}
return false;
});
}],
canGetInventory: ['Rest', 'ProcessErrors', 'jobTemplateData',
function(Rest, ProcessErrors, jobTemplateData) {
Rest.setUrl(jobTemplateData.related.inventory);
return Rest.get()
.then(() => {
return true;
})
.catch(({data, status}) => {
if (status === 403) {
/* User doesn't have read access to the project, no problem. */
} else {
ProcessErrors(null, data, status, null, {
hdr: 'Error!',
msg: 'Failed to get project. GET returned ' +
'status: ' + status
});
}
return false;
});
}],
InstanceGroupsData: ['$stateParams', 'Rest', 'GetBasePath', 'ProcessErrors',
function($stateParams, Rest, GetBasePath, ProcessErrors){
let path = `${GetBasePath('job_templates')}${$stateParams.job_template_id}/instance_groups/`;
@@ -155,32 +210,32 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplates.
});
});
}],
availableLabels: ['Rest', '$stateParams', 'GetBasePath', 'ProcessErrors', 'TemplatesService',
function(Rest, $stateParams, GetBasePath, ProcessErrors, TemplatesService) {
return TemplatesService.getAllLabelOptions()
.then(function(labels){
return labels;
}).catch(function(response){
ProcessErrors(null, response.data, response.status, null, {
hdr: 'Error!',
msg: 'Failed to get labels. GET returned status: ' +
response.status
});
availableLabels: ['Rest', '$stateParams', 'GetBasePath', 'ProcessErrors', 'TemplatesService',
function(Rest, $stateParams, GetBasePath, ProcessErrors, TemplatesService) {
return TemplatesService.getAllLabelOptions()
.then(function(labels){
return labels;
}).catch(function(response){
ProcessErrors(null, response.data, response.status, null, {
hdr: 'Error!',
msg: 'Failed to get labels. GET returned status: ' +
response.status
});
}],
selectedLabels: ['Rest', '$stateParams', 'GetBasePath', 'TemplatesService', 'ProcessErrors',
function(Rest, $stateParams, GetBasePath, TemplatesService, ProcessErrors) {
return TemplatesService.getAllJobTemplateLabels($stateParams.job_template_id)
.then(function(labels){
return labels;
}).catch(function(response){
ProcessErrors(null, response.data, response.status, null, {
hdr: 'Error!',
msg: 'Failed to get workflow job template labels. GET returned status: ' +
response.status
});
});
}],
selectedLabels: ['Rest', '$stateParams', 'GetBasePath', 'TemplatesService', 'ProcessErrors',
function(Rest, $stateParams, GetBasePath, TemplatesService, ProcessErrors) {
return TemplatesService.getAllJobTemplateLabels($stateParams.job_template_id)
.then(function(labels){
return labels;
}).catch(function(response){
ProcessErrors(null, response.data, response.status, null, {
hdr: 'Error!',
msg: 'Failed to get workflow job template labels. GET returned status: ' +
response.status
});
}]
});
}]
}
}
});