support server-side webhook key generation

This commit is contained in:
Jake McDermott 2019-09-09 13:04:00 -04:00 committed by Jeff Bradberry
parent 178a2c7c49
commit 5f7bfaa20a
4 changed files with 102 additions and 46 deletions

View File

@ -794,9 +794,21 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
}
if (field.genHash) {
html += "<span class=\"input-group-btn input-group-prepend\"><button type=\"button\" class=\"btn Form-lookupButton\" ng-click=\"genHash('" + fld + "')\" " +
"aw-tool-tip=\"Generate " + field.label + "\" data-placement=\"top\" id=\"" + this.form.name + "_" + fld + "_gen_btn\">" +
"<i class=\"fa fa-magic\"></i></button></span>\n</div>\n";
const defaultGenHashButtonTemplate = `
<span class="input-group-btn input-group-prepend">
<button
type="button"
class="btn Form-lookupButton"
ng-click="genHash('${fld}')"
aw-tool-tip="Generate ${field.label}"
data-placement="top"
id="${this.form.name}_${fld}_gen_btn"
>
<i class="fa fa-refresh" />
</button>
</span>`;
const genHashButtonTemplate = _.get(field, 'genHashButtonTemplate', defaultGenHashButtonTemplate);
html += `${genHashButtonTemplate}\n</div>\n`;
}
// Add error messages

View File

@ -44,6 +44,7 @@
$scope.parseType = 'yaml';
$scope.credentialNotPresent = false;
$scope.canGetAllRelatedResources = true;
$scope.webhook_key_help = i18n._('Webhook services can use this as a shared secret.');
//
// webhook credential - all handlers, dynamic state, etc. live here
@ -105,6 +106,8 @@
$scope.webhookCredential.modalSelectedName = null;
};
$scope.handleWebhookKeyButtonClick = () => {};
$('#content-container').append($compile(`
<at-dialog
title="webhookCredential.modalTitle"
@ -159,6 +162,9 @@
default_val: false
});
CallbackHelpInit({ scope: $scope });
// set initial vals for webhook checkbox
$scope.enable_webhook = false;
master.enable_webhook = false;
$scope.surveyTooltip = i18n._('Please save before adding a survey to this job template.');
@ -462,6 +468,7 @@
delete data.credential;
delete data.vault_credential;
delete data.webhook_url;
delete data.webhook_key;
data.webhook_credential = $scope.webhookCredential.id;
if (!data.webhook_credential) {
data.webhook_service = null;

View File

@ -76,24 +76,28 @@ export default
const virtualEnvs = ConfigData.custom_virtualenvs || [];
$scope.custom_virtualenvs_options = virtualEnvs;
$scope.webhook_url_help = i18n._('Webhook services can launch jobs with this job template by making a POST request to this URL.');
$scope.webhook_key_help = i18n._('Webhook services can use this as a shared secret.');
$scope.currentlySavedWebhookKey = webhookKey;
$scope.webhook_key = webhookKey;
//
// webhook credential - all handlers, dynamic state, etc. live here
//
$scope.webhook_key = webhookKey;
$scope.webhookCredential = {
id: _.get(jobTemplateData, ['summary_fields', 'webhook_credential', 'id']),
name: _.get(jobTemplateData, ['summary_fields', 'webhook_credential', 'name']),
isModalOpen: false,
isModalReady: false,
modalTitle: i18n._('Select Webhook Credential'),
modalSelectedId: null,
modalSelectedName: null,
modalBaseParams: {
order_by: 'name',
page_size: 5,
credential_type__namespace: `${jobTemplateData.webhook_service}_token`,
},
modalSelectedId: null,
modalSelectedName: null,
modalTitle: i18n._('Select Webhook Credential'),
};
$scope.handleWebhookCredentialLookupClick = () => {
@ -125,7 +129,6 @@ export default
$scope.webhookCredential.isModalReady = false;
$scope.webhookCredential.modalSelectedId = null;
$scope.webhookCredential.modalSelectedName = null;
};
$scope.handleWebhookCredentialSelect = () => {
@ -137,6 +140,23 @@ export default
$scope.webhookCredential.modalSelectedName = null;
};
$scope.handleWebhookKeyButtonClick = () => {
Rest.setUrl(jobTemplateData.related.webhook_key);
Wait('start');
Rest.post({})
.then(({ data }) => {
$scope.currentlySavedWebhookKey = data.webhook_key;
$scope.webhook_key = data.webhook_key;
})
.catch(({ data }) => {
const errorMsg = `Failed to generate new webhook key. POST returned status: ${status}`;
ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: errorMsg });
})
.finally(() => {
Wait('stop');
});
};
$('#content-container').append($compile(`
<at-dialog
title="webhookCredential.modalTitle"
@ -185,6 +205,13 @@ export default
$scope.webhookCredential.id = null;
$scope.webhookCredential.name = null;
}
if (newServiceValue !== newValue) {
if (newServiceValue === jobTemplateData.webhook_service) {
$scope.webhook_key = $scope.currentlySavedWebhookKey;
} else {
$scope.webhook_key = i18n._('A NEW WEBHOOK KEY WILL BE GENERATED ON SAVE');
}
}
}
});
@ -430,13 +457,14 @@ export default
default_val: dft
});
const defaultWebhookKey = ($scope.webhook_key === "" || $scope.webhook_key === null) ? false : true;
hashSetup({
scope: $scope,
master: master,
check_field: 'enable_webhooks',
default_val: defaultWebhookKey
});
// set initial vals for webhook checkbox
if (jobTemplateData.webhook_service) {
$scope.enable_webhook = true;
master.enable_webhook = true;
} else {
$scope.enable_webhook = false;
master.enable_webhook = false;
}
ParseTypeChange({
scope: $scope,
@ -703,14 +731,6 @@ export default
});
});
let webhookKeyPromise = Promise.resolve();
if ($scope.webhook_key !== webhookKey) {
Rest.setUrl(jobTemplateData.related.webhook_key);
webhookKeyPromise = Rest.post({ webhook_key: $scope.webhook_key });
}
var orgDefer = $q.defer();
var associationDefer = $q.defer();
var associatedLabelsDefer = $q.defer();
@ -783,7 +803,6 @@ export default
for (var i = 0; i < toPost.length; i++) {
defers.push(Rest.post(toPost[i]));
}
defers.push(webhookKeyPromise);
$q.all(defers)
.then(function() {
Wait('stop');
@ -888,11 +907,18 @@ export default
data.skip_tags = (Array.isArray($scope.skip_tags)) ? _.uniq($scope.skip_tags).join() : "";
delete data.webhook_url;
data.webhook_credential = $scope.webhookCredential.id;
if (!data.webhook_credential) {
data.webhook_service = null;
}
delete data.webhook_key;
delete data.enable_webhook;
data.webhook_credential = $scope.webhookCredential.id;
if (!data.webhook_service) {
data.webhook_credential = null;
}
if (!$scope.enable_webhook) {
data.webhook_service = '';
data.webhook_credential = null;
}
Rest.setUrl(defaultUrl + $state.params.job_template_id);
Rest.patch(data)

View File

@ -339,10 +339,9 @@ function(NotificationsList, i18n) {
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
}, {
name: 'enable_webhooks',
label: i18n._('Enable Webhook'),
name: 'enable_webhook',
label: i18n._('Webhooks'),
type: 'checkbox',
ngChange: "toggleCallback('webhook_key')",
column: 2,
awPopOver: "<p>" + i18n._("Enabled webhook for this job template.") + "</p>",
dataPlacement: 'right',
@ -407,10 +406,9 @@ function(NotificationsList, i18n) {
type:'select',
defaultText: i18n._('Choose a Webhook Service'),
ngOptions: 'svc.label for svc in webhook_service_options track by svc.value',
ngShow: "enable_webhooks && enable_webhooks !== 'false'",
ngShow: "enable_webhook && enable_webhook !== 'false'",
ngDisabled: "!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate) || !canGetAllRelatedResources",
id: 'webhook-service-select',
required: false,
column: 1,
awPopOver: "<p>" + i18n._("Select a webhook service.") + "</p>",
dataTitle: i18n._('Webhook Service'),
@ -420,36 +418,49 @@ function(NotificationsList, i18n) {
webhook_url: {
label: i18n._('Webhook URL'),
type: 'text',
ngShow: "enable_webhooks && enable_webhooks !== 'false'",
column: 2,
ngShow: "job_template_obj && enable_webhook && enable_webhook !== 'false'",
awPopOver: "webhook_url_help",
awPopOverWatch: "webhook_url_help",
dataPlacement: 'top',
dataTitle: i18n._('Webhook URL'),
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
readonly: true
},
webhook_key: {
label: i18n._('Webhook Key'),
type: 'text',
ngShow: "enable_webhooks && enable_webhooks !== 'false'",
ngShow: "enable_webhook && enable_webhook !== 'false'",
genHash: true,
column: 2,
genHashButtonTemplate: `
<span
ng-if="job_template_obj && currentlySavedWebhookKey === webhook_key"
class="input-group-btn input-group-prepend"
>
<button
type="button"
class="btn Form-lookupButton"
ng-click="handleWebhookKeyButtonClick()"
aw-tool-tip="${i18n._('Rotate Webhook Key')}"
data-placement="top"
id="job_template_webhook_key_gen_btn"
>
<i class="fa fa-refresh" />
</button>
</span>
`,
genHashButtonClickHandlerName: "handleWebhookKeyButtonClick",
awPopOver: "webhook_key_help",
awPopOverWatch: "webhook_key_help",
dataPlacement: 'right',
dataTitle: i18n._("Webhook Config Key"),
dataTitle: i18n._("Webhook Key"),
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)',
awRequiredWhen: {
reqExpression: 'enable_webhooks',
alwaysShowAsterisk: true
}
readonly: true,
required: false,
},
webhook_credential: {
label: i18n._('Webhook Credential'),
type: 'custom',
ngShow: "enable_webhooks && enable_webhooks !== 'false'",
ngShow: "enable_webhook && enable_webhook !== 'false'",
control: `
<webhook-credential-input
is-field-disabled="!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate) || !(webhookCredential.modalBaseParams.credential_type__namespace)"
@ -461,7 +472,7 @@ function(NotificationsList, i18n) {
dataTitle: i18n._('Webhook Credential'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: 'canAddJobTemplate',
ngDisabled: '!(webhook_key || webhook_key.value)',
required: false,
},
extra_vars: {