mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 10:30:03 -03:30
use updated credentials endpoint
This commit is contained in:
parent
79bd8b2c72
commit
916d91cbc7
@ -48,6 +48,14 @@
|
||||
CallbackHelpInit({ scope: $scope });
|
||||
|
||||
$scope.surveyTooltip = i18n._('Please save before adding a survey to this job template.');
|
||||
|
||||
MultiCredentialService.getCredentialTypes()
|
||||
.then(({ data }) => {
|
||||
$scope.multiCredential = {
|
||||
credentialTypes: data.results,
|
||||
selectedCredentials: []
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
callback = function() {
|
||||
@ -328,7 +336,6 @@
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.post(data)
|
||||
.then(({data}) => {
|
||||
|
||||
if (data.related && data.related.callback) {
|
||||
Alert('Callback URL',
|
||||
`Host callbacks are enabled for this template. The callback URL is:
|
||||
@ -347,12 +354,6 @@
|
||||
null, true);
|
||||
}
|
||||
|
||||
MultiCredentialService
|
||||
.saveExtraCredentials({
|
||||
creds: $scope.credentialsToPost,
|
||||
url: data.related.credentials
|
||||
});
|
||||
|
||||
var orgDefer = $q.defer();
|
||||
var associationDefer = $q.defer();
|
||||
Rest.setUrl(data.related.labels);
|
||||
@ -441,7 +442,9 @@
|
||||
});
|
||||
}
|
||||
|
||||
saveCompleted(data.id);
|
||||
MultiCredentialService
|
||||
.saveRelated(data, $scope.multiCredential.selectedCredentials)
|
||||
.then(() => saveCompleted(data.id));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -362,74 +362,37 @@ export default
|
||||
|
||||
$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 = !projectGetPermissionDenied && !inventoryGetPermissionDenied && !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;
|
||||
// }
|
||||
const multiCredential = {};
|
||||
const credentialTypesPromise = MultiCredentialService.getCredentialTypes()
|
||||
.then(({ data }) => {
|
||||
multiCredential.credentialTypes = data.results;
|
||||
});
|
||||
const multiCredentialPromises = [credentialTypesPromise];
|
||||
|
||||
// if (jobTemplateData.summary_fields.vault_credential) {
|
||||
// $scope.selectedCredentials.vault = jobTemplateData.summary_fields.vault_credential;
|
||||
// }
|
||||
|
||||
if (jobTemplateData.summary_fields.credentials) {
|
||||
$scope.selectedCredentials.extra = jobTemplateData.summary_fields.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 + ":"
|
||||
}));
|
||||
if ($scope.can_edit) {
|
||||
const selectedCredentialsPromise = MultiCredentialService
|
||||
.getRelated(jobTemplateData, { permitted: [403] })
|
||||
.then(({ data, status }) => {
|
||||
if (status === 403) {
|
||||
$scope.canGetAllRelatedResources = false;
|
||||
multiCredential.selectedCredentials = _.get(jobTemplateData, 'summary_fields.credentials');
|
||||
} else {
|
||||
$scope.canGetAllRelatedResources = !projectGetPermissionDenied && !inventoryGetPermissionDenied;
|
||||
multiCredential.selectedCredentials = data.results;
|
||||
}
|
||||
|
||||
//$scope.credentialsToPost = machineAndVaultCreds.concat(extraCreds);
|
||||
$scope.credentialsToPost = extraCreds;
|
||||
|
||||
$scope.$emit('jobTemplateLoaded', master);
|
||||
});
|
||||
|
||||
multiCredentialPromises.push(selectedCredentialsPromise);
|
||||
} else {
|
||||
$scope.canGetAllRelatedResources = false;
|
||||
multiCredential.selectedCredentials = _.get(jobTemplateData, 'summary_fields.credentials');
|
||||
}
|
||||
|
||||
$q.all(multiCredentialPromises)
|
||||
.then(() => {
|
||||
$scope.multiCredential = multiCredential;
|
||||
$scope.$emit('jobTemplateLoaded', master);
|
||||
});
|
||||
});
|
||||
|
||||
if ($scope.removeChoicesReady) {
|
||||
@ -522,10 +485,7 @@ export default
|
||||
}
|
||||
|
||||
MultiCredentialService
|
||||
.findChangedExtraCredentials({
|
||||
creds: $scope.credentialsToPost,
|
||||
url: data.related.credentials
|
||||
});
|
||||
.saveRelated(jobTemplateData, $scope.multiCredential.selectedCredentials);
|
||||
|
||||
InstanceGroupsService.editInstanceGroups(instance_group_url, $scope.instance_groups)
|
||||
.catch(({data, status}) => {
|
||||
@ -668,13 +628,24 @@ export default
|
||||
data.ask_credential_on_launch = $scope.ask_credential_on_launch ? $scope.ask_credential_on_launch : false;
|
||||
data.job_tags = (Array.isArray($scope.job_tags)) ? $scope.job_tags.join() : "";
|
||||
data.skip_tags = (Array.isArray($scope.skip_tags)) ? $scope.skip_tags.join() : "";
|
||||
|
||||
// drop legacy 'credential' and 'vault_credential' keys from the update request as they will
|
||||
// be provided to the related credentials endpoint by the template save success handler.
|
||||
delete data.credential;
|
||||
delete data.vault_credential;
|
||||
|
||||
data.extra_vars = ToJSON($scope.parseType, $scope.variables, true);
|
||||
if ($scope.selectedCredentials && $scope.selectedCredentials
|
||||
.machine && $scope.selectedCredentials
|
||||
.machine.id) {
|
||||
data.credential = $scope.selectedCredentials
|
||||
.machine.id;
|
||||
} else {
|
||||
data.credential = null;
|
||||
}
|
||||
if ($scope.selectedCredentials && $scope.selectedCredentials
|
||||
.vault && $scope.selectedCredentials
|
||||
.vault.id) {
|
||||
data.vault_credential = $scope.selectedCredentials
|
||||
.vault.id;
|
||||
} else {
|
||||
data.vault_credential = null;
|
||||
}
|
||||
data.extra_vars = ToJSON($scope.parseType,
|
||||
$scope.variables, true);
|
||||
|
||||
// We only want to set the survey_enabled flag to
|
||||
// true for this job template if a survey exists
|
||||
@ -713,6 +684,10 @@ export default
|
||||
data.job_tags = (Array.isArray($scope.job_tags)) ? _.uniq($scope.job_tags).join() : "";
|
||||
data.skip_tags = (Array.isArray($scope.skip_tags)) ? _.uniq($scope.skip_tags).join() : "";
|
||||
|
||||
// drop legacy 'credential' and 'vault_credential' keys from the update request as they will
|
||||
// be provided to the related credentials endpoint by the template save success handler.
|
||||
delete data.credential;
|
||||
delete data.vault_credential;
|
||||
|
||||
Rest.setUrl(defaultUrl + $state.params.job_template_id);
|
||||
Rest.put(data)
|
||||
|
||||
@ -124,9 +124,9 @@ function(NotificationsList, CompletedJobsList, i18n) {
|
||||
<multi-credential
|
||||
credentials="credentials"
|
||||
prompt="ask_credential_on_launch"
|
||||
selected-credentials="selectedCredentials"
|
||||
credential-not-present="credentialNotPresent"
|
||||
credentials-to-post="credentialsToPost"
|
||||
selected-credentials="multiCredential.selectedCredentials"
|
||||
credential-types="multiCredential.credentialTypes"
|
||||
field-is-disabled="!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)">
|
||||
</multi-credential>`,
|
||||
awPopOver: i18n._('Select credentials that allow Tower to access the nodes this job will be ran against. You can only select one credential of each type. For machine credentials (SSH), checking "Prompt on launch" without selecting credentials will require you to select a machine credential at run time. If you select credentials and check "Prompt on launch", the selected credential(s) become the defaults that can be updated at run time.'),
|
||||
|
||||
@ -1,272 +1,251 @@
|
||||
export default ['templateUrl', 'Rest', 'GetBasePath', 'generateList', '$compile', 'CreateSelect2', 'i18n', 'MultiCredentialService', 'credentialTypesLookup',
|
||||
function(templateUrl, Rest, GetBasePath, GenerateList, $compile, CreateSelect2, i18n, MultiCredentialService, credentialTypesLookup) {
|
||||
/*************************************************
|
||||
* Copyright (c) 2018 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
function MultiCredentialModal(
|
||||
templateUrl,
|
||||
generateList,
|
||||
$compile,
|
||||
CreateSelect2,
|
||||
i18n,
|
||||
CredentialList
|
||||
) {
|
||||
const templatePath = 'templates/job_templates/multi-credential/multi-credential-modal';
|
||||
const emptyListText = i18n._('No Credentials Matching This Type Have Been Created');
|
||||
|
||||
const list = _.cloneDeep(CredentialList);
|
||||
const vaultList = _.cloneDeep(CredentialList);
|
||||
|
||||
list.emptyListText = emptyListText;
|
||||
|
||||
vaultList.emptyListText = emptyListText;
|
||||
vaultList.fields.name.modalColumnClass = 'col-md-6';
|
||||
vaultList.fields.info = {
|
||||
label: i18n._('Vault ID'),
|
||||
ngBind: 'credential.inputs.vault_id',
|
||||
key: false,
|
||||
nosort: true,
|
||||
modalColumnClass: 'col-md-6',
|
||||
infoHeaderClass: '',
|
||||
dataPlacement: 'top',
|
||||
};
|
||||
|
||||
const listHtml = generateList.build({ mode: 'lookup', input_type: 'radio', list });
|
||||
const vaultHtml = generateList.build({ mode: 'lookup', input_type: 'checkbox', list: vaultList });
|
||||
|
||||
return {
|
||||
templateUrl: templateUrl(templatePath),
|
||||
restrict: 'E',
|
||||
controllerAs: 'vm',
|
||||
require: ['multiCredentialModal'],
|
||||
scope: {
|
||||
credentialsToPost: '=',
|
||||
credentials: '=',
|
||||
selectedCredentials: '='
|
||||
credentialTypes: '=',
|
||||
selectedCredentials: '=',
|
||||
},
|
||||
templateUrl: templateUrl('templates/job_templates/multi-credential/multi-credential-modal'),
|
||||
link: (scope, element, attrs, controllers) => {
|
||||
const compiledList = $compile(listHtml)(scope);
|
||||
const compiledVaultList = $compile(vaultHtml)(scope);
|
||||
|
||||
link: function(scope, element) {
|
||||
credentialTypesLookup()
|
||||
.then(kinds => {
|
||||
scope.credentialKinds = kinds;
|
||||
const modalBodyElement = $('#multi-credential-modal-body');
|
||||
const modalElement = $('#multi-credential-modal');
|
||||
|
||||
const machineIsReadOnly = _.get(scope.selectedCredentials, 'machine.readOnly');
|
||||
const vaultIsReadOnly = _.get(scope.selectedCredentials, 'vault.readOnly');
|
||||
scope.showModal = () => modalElement.modal('show');
|
||||
scope.hideModal = () => modalElement.modal('hide');
|
||||
|
||||
if (!machineIsReadOnly) {
|
||||
scope.credentialKind = `${kinds.Machine}`;
|
||||
} else if (!vaultIsReadOnly) {
|
||||
scope.credentialKind = `${kinds.Vault}`;
|
||||
} else {
|
||||
scope.credentialKind = `${kinds.Network}`;
|
||||
}
|
||||
scope.createList = () => modalBodyElement.append(compiledList);
|
||||
scope.createVaultList = () => modalBodyElement.append(compiledVaultList);
|
||||
scope.destroyList = () => modalBodyElement.empty();
|
||||
|
||||
scope.showModal = function() {
|
||||
scope.modalHidden = false;
|
||||
$('#multi-credential-modal').modal('show');
|
||||
};
|
||||
|
||||
scope.hideModal = function() {
|
||||
scope.modalHidden = true;
|
||||
scope.credentialKind = kinds.Machine;
|
||||
$('#multi-credential-modal').modal('hide');
|
||||
};
|
||||
|
||||
scope.generateCredentialList = function(inputType = 'radio', list = scope.list) {
|
||||
console.log(inputType);
|
||||
let html = GenerateList.build({
|
||||
list,
|
||||
input_type: inputType,
|
||||
mode: 'lookup'
|
||||
});
|
||||
|
||||
$('#multi-credential-modal-body').append($compile(html)(scope));
|
||||
scope.listRendered = true;
|
||||
};
|
||||
|
||||
scope.destroyCredentialList = () => {
|
||||
$('#multi-credential-modal-body').empty();
|
||||
scope.listRendered = false;
|
||||
}
|
||||
|
||||
$('#multi-credential-modal').on('hidden.bs.modal', function () {
|
||||
$('#multi-credential-modal').off('hidden.bs.modal');
|
||||
$(element).remove();
|
||||
});
|
||||
|
||||
CreateSelect2({
|
||||
element: `#multi-credential-kind-select`,
|
||||
multiple: false,
|
||||
placeholder: i18n._('Select a credential')
|
||||
});
|
||||
|
||||
MultiCredentialService.getCredentialTypes()
|
||||
.then(({credential_types, credentialTypeOptions}) => {
|
||||
scope.credential_types = credential_types;
|
||||
scope.credentialTypeOptions = credentialTypeOptions;
|
||||
scope.allCredentialTypeOptions = _.cloneDeep(credentialTypeOptions);
|
||||
|
||||
// We want to hide the machine dropdown option if a machine credential
|
||||
// has already been selected and the user interacting with the form doesn't
|
||||
// have the ability to change it.
|
||||
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');
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
controller: ['$scope', 'CredentialList', 'i18n', 'QuerySet',
|
||||
'GetBasePath', function($scope, CredentialList, i18n, qs,
|
||||
GetBasePath) {
|
||||
let updateExtraCredentialsList = function() {
|
||||
let extraCredIds = $scope.selectedCredentials.extra.map(cred => cred.id);
|
||||
$scope.credentials.forEach(cred => {
|
||||
cred.checked = (extraCredIds.indexOf(cred.id) > - 1) ? 1 : 0;
|
||||
});
|
||||
|
||||
$scope.credTags = MultiCredentialService
|
||||
.updateCredentialTags($scope.selectedCredentials,
|
||||
$scope.allCredentialTypeOptions);
|
||||
};
|
||||
|
||||
let uncheckAllCredentials = function() {
|
||||
$scope.credentials.forEach(cred => {
|
||||
cred.checked = 0;
|
||||
});
|
||||
|
||||
$scope.credTags = MultiCredentialService
|
||||
.updateCredentialTags($scope.selectedCredentials,
|
||||
$scope.allCredentialTypeOptions);
|
||||
};
|
||||
|
||||
const onCredentialKindChanged = (newValue, oldValue) => {
|
||||
const newValueIsVault = (parseInt(newValue) === _.get($scope, 'credentialKinds.Vault'));
|
||||
const oldValueIsVault = (parseInt(oldValue) === _.get($scope, 'credentialKinds.Vault'));
|
||||
const isClosing = ((newValue !== oldValue) && $scope.modalHidden);
|
||||
|
||||
if ((oldValueIsVault || newValueIsVault) && !isClosing) {
|
||||
$scope.destroyCredentialList();
|
||||
}
|
||||
|
||||
$scope.credential_queryset.page = 1;
|
||||
$scope.credential_default_params.credential_type = parseInt($scope.credentialKind);
|
||||
$scope.credential_queryset.credential_type = parseInt($scope.credentialKind);
|
||||
|
||||
qs.search(GetBasePath('credentials'), $scope.credential_default_params)
|
||||
.then(({ data }) => {
|
||||
$scope.credential_dataset = data;
|
||||
$scope.credentials = $scope.credential_dataset.results;
|
||||
|
||||
if(!$scope.listRendered) {
|
||||
if (newValueIsVault) {
|
||||
$scope.generateCredentialList('checkbox', $scope.vaultList);
|
||||
} else {
|
||||
$scope.generateCredentialList();
|
||||
}
|
||||
updateExtraCredentialsList();
|
||||
$scope.showModal();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let init = function() {
|
||||
$scope.originalSelectedCredentials = _.cloneDeep($scope.selectedCredentials);
|
||||
$scope.credential_dataset = [];
|
||||
$scope.credentials = $scope.credentials || [];
|
||||
$scope.listRendered = false;
|
||||
|
||||
const credList = _.cloneDeep(CredentialList);
|
||||
|
||||
credList.emptyListText = i18n._('No Credentials Matching This Type Have Been Created');
|
||||
|
||||
const vaultCredList = _.cloneDeep(credList);
|
||||
|
||||
vaultCredList.fields.name.modalColumnClass = 'col-md-6';
|
||||
|
||||
vaultCredList.fields.info = {
|
||||
label: i18n._('Vault ID'),
|
||||
ngBind: 'credential.inputs.vault_id',
|
||||
key: false,
|
||||
nosort: true,
|
||||
modalColumnClass: 'col-md-6',
|
||||
infoHeaderClass: '',
|
||||
dataPlacement: 'top'
|
||||
};
|
||||
|
||||
$scope.list = credList;
|
||||
$scope.vaultList = vaultCredList;
|
||||
|
||||
$scope.credential_default_params = {
|
||||
order_by: 'name',
|
||||
page_size: 5
|
||||
};
|
||||
|
||||
$scope.credential_queryset = {
|
||||
order_by: 'name',
|
||||
page_size: 5
|
||||
};
|
||||
|
||||
$scope.$watch('credentialKind', onCredentialKindChanged);
|
||||
|
||||
$scope.$watchCollection('credentials', updateExtraCredentialsList);
|
||||
|
||||
$scope.$watchCollection('selectedCredentials.extra', () => {
|
||||
if($scope.credentials && $scope.credentials.length > 0) {
|
||||
if($scope.selectedCredentials.extra &&
|
||||
$scope.selectedCredentials.extra.length > 0) {
|
||||
updateExtraCredentialsList();
|
||||
} else {
|
||||
uncheckAllCredentials();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$on('multiCredentialModalLinked', function() {
|
||||
init();
|
||||
modalElement.on('hidden.bs.modal', () => {
|
||||
modalElement.off('hidden.bs.modal');
|
||||
$(element).remove();
|
||||
});
|
||||
|
||||
$scope.toggle_credential = id => {
|
||||
// This is called only when a checkbox input is clicked directly. For clicks anywhere
|
||||
// else on the row or direct radio button clicks, the toggle_row handler is called
|
||||
// instead with a slightly different set of arguments. We normalize those arguments
|
||||
// here and pass them through to the other handler so that the behavior is consistent.
|
||||
const [credential] = $scope.credentials.filter(credential => credential.id === id);
|
||||
return $scope.toggle_row(credential);
|
||||
};
|
||||
CreateSelect2({
|
||||
placeholder: i18n._('Select a credential'),
|
||||
element: '#multi-credential-kind-select',
|
||||
multiple: false,
|
||||
});
|
||||
|
||||
$scope.toggle_row = function(credential) {
|
||||
let rowDeselected = false;
|
||||
for (let i = $scope.selectedCredentials.extra.length - 1; i >= 0; i--) {
|
||||
if($scope.selectedCredentials.extra[i].id === credential.id) {
|
||||
$scope.selectedCredentials.extra.splice(i, 1);
|
||||
rowDeselected = true;
|
||||
} else if(credential.credential_type === $scope.selectedCredentials.extra[i].credential_type) {
|
||||
if (credential.credential_type !== $scope.credentialKinds.Vault) {
|
||||
$scope.selectedCredentials.extra.splice(i, 1);
|
||||
} else if($scope.selectedCredentials.extra[i].inputs.vault_id === credential.inputs.vault_id) {
|
||||
// remove existing vault credentials if they have the same vault_id as a recently
|
||||
// toggled vault credential
|
||||
$scope.selectedCredentials.extra.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!rowDeselected) {
|
||||
$scope.selectedCredentials.extra
|
||||
.push(_.cloneDeep(credential));
|
||||
}
|
||||
};
|
||||
|
||||
$scope.selectedCredentialsDirty = function() {
|
||||
if ($scope.originalSelectedCredentials) {
|
||||
return !($scope.originalSelectedCredentials.extra.length === 0) &&
|
||||
!_.isEqual($scope.selectedCredentials,
|
||||
$scope.originalSelectedCredentials);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.revertToDefaultCredentials = function() {
|
||||
$scope.selectedCredentials = _.cloneDeep($scope.originalSelectedCredentials);
|
||||
};
|
||||
|
||||
$scope.removeCredential = function(credToRemove) {
|
||||
[$scope.selectedCredentials,
|
||||
$scope.credTags] = MultiCredentialService
|
||||
.removeCredential(credToRemove,
|
||||
$scope.selectedCredentials, $scope.credTags);
|
||||
|
||||
if ($scope.credentials
|
||||
.filter(cred => cred.id === credToRemove).length) {
|
||||
uncheckAllCredentials();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cancelForm = function() {
|
||||
$scope.selectedCredentials = $scope.originalSelectedCredentials;
|
||||
$scope.credTags = $scope.credentialsToPost;
|
||||
$scope.hideModal();
|
||||
};
|
||||
|
||||
$scope.saveForm = function() {
|
||||
$scope.credentialsToPost = $scope.credTags;
|
||||
$scope.hideModal();
|
||||
};
|
||||
}]
|
||||
scope.list = list;
|
||||
controllers[0].init(scope);
|
||||
},
|
||||
controller: ['GetBasePath', 'QuerySet', 'MultiCredentialService',
|
||||
multiCredentialModalController],
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
function multiCredentialModalController(GetBasePath, qs, MultiCredentialService) {
|
||||
const vm = this;
|
||||
const { createTag, isReadOnly } = MultiCredentialService;
|
||||
|
||||
const types = {};
|
||||
const unwatch = [];
|
||||
|
||||
let scope;
|
||||
|
||||
vm.init = _scope_ => {
|
||||
scope = _scope_;
|
||||
scope.modalSelectedCredentials = _.cloneDeep(scope.selectedCredentials);
|
||||
|
||||
scope.credentialTypes.forEach(({ name, id }) => types[name] = id);
|
||||
|
||||
scope.toggle_row = vm.toggle_row;
|
||||
scope.toggle_credential = vm.toggle_credential;
|
||||
|
||||
scope.credential_default_params = { order_by: 'name', page_size: 5 };
|
||||
scope.credential_queryset = { order_by: 'name', page_size: 5 };
|
||||
scope.credential_dataset = { results: [], count: 0 };
|
||||
scope.credentials = scope.credential_dataset.results;
|
||||
|
||||
scope.credentialType = `${types.Vault}`;
|
||||
scope.displayedCredentialTypes = scope.credentialTypes;
|
||||
|
||||
const watchType = scope.$watch('credentialType', (newValue, oldValue) => {
|
||||
if (newValue !== oldValue) {
|
||||
fetchCredentials(parseInt(newValue));
|
||||
}
|
||||
});
|
||||
scope.$watchCollection('modalSelectedCredentials', updateListView);
|
||||
scope.$watchCollection('modalSelectedCredentials', updateTagView);
|
||||
scope.$watchCollection('modalSelectedCredentials', updateDisplayedCredentialTypes);
|
||||
scope.$watchCollection('credentials', updateListView);
|
||||
|
||||
unwatch.push(watchType);
|
||||
|
||||
fetchCredentials(parseInt(scope.credentialType))
|
||||
.then(scope.showModal);
|
||||
};
|
||||
|
||||
function updateTagView () {
|
||||
scope.tags = scope.modalSelectedCredentials
|
||||
.map(c => createTag(c, scope.credentialTypes));
|
||||
}
|
||||
|
||||
function updateListView () {
|
||||
scope.credentials.forEach(credential => {
|
||||
const index = scope.modalSelectedCredentials
|
||||
.map(({ id }) => id)
|
||||
.indexOf(credential.id);
|
||||
|
||||
if (index > -1) {
|
||||
credential.checked = 1;
|
||||
} else {
|
||||
credential.checked = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateDisplayedCredentialTypes() {
|
||||
const displayedCredentialTypes = _.cloneDeep(scope.credentialTypes);
|
||||
|
||||
scope.modalSelectedCredentials.forEach(credential => {
|
||||
const credentialTypeId = credential.credential_type || credential.credential_type_id;
|
||||
|
||||
if(isReadOnly(credential) && credentialTypeId !== types.Vault) {
|
||||
const index = displayedCredentialTypes
|
||||
.map(t => t.id).indexOf(credential.credential_type);
|
||||
|
||||
if (index > -1) {
|
||||
displayedCredentialTypes.splice(index, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
scope.displayedCredentialTypes = displayedCredentialTypes;
|
||||
}
|
||||
|
||||
function fetchCredentials (credentialType) {
|
||||
const endpoint = GetBasePath('credentials');
|
||||
|
||||
scope.credential_queryset.page = 1;
|
||||
scope.credential_default_params.credential_type = credentialType;
|
||||
scope.credential_queryset.credential_type = credentialType;
|
||||
|
||||
return qs.search(endpoint, scope.credential_default_params)
|
||||
.then(({ data }) => {
|
||||
const results = data.results.filter(c => !isReadOnly(c));
|
||||
const readOnlyCount = data.results.length - results.length;
|
||||
|
||||
data.results = results;
|
||||
data.count = data.count - readOnlyCount;
|
||||
|
||||
scope.credential_dataset = data;
|
||||
scope.credentials = data.results;
|
||||
|
||||
scope.destroyList();
|
||||
|
||||
if (credentialType === types.Vault) {
|
||||
scope.createVaultList();
|
||||
} else {
|
||||
scope.createList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
vm.revertSelectedCredentials = () => {
|
||||
scope.modalSelectedCredentials = _.cloneDeep(scope.selectedCredentials);
|
||||
};
|
||||
|
||||
vm.removeCredential = id => {
|
||||
const index = scope.modalSelectedCredentials.map(c => c.id).indexOf(id);
|
||||
const isSelected = index > -1;
|
||||
|
||||
if (isSelected) {
|
||||
scope.modalSelectedCredentials.splice(index, 1);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
vm.toggle_credential = id => {
|
||||
// This is called only when a checkbox input is clicked directly. Clicks anywhere else on
|
||||
// the row or direct radio button clicks invoke the toggle_row handler instead with a
|
||||
// different set of arguments. We normalize those arguments here and pass them through to
|
||||
// the other function so that the behavior is consistent.
|
||||
const credential = scope.credentials.find(c => c.id === id);
|
||||
scope.toggle_row(credential);
|
||||
};
|
||||
|
||||
vm.toggle_row = credential => {
|
||||
const index = scope.modalSelectedCredentials.map(c => c.id).indexOf(credential.id);
|
||||
const isSelected = index > -1;
|
||||
|
||||
if (isSelected) {
|
||||
scope.modalSelectedCredentials.splice(index, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (credential.credential_type === types.Vault) {
|
||||
scope.modalSelectedCredentials = scope.modalSelectedCredentials
|
||||
.filter(({ inputs }) => inputs.vault_id !== credential.inputs.vault_id)
|
||||
.concat([credential]);
|
||||
} else {
|
||||
scope.modalSelectedCredentials = scope.modalSelectedCredentials
|
||||
.filter(({ credential_type }) => credential_type !== credential.credential_type)
|
||||
.concat([credential]);
|
||||
}
|
||||
};
|
||||
|
||||
vm.cancelForm = () => {
|
||||
unwatch.forEach(cb => cb());
|
||||
scope.hideModal();
|
||||
};
|
||||
|
||||
vm.saveForm = () => {
|
||||
scope.selectedCredentials = _.cloneDeep(scope.modalSelectedCredentials);
|
||||
unwatch.forEach(cb => cb());
|
||||
scope.hideModal();
|
||||
};
|
||||
}
|
||||
|
||||
MultiCredentialModal.$inject = [
|
||||
'templateUrl',
|
||||
'generateList',
|
||||
'$compile',
|
||||
'CreateSelect2',
|
||||
'i18n',
|
||||
'CredentialList',
|
||||
];
|
||||
|
||||
export default MultiCredentialModal;
|
||||
|
||||
@ -2,21 +2,19 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header Form-header">
|
||||
<div class="Form-title Form-title--uppercase"
|
||||
translate>
|
||||
CREDENTIALS
|
||||
<div class="Form-title Form-title--uppercase" translate>
|
||||
CREDENTIALS
|
||||
</div>
|
||||
<div class="Form-header--fields"></div>
|
||||
<div class="Form-exitHolder">
|
||||
<button type="button" class="Form-exit"
|
||||
ng-click="cancelForm()">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
<button type="button" class="Form-exit" ng-click="cancelForm()">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="MultiCredential-selectedBar"
|
||||
ng-show="(credTags && credTags.length) || selectedCredentialsDirty()">
|
||||
ng-show="(tags && tags.length) || selectedCredentialsDirty()">
|
||||
<span class="MultiCredential-selectedBarLabel" translate>
|
||||
SELECTED:
|
||||
</span>
|
||||
@ -25,20 +23,18 @@
|
||||
<div class="MultiCredential-flexContainer">
|
||||
<div
|
||||
class="MultiCredential-tagContainer ng-scope"
|
||||
ng-repeat="tag in credTags track by $index">
|
||||
ng-repeat="tag in tags track by $index">
|
||||
<div class="MultiCredential-deleteContainer"
|
||||
ng-click="removeCredential(tag.id)"
|
||||
ng-click="vm.removeCredential(tag.id)"
|
||||
ng-if="!tag.readOnly">
|
||||
<i class="fa fa-times
|
||||
MultiCredential-tagDelete">
|
||||
</i>
|
||||
<i class="fa fa-times MultiCredential-tagDelete"></i>
|
||||
</div>
|
||||
<div class="MultiCredential-tag" ng-class="tag.readOnly ? 'MultiCredential-tag--disabled' : 'MultiCredential-tag--deletable'">
|
||||
<span ng-if="tag.kind !== 'Vault'" class="MultiCredential-name--label ng-binding">
|
||||
{{ tag.kind }}:
|
||||
<span ng-if="tag.info" class="MultiCredential-name--label ng-binding">
|
||||
{{ tag.typeName }} {{ tag.info }}:
|
||||
</span>
|
||||
<span ng-if="tag.kind === 'Vault'" class="MultiCredential-name--label ng-binding">
|
||||
{{ tag.kind }} ({{ tag.vault_id }}):
|
||||
<span ng-if="!tag.info" class="MultiCredential-name--label ng-binding">
|
||||
{{ tag.typeName }}:
|
||||
</span>
|
||||
<span class="MultiCredential-name u-wordwrap ng-binding">
|
||||
{{ tag.name }}
|
||||
@ -49,28 +45,35 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="MultiCredential-previewTagRevert">
|
||||
<a class="MultiCredential-revertLink" href="" ng-show="selectedCredentialsDirty()" ng-click="revertToDefaultCredentials()" translate>REVERT</a>
|
||||
<a class="MultiCredential-revertLink"
|
||||
ng-show="selectedCredentialsDirty()"
|
||||
ng-click="vm.revertSelectedCredentials()"
|
||||
href=""
|
||||
translate>
|
||||
REVERT
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="MultiCredential-credentialSubSection">
|
||||
<span class="MultiCredential-selectLabel" translate>CREDENTIAL TYPE:</span>
|
||||
<select id="multi-credential-kind-select" ng-model="credentialKind">
|
||||
<option ng-repeat="option in credentialTypeOptions"
|
||||
value="{{option.value}}">{{option.name}}</option>
|
||||
<select id="multi-credential-kind-select" ng-model="credentialType">
|
||||
<option ng-repeat="t in displayedCredentialTypes" value="{{ t.id }}">{{ t.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="multi-credential-modal-body"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" ng-click="cancelForm()"
|
||||
<button type="button"
|
||||
ng-click="vm.cancelForm()"
|
||||
class="Lookup-cancel btn btn-sm Form-cancelButton"
|
||||
translate>
|
||||
CANCEL
|
||||
CANCEL
|
||||
</button>
|
||||
<button type="button"
|
||||
ng-click="saveForm()"
|
||||
ng-click="vm.saveForm()"
|
||||
ng-disabled="!credentials || credentials.length === 0"
|
||||
class="btn btn-sm Form-saveButton" translate>
|
||||
class="btn btn-sm Form-saveButton"
|
||||
translate>
|
||||
SELECT
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -1,70 +1,70 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2017 Ansible, Inc.
|
||||
* Copyright (c) 2018 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
const templatePath = 'templates/job_templates/multi-credential/multi-credential';
|
||||
|
||||
export default ['templateUrl', '$compile',
|
||||
function(templateUrl, $compile) {
|
||||
return {
|
||||
scope: {
|
||||
credentials: '=',
|
||||
selectedCredentials: '=',
|
||||
prompt: '=',
|
||||
credentialNotPresent: '=',
|
||||
credentialsToPost: '=',
|
||||
fieldIsDisabled: '='
|
||||
},
|
||||
restrict: 'E',
|
||||
templateUrl: templateUrl('templates/job_templates/multi-credential/multi-credential'),
|
||||
controller: ['$scope', 'MultiCredentialService',
|
||||
function($scope, MultiCredentialService) {
|
||||
if (!$scope.selectedCredentials) {
|
||||
$scope.selectedCredentials = {
|
||||
machine: null,
|
||||
vault: null,
|
||||
extra: []
|
||||
};
|
||||
}
|
||||
function MultiCredential ($compile, templateUrl) {
|
||||
return {
|
||||
templateUrl: templateUrl(templatePath),
|
||||
require: ['multiCredential'],
|
||||
restrict: 'E',
|
||||
controllerAs: 'vm',
|
||||
scope: {
|
||||
selectedCredentials: '=',
|
||||
prompt: '=',
|
||||
credentialNotPresent: '=',
|
||||
fieldIsDisabled: '=',
|
||||
credentialTypes: '=',
|
||||
},
|
||||
link: (scope, element, attrs, controllers) => {
|
||||
const [controller] = controllers;
|
||||
|
||||
if (!$scope.credentialsToPost) {
|
||||
$scope.credentialsToPost = [];
|
||||
}
|
||||
scope.openModal = () => {
|
||||
const containerElement = $('#content-container');
|
||||
const templateFunction = $compile(`
|
||||
<multi-credential-modal
|
||||
credential-types="credentialTypes"
|
||||
selected-credentials="selectedCredentials">
|
||||
</multi-credential-modal>`);
|
||||
containerElement.append(templateFunction(scope));
|
||||
};
|
||||
|
||||
$scope.fieldDirty = false;
|
||||
controller.init(scope);
|
||||
},
|
||||
controller: multiCredentialController,
|
||||
};
|
||||
}
|
||||
|
||||
$scope.$watchGroup(['prompt', 'credentialsToPost'],
|
||||
function() {
|
||||
if ($scope.prompt ||
|
||||
$scope.credentialsToPost.length) {
|
||||
$scope.fieldDirty = true;
|
||||
}
|
||||
function multiCredentialController (MultiCredentialService) {
|
||||
const vm = this;
|
||||
const { createTag } = MultiCredentialService;
|
||||
|
||||
$scope.credentialNotPresent = !$scope.prompt &&
|
||||
$scope.selectedCredentials.machine === null &&
|
||||
$scope.selectedCredentials.vault === null;
|
||||
});
|
||||
let scope;
|
||||
|
||||
$scope.removeCredential = function(credToRemove) {
|
||||
[$scope.selectedCredentials,
|
||||
$scope.credentialsToPost] = MultiCredentialService
|
||||
.removeCredential(credToRemove, $scope.
|
||||
selectedCredentials,
|
||||
$scope.credentialsToPost);
|
||||
};
|
||||
}
|
||||
],
|
||||
link: function(scope) {
|
||||
scope.openMultiCredentialModal = function() {
|
||||
$('#content-container')
|
||||
.append($compile(`
|
||||
<multi-credential-modal
|
||||
credentials="credentials"
|
||||
credentials-to-post="credentialsToPost"
|
||||
selected-credentials="selectedCredentials">
|
||||
</multi-credential-modal>`)(scope));
|
||||
};
|
||||
}
|
||||
};
|
||||
vm.init = _scope_ => {
|
||||
scope = _scope_;
|
||||
scope.$watchCollection('selectedCredentials', onSelectedCredentialsChanged);
|
||||
};
|
||||
|
||||
function onSelectedCredentialsChanged (oldValues, newValues) {
|
||||
if (oldValues !== newValues) {
|
||||
scope.fieldDirty = (scope.prompt && scope.selectedCredentials.length > 0);
|
||||
scope.tags = scope.selectedCredentials.map(c => createTag(c, scope.credentialTypes));
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
vm.deselectCredential = ({ id }) => {
|
||||
const index = scope.selectedCredentials.map(c => c.id).indexOf(id);
|
||||
|
||||
if (index > -1) {
|
||||
scope.selectedCredentials.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
MultiCredential.$inject = ['$compile', 'templateUrl'];
|
||||
multiCredentialController.$inject = ['MultiCredentialService'];
|
||||
|
||||
export default MultiCredential;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<button type="button"
|
||||
class="Form-lookupButton
|
||||
Form-lookupButton--variableHeight btn btn-default"
|
||||
ng-click="openMultiCredentialModal()"
|
||||
ng-click="openModal()"
|
||||
ng-disabled="fieldIsDisabled">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
@ -16,19 +16,19 @@
|
||||
<div class="MultiCredential-tagSection">
|
||||
<div class="MultiCredential-flexContainer">
|
||||
<div class="MultiCredential-tagContainer ng-scope"
|
||||
ng-repeat="tag in credentialsToPost track by $index">
|
||||
ng-repeat="tag in tags track by $index">
|
||||
<div class="MultiCredential-deleteContainer"
|
||||
ng-click="removeCredential(tag.id)"
|
||||
ng-click="vm.deselectCredential(tag)"
|
||||
ng-hide="fieldIsDisabled || tag.readOnly">
|
||||
<i class="fa fa-times MultiCredential-tagDelete"></i>
|
||||
</div>
|
||||
<div class="MultiCredential-tag"
|
||||
ng-class="{'MultiCredential-tag--deletable': !fieldIsDisabled && !tag.readOnly, 'MultiCredential-tag--disabled': tag.readOnly}">
|
||||
<span ng-if="tag.kind !== 'Vault'" class="MultiCredential-name--label ng-binding">
|
||||
{{ tag.kind }}:
|
||||
<span ng-if="tag.info" class="MultiCredential-name--label ng-binding">
|
||||
{{ tag.typeName }} {{ tag.info }}:
|
||||
</span>
|
||||
<span ng-if="tag.kind === 'Vault'" class="MultiCredential-name--label ng-binding">
|
||||
{{ tag.kind }} ({{ tag.vault_id }}):
|
||||
<span ng-if="!tag.info" class="MultiCredential-name--label ng-binding">
|
||||
{{ tag.typeName }}:
|
||||
</span>
|
||||
<span class="MultiCredential-name u-wordwrap ng-binding">
|
||||
{{ tag.name }}
|
||||
|
||||
@ -1,292 +1,101 @@
|
||||
export default ['Rest', 'ProcessErrors', '$q', 'GetBasePath', function(Rest, ProcessErrors, $q, GetBasePath) {
|
||||
let val = {};
|
||||
function MultiCredentialService (Rest, ProcessErrors, $q, GetBasePath) {
|
||||
|
||||
// given an array of creds, POST them to url and return an array
|
||||
// of promises
|
||||
val.saveExtraCredentials = ({creds, url, disassociate = false,
|
||||
msg = "Failed to add extra credential. POST returned status:"}) => {
|
||||
if (creds && creds[0] && typeof creds[0] !== 'number') {
|
||||
creds = creds.map(cred => cred.id);
|
||||
const handleError = (method, resource, permitted = []) => {
|
||||
return ({ data, status }) => {
|
||||
if (permitted.indexOf(status) > -1) {
|
||||
return { data, status };
|
||||
}
|
||||
const hdr = 'Error!';
|
||||
const msg = `${resource} request failed. ${method} returned status: ${status}`;
|
||||
return ProcessErrors(null, data, status, null, { hdr, msg });
|
||||
};
|
||||
};
|
||||
|
||||
return creds.map((cred_id) => {
|
||||
let payload = {'id': cred_id};
|
||||
const associate = ({ related }, id) => {
|
||||
Rest.setUrl(related.credentials);
|
||||
return Rest
|
||||
.post({ id })
|
||||
.then(({ data }) => data.results)
|
||||
.catch(handleError('POST', 'credential association'));
|
||||
};
|
||||
|
||||
if (disassociate) {
|
||||
payload.disassociate = true;
|
||||
}
|
||||
const disassociate = ({ related }, id) => {
|
||||
Rest.setUrl(related.credentials);
|
||||
return Rest
|
||||
.post({ id, disassociate: true })
|
||||
.catch(handleError('POST', 'credential disassociation'));
|
||||
};
|
||||
|
||||
Rest.setUrl(url);
|
||||
this.saveRelated = ({ related }, credentials) => {
|
||||
Rest.setUrl(related.credentials);
|
||||
return Rest
|
||||
.get()
|
||||
.then(({ data }) => {
|
||||
const currentlyAssociated = data.results.map(c => c.id);
|
||||
const selected = credentials.map(c => c.id);
|
||||
|
||||
return Rest.post(payload)
|
||||
.catch(({data, status}) => {
|
||||
ProcessErrors(
|
||||
null, data, status, null,
|
||||
{
|
||||
hdr: 'Error!',
|
||||
msg: `${msg} ${status}`
|
||||
});
|
||||
});
|
||||
const disassociationPromises = currentlyAssociated
|
||||
.filter(id => selected.indexOf(id) < 0)
|
||||
.map(id => disassociate({ related }, id));
|
||||
|
||||
const associationPromises = selected
|
||||
.filter(id => currentlyAssociated.indexOf(id) < 0)
|
||||
.map(id => associate({ related }, id));
|
||||
|
||||
const promises = disassociationPromises
|
||||
.concat(associationPromises);
|
||||
|
||||
return $q.all(promises);
|
||||
});
|
||||
};
|
||||
|
||||
// removes credentials no longer a part of the jt, and adds
|
||||
// new ones
|
||||
val.findChangedExtraCredentials = ({creds, url}) => {
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.then(({data}) => {
|
||||
let existingCreds = data.results
|
||||
.map(cred => cred.id);
|
||||
|
||||
let newCreds = creds
|
||||
.map(cred => cred.id);
|
||||
|
||||
let [toAdd, toRemove] = _.partition(_.xor(existingCreds,
|
||||
newCreds), cred => (newCreds.indexOf(cred) > -1));
|
||||
|
||||
let destroyResolve = [];
|
||||
|
||||
destroyResolve = val.saveExtraCredentials({
|
||||
creds: toRemove,
|
||||
url: url,
|
||||
disassociate: true,
|
||||
msg: `Failed to disassociate existing credential.
|
||||
POST returned status:`
|
||||
});
|
||||
|
||||
$q.all(destroyResolve).then(() => {
|
||||
val.saveExtraCredentials({
|
||||
creds: toAdd,
|
||||
url: url
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
.catch(({data, status}) => {
|
||||
ProcessErrors(null, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get existing extra credentials. GET ' +
|
||||
'returned status: ' + status
|
||||
});
|
||||
});
|
||||
this.getRelated = ({ related }, params = { permitted: [] }) => {
|
||||
Rest.setUrl(related.credentials);
|
||||
return Rest
|
||||
.get()
|
||||
.catch(handleError('GET', 'related credentials', params.permitted));
|
||||
};
|
||||
|
||||
// calls credential types and returns the data needed to set up the
|
||||
// credential type selector
|
||||
val.getCredentialTypes = () => {
|
||||
this.getCredentials = () => {
|
||||
Rest.setUrl(GetBasePath('credentials'));
|
||||
return Rest
|
||||
.get()
|
||||
.catch(handleError('GET', 'related credentials'));
|
||||
};
|
||||
|
||||
this.getCredentialTypes = () => {
|
||||
Rest.setUrl(GetBasePath('credential_types'));
|
||||
return Rest.get()
|
||||
.then(({data}) => {
|
||||
let credential_types = {}, credentialTypeOptions = [];
|
||||
|
||||
data.results.forEach((credentialType => {
|
||||
credential_types[credentialType.id] = credentialType;
|
||||
if(credentialType.kind
|
||||
.match(/^(machine|cloud|net|ssh|vault)$/)) {
|
||||
credentialTypeOptions.push({
|
||||
name: credentialType.name,
|
||||
value: credentialType.id
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
return {
|
||||
credential_types,
|
||||
credentialTypeOptions
|
||||
};
|
||||
})
|
||||
.catch(({data, status}) => {
|
||||
ProcessErrors(null, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get credential types. GET returned ' +
|
||||
'status: ' + status
|
||||
});
|
||||
});
|
||||
return Rest
|
||||
.get()
|
||||
.catch(handleError('GET', 'credential types'));
|
||||
};
|
||||
|
||||
// converts structured selected credential data into array for tag-based
|
||||
// view
|
||||
val.updateCredentialTags = (creds, typeOpts) => {
|
||||
let machineCred = [];
|
||||
let extraCreds = [];
|
||||
let vaultCred = [];
|
||||
this.isReadOnly = credential => {
|
||||
const canEdit = _.get(credential, 'summary_fields.user_capabilities.edit');
|
||||
const canDelete = _.get(credential, 'summary_fields.user_capabilities.delete');
|
||||
|
||||
if (creds.machine) {
|
||||
let mach = creds.machine;
|
||||
mach.postType = "machine";
|
||||
machineCred = [mach];
|
||||
}
|
||||
|
||||
if (creds.vault) {
|
||||
let vault = creds.vault;
|
||||
vault.postType = "vault";
|
||||
vaultCred = [vault];
|
||||
}
|
||||
|
||||
if (creds.extra) {
|
||||
extraCreds = creds.extra
|
||||
.map((cred) => {
|
||||
cred.postType = "extra";
|
||||
|
||||
return cred;
|
||||
});
|
||||
}
|
||||
|
||||
return machineCred.concat(extraCreds).concat(vaultCred).map(cred => {
|
||||
const { name, id, postType, readOnly } = cred;
|
||||
const [type] = typeOpts.filter(type => parseInt(cred.credential_type) === type.value)
|
||||
|
||||
const tagData = {
|
||||
name: cred.name,
|
||||
id: cred.id,
|
||||
postType: cred.postType,
|
||||
readOnly: cred.readOnly ? true : false,
|
||||
kind: `${type.name}`
|
||||
};
|
||||
|
||||
if (type.name === 'Vault') {
|
||||
tagData.vault_id = _.get(cred, 'inputs.vault_id');
|
||||
}
|
||||
|
||||
return tagData;
|
||||
})
|
||||
return !(canEdit || canDelete);
|
||||
};
|
||||
|
||||
// remove credential from structured selected credential data and tag-view
|
||||
// array
|
||||
val.removeCredential = (credToRemove, structuredObj, tagArr) => {
|
||||
tagArr.forEach((cred) => {
|
||||
if (credToRemove === cred.id) {
|
||||
if (cred.postType === 'machine') {
|
||||
structuredObj[cred.postType] = null;
|
||||
} else if (cred.postType === 'vault') {
|
||||
structuredObj[cred.postType] = null;
|
||||
} else {
|
||||
structuredObj[cred.postType] = structuredObj[cred.postType]
|
||||
.filter(cred => cred
|
||||
.id !== credToRemove);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.createTag = (credential, credential_types) => {
|
||||
const credentialTypeId = credential.credential_type || credential.credential_type_id;
|
||||
const credentialType = credential_types.find(t => t.id === credentialTypeId);
|
||||
|
||||
tagArr = tagArr
|
||||
.filter(cred => cred.id !== credToRemove);
|
||||
|
||||
return [structuredObj, tagArr];
|
||||
return {
|
||||
id: credential.id,
|
||||
name: credential.name,
|
||||
typeName: _.get(credentialType, 'name'),
|
||||
info: _.get(credential, 'inputs.vault_id'),
|
||||
readOnly: this.isReadOnly(credential),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// load all relevant credential data to populate job template edit form
|
||||
val.loadCredentials = (data) => {
|
||||
let selectedCredentials = {
|
||||
machine: null,
|
||||
vault: null,
|
||||
extra: []
|
||||
}, credTypes, credTypeOptions, credTags;
|
||||
MultiCredentialService.$inject = [
|
||||
'Rest',
|
||||
'ProcessErrors',
|
||||
'$q',
|
||||
'GetBasePath',
|
||||
];
|
||||
|
||||
let credDefers = [];
|
||||
let job_template_obj = data;
|
||||
let credentialGetPermissionDenied = false;
|
||||
|
||||
// get machine credential
|
||||
// if (data.related.credential) {
|
||||
// Rest.setUrl(data.related.credential);
|
||||
// credDefers.push(Rest.get()
|
||||
// .then(({data}) => {
|
||||
// selectedCredentials.machine = data;
|
||||
// })
|
||||
// .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,
|
||||
// {
|
||||
// hdr: 'Error!',
|
||||
// msg: 'Failed to get machine credential. ' +
|
||||
// 'Get returned status: ' +
|
||||
// status
|
||||
// });
|
||||
// }
|
||||
// }));
|
||||
// }
|
||||
|
||||
// if (data.related.vault_credential) {
|
||||
// Rest.setUrl(data.related.vault_credential);
|
||||
// credDefers.push(Rest.get()
|
||||
// .then(({data}) => {
|
||||
// selectedCredentials.vault = data;
|
||||
// })
|
||||
// .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,
|
||||
// {
|
||||
// hdr: 'Error!',
|
||||
// msg: 'Failed to get machine credential. ' +
|
||||
// 'Get returned status: ' +
|
||||
// status
|
||||
// });
|
||||
// }
|
||||
// }));
|
||||
// }
|
||||
|
||||
// get credentials
|
||||
if (data.related.credentials) {
|
||||
Rest.setUrl(data.related.credentials);
|
||||
credDefers.push(Rest.get()
|
||||
.then(({data}) => {
|
||||
selectedCredentials.extra = data.results;
|
||||
})
|
||||
.catch(({data, status}) => {
|
||||
if (status === 403) {
|
||||
/* User doesn't have read access to the credentials, so use summary_fields */
|
||||
credentialGetPermissionDenied = true;
|
||||
selectedCredentials.extra = job_template_obj.summary_fields.credentials;
|
||||
_.map(selectedCredentials.extra, (cred) => {
|
||||
cred.credential_type = cred.credential_type_id;
|
||||
cred.readOnly = true;
|
||||
return cred;
|
||||
});
|
||||
} else {
|
||||
ProcessErrors(null, data, status, null,
|
||||
{
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get extra credentials. ' +
|
||||
'Get returned status: ' +
|
||||
status
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// get credential types
|
||||
credDefers.push(val.getCredentialTypes()
|
||||
.then(({credential_types, credentialTypeOptions}) => {
|
||||
credTypes = credential_types;
|
||||
credTypeOptions = credentialTypeOptions;
|
||||
}));
|
||||
|
||||
return $q.all(credDefers).then(() => {
|
||||
// get credential tags
|
||||
credTags = val
|
||||
.updateCredentialTags(selectedCredentials, credTypeOptions);
|
||||
|
||||
return [selectedCredentials, credTypes, credTypeOptions,
|
||||
credTags, credentialGetPermissionDenied];
|
||||
});
|
||||
};
|
||||
|
||||
return val;
|
||||
}];
|
||||
export default MultiCredentialService;
|
||||
|
||||
@ -1,416 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('MultiCredentialService', () => {
|
||||
let MultiCredentialService;
|
||||
|
||||
beforeEach(angular.mock.module('multiCredential',
|
||||
($provide) => {
|
||||
['Rest', 'ProcessErrors', '$q', 'GetBasePath']
|
||||
.forEach(item => $provide.value(item, {}));
|
||||
}));
|
||||
|
||||
beforeEach(angular.mock.inject((_MultiCredentialService_) => {
|
||||
MultiCredentialService = _MultiCredentialService_;
|
||||
}));
|
||||
|
||||
describe('saveExtraCredentials', () => {
|
||||
xit('should handle creds as array of objects and array of ids', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should post creds with add payload', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should post creds with disassociate payload', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should call ProcessErrors when post fails', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findChangedExtraCredentials', () => {
|
||||
xit('should find which creds to add and post them', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should find which creds to remove and disassociate them', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should not post/disassociate non-changed creds', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should call ProcessErrors when any get/post fails', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCredentialTypes', () => {
|
||||
xit('should get cred types and return them directly, as well ' +
|
||||
'as options for building credential type select box', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should call ProcessErrors when getting cred types fails', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateCredentialTags', () => {
|
||||
it('should return array of selected credentials (empty)', () => {
|
||||
let creds = {
|
||||
machine: null,
|
||||
extra: []
|
||||
};
|
||||
|
||||
let typeOpts = [];
|
||||
|
||||
let expected = [];
|
||||
|
||||
let actual = MultiCredentialService
|
||||
.updateCredentialTags(creds, typeOpts);
|
||||
|
||||
let equal = _.isEqual(expected.sort(), actual.sort());
|
||||
|
||||
expect(equal).toBe(true);
|
||||
});
|
||||
|
||||
it('should return array of selected credentials (populated, not read only)', () => {
|
||||
let creds = {
|
||||
machine: {
|
||||
credential_type: 1,
|
||||
id: 3,
|
||||
name: 'ssh'
|
||||
},
|
||||
extra: [
|
||||
{
|
||||
credential_type: 2,
|
||||
id: 4,
|
||||
name: 'aws'
|
||||
},
|
||||
{
|
||||
credential_type: 3,
|
||||
id: 5,
|
||||
name: 'gce'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
let typeOpts = [
|
||||
{
|
||||
name: 'SSH',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
name: 'Amazon Web Services',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
name: 'Google Compute Engine',
|
||||
value: 3
|
||||
}
|
||||
];
|
||||
|
||||
let expected = [
|
||||
{
|
||||
name: 'ssh',
|
||||
id: 3,
|
||||
postType: 'machine',
|
||||
readOnly: false,
|
||||
kind: 'SSH:'
|
||||
},
|
||||
{
|
||||
name: 'aws',
|
||||
id: 4,
|
||||
postType: 'extra',
|
||||
readOnly: false,
|
||||
kind: 'Amazon Web Services:'
|
||||
},
|
||||
{
|
||||
name: 'gce',
|
||||
id: 5,
|
||||
postType: 'extra',
|
||||
readOnly: false,
|
||||
kind: 'Google Compute Engine:'
|
||||
}
|
||||
];
|
||||
|
||||
let actual = MultiCredentialService
|
||||
.updateCredentialTags(creds, typeOpts);
|
||||
|
||||
let equal = _.isEqual(expected.sort(), actual.sort());
|
||||
|
||||
expect(equal).toBe(true);
|
||||
});
|
||||
|
||||
it('should return array of selected credentials (populated, read only)', () => {
|
||||
let creds = {
|
||||
machine: {
|
||||
credential_type: 1,
|
||||
id: 3,
|
||||
name: 'ssh',
|
||||
readOnly: true
|
||||
},
|
||||
extra: [
|
||||
{
|
||||
credential_type: 2,
|
||||
id: 4,
|
||||
name: 'aws',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
credential_type: 3,
|
||||
id: 5,
|
||||
name: 'gce',
|
||||
readOnly: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
let typeOpts = [
|
||||
{
|
||||
name: 'SSH',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
name: 'Amazon Web Services',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
name: 'Google Compute Engine',
|
||||
value: 3
|
||||
}
|
||||
];
|
||||
|
||||
let expected = [
|
||||
{
|
||||
name: 'ssh',
|
||||
id: 3,
|
||||
postType: 'machine',
|
||||
readOnly: true,
|
||||
kind: 'SSH:'
|
||||
},
|
||||
{
|
||||
name: 'aws',
|
||||
id: 4,
|
||||
postType: 'extra',
|
||||
readOnly: true,
|
||||
kind: 'Amazon Web Services:'
|
||||
},
|
||||
{
|
||||
name: 'gce',
|
||||
id: 5,
|
||||
postType: 'extra',
|
||||
readOnly: true,
|
||||
kind: 'Google Compute Engine:'
|
||||
}
|
||||
];
|
||||
|
||||
let actual = MultiCredentialService
|
||||
.updateCredentialTags(creds, typeOpts);
|
||||
|
||||
let equal = _.isEqual(expected.sort(), actual.sort());
|
||||
|
||||
expect(equal).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeCredential', () => {
|
||||
it('should remove machine cred from structured obj and tag arr', () => {
|
||||
let credToRemove = 3;
|
||||
|
||||
let structuredObj = {
|
||||
machine: {
|
||||
credential_type: 1,
|
||||
id: 3,
|
||||
name: 'ssh'
|
||||
},
|
||||
extra: [
|
||||
{
|
||||
credential_type: 2,
|
||||
id: 4,
|
||||
name: 'aws'
|
||||
},
|
||||
{
|
||||
credential_type: 3,
|
||||
id: 5,
|
||||
name: 'gce'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
let tagArr = [
|
||||
{
|
||||
name: 'ssh',
|
||||
id: 3,
|
||||
postType: 'machine',
|
||||
kind: 'SSH:'
|
||||
},
|
||||
{
|
||||
name: 'aws',
|
||||
id: 4,
|
||||
postType: 'extra',
|
||||
kind: 'Amazon Web Services:'
|
||||
},
|
||||
{
|
||||
name: 'gce',
|
||||
id: 5,
|
||||
postType: 'extra',
|
||||
kind: 'Google Compute Engine:'
|
||||
}
|
||||
];
|
||||
|
||||
let expected = [
|
||||
{
|
||||
machine: null,
|
||||
extra: [
|
||||
{
|
||||
credential_type: 2,
|
||||
id: 4,
|
||||
name: 'aws'
|
||||
},
|
||||
{
|
||||
credential_type: 3,
|
||||
id: 5,
|
||||
name: 'gce'
|
||||
}
|
||||
]
|
||||
},
|
||||
[
|
||||
{
|
||||
name: 'aws',
|
||||
id: 4,
|
||||
postType: 'extra',
|
||||
kind: 'Amazon Web Services:'
|
||||
},
|
||||
{
|
||||
name: 'gce',
|
||||
id: 5,
|
||||
postType: 'extra',
|
||||
kind: 'Google Compute Engine:'
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
let actual = MultiCredentialService
|
||||
.removeCredential(credToRemove, structuredObj, tagArr);
|
||||
|
||||
let equal = _.isEqual(expected.sort(), actual.sort());
|
||||
|
||||
expect(equal).toBe(true);
|
||||
});
|
||||
|
||||
it('should remove extra cred from structured obj and tag arr', () => {
|
||||
let credToRemove = 4;
|
||||
|
||||
let structuredObj = {
|
||||
machine: {
|
||||
credential_type: 1,
|
||||
id: 3,
|
||||
name: 'ssh'
|
||||
},
|
||||
extra: [
|
||||
{
|
||||
credential_type: 2,
|
||||
id: 4,
|
||||
name: 'aws'
|
||||
},
|
||||
{
|
||||
credential_type: 3,
|
||||
id: 5,
|
||||
name: 'gce'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
let tagArr = [
|
||||
{
|
||||
name: 'ssh',
|
||||
id: 3,
|
||||
postType: 'machine',
|
||||
kind: 'SSH:'
|
||||
},
|
||||
{
|
||||
name: 'aws',
|
||||
id: 4,
|
||||
postType: 'extra',
|
||||
kind: 'Amazon Web Services:'
|
||||
},
|
||||
{
|
||||
name: 'gce',
|
||||
id: 5,
|
||||
postType: 'extra',
|
||||
kind: 'Google Compute Engine:'
|
||||
}
|
||||
];
|
||||
|
||||
let expected = [
|
||||
{
|
||||
machine: {
|
||||
credential_type: 1,
|
||||
id: 3,
|
||||
name: 'ssh'
|
||||
},
|
||||
extra: [
|
||||
{
|
||||
credential_type: 3,
|
||||
id: 5,
|
||||
name: 'gce'
|
||||
}
|
||||
]
|
||||
},
|
||||
[
|
||||
{
|
||||
name: 'ssh',
|
||||
id: 3,
|
||||
postType: 'machine',
|
||||
kind: 'SSH:'
|
||||
},
|
||||
{
|
||||
name: 'gce',
|
||||
id: 5,
|
||||
postType: 'extra',
|
||||
kind: 'Google Compute Engine:'
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
let actual = MultiCredentialService
|
||||
.removeCredential(credToRemove, structuredObj, tagArr);
|
||||
|
||||
let equal = _.isEqual(expected.sort(), actual.sort());
|
||||
|
||||
expect(equal).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadCredentials', () => {
|
||||
xit('should call to get machine credential data', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should call ProcessErrors if machine cred get fails', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should call to get extra credentials data', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should call ProcessErrors if extra creds get fails', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should call to get credential types', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
|
||||
xit('should call to update cred tags once GETs have completed', () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user