[8.11] [Fleet] Support integration secrets with required: false (#172078) (#172201)

# Backport

This will backport the following commits from `main` to `8.11`:
- [[Fleet] Support integration secrets with `required: false`
(#172078)](https://github.com/elastic/kibana/pull/172078)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Kyle
Pollich","email":"kyle.pollich@elastic.co"},"sourceCommit":{"committedDate":"2023-11-28T16:52:01Z","message":"[Fleet]
Support integration secrets with `required: false` (#172078)\n\n##
Summary\r\n\r\nSupport secrets with `required: false` in package
manifests.\r\n\r\nCloses #172061\r\n\r\n## To test\r\n\r\n1. Set up an
integration in a local package registry with a variable\r\nthat has
`secret: true` and `required: false`, e.g.\r\n\r\n```yml\r\n- name:
secret_token\r\n type: password\r\n title: (Test) Secret Token\r\n
description: |\r\n Test non-required secret\r\n show_user: true\r\n
secret: true\r\n required: false\r\n```\r\n\r\n2. Create a package
policy for your test package and note the optional\r\nsecret is rendered
properly\r\n3. Submit the policy editor form without filling out a value
for the\r\noptional secret\r\n4. Observe the request is successful\r\n5.
Edit the package policy and set a value for the optional secret\r\n6.
Observe that the secret creation logic works as expected\r\n\r\n##
Screen
recording\r\n\r\n\r\n36e271c5-29d0-49f8-91e8-abc6a7871b20","sha":"e64f475a013e1160f0938e14f8da7dcbf97a44bb","branchLabelMapping":{"^v8.12.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:Fleet","backport:prev-minor","v8.12.0"],"number":172078,"url":"https://github.com/elastic/kibana/pull/172078","mergeCommit":{"message":"[Fleet]
Support integration secrets with `required: false` (#172078)\n\n##
Summary\r\n\r\nSupport secrets with `required: false` in package
manifests.\r\n\r\nCloses #172061\r\n\r\n## To test\r\n\r\n1. Set up an
integration in a local package registry with a variable\r\nthat has
`secret: true` and `required: false`, e.g.\r\n\r\n```yml\r\n- name:
secret_token\r\n type: password\r\n title: (Test) Secret Token\r\n
description: |\r\n Test non-required secret\r\n show_user: true\r\n
secret: true\r\n required: false\r\n```\r\n\r\n2. Create a package
policy for your test package and note the optional\r\nsecret is rendered
properly\r\n3. Submit the policy editor form without filling out a value
for the\r\noptional secret\r\n4. Observe the request is successful\r\n5.
Edit the package policy and set a value for the optional secret\r\n6.
Observe that the secret creation logic works as expected\r\n\r\n##
Screen
recording\r\n\r\n\r\n36e271c5-29d0-49f8-91e8-abc6a7871b20","sha":"e64f475a013e1160f0938e14f8da7dcbf97a44bb"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.12.0","labelRegex":"^v8.12.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/172078","number":172078,"mergeCommit":{"message":"[Fleet]
Support integration secrets with `required: false` (#172078)\n\n##
Summary\r\n\r\nSupport secrets with `required: false` in package
manifests.\r\n\r\nCloses #172061\r\n\r\n## To test\r\n\r\n1. Set up an
integration in a local package registry with a variable\r\nthat has
`secret: true` and `required: false`, e.g.\r\n\r\n```yml\r\n- name:
secret_token\r\n type: password\r\n title: (Test) Secret Token\r\n
description: |\r\n Test non-required secret\r\n show_user: true\r\n
secret: true\r\n required: false\r\n```\r\n\r\n2. Create a package
policy for your test package and note the optional\r\nsecret is rendered
properly\r\n3. Submit the policy editor form without filling out a value
for the\r\noptional secret\r\n4. Observe the request is successful\r\n5.
Edit the package policy and set a value for the optional secret\r\n6.
Observe that the secret creation logic works as expected\r\n\r\n##
Screen
recording\r\n\r\n\r\n36e271c5-29d0-49f8-91e8-abc6a7871b20","sha":"e64f475a013e1160f0938e14f8da7dcbf97a44bb"}}]}]
BACKPORT-->

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Kyle Pollich 2023-11-29 15:18:44 -05:00 committed by GitHub
parent c504960d67
commit c4b7c82eae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 834 additions and 707 deletions

View file

@ -338,8 +338,8 @@ const SecretFieldWrapper = ({ children }: { children: React.ReactNode }) => {
const SecretFieldLabel = ({ fieldLabel }: { fieldLabel: string }) => {
return (
<>
<EuiFlexGroup alignItems="center" gutterSize="xs">
<EuiFlexItem grow={true} aria-label={fieldLabel}>
<EuiFlexGroup alignItems="flexEnd" gutterSize="xs">
<EuiFlexItem grow={false} aria-label={fieldLabel}>
{fieldLabel}
</EuiFlexItem>
<EuiFlexItem grow={false}>
@ -376,12 +376,37 @@ function SecretInputField({
setIsDirty,
isDirty,
}: InputComponentProps) {
const [editMode, setEditMode] = useState(isEditPage && !value);
const [isReplacing, setIsReplacing] = useState(isEditPage && !value);
const valueOnFirstRender = useRef(value);
const hasExistingValue = !!valueOnFirstRender.current;
const lowercaseTitle = varDef.title?.toLowerCase();
const showInactiveReplaceUi = isEditPage && !isReplacing && hasExistingValue;
const valueIsSecretRef = value && value?.isSecretRef;
if (isEditPage && !editMode) {
const inputComponent = getInputComponent({
varDef,
value: isReplacing && valueIsSecretRef ? '' : value,
onChange,
frozen,
packageName,
packageType,
datastreams,
isEditPage,
isInvalid,
fieldLabel,
fieldTestSelector,
isDirty,
setIsDirty,
});
// If there's no value for this secret, display the input as its "brand new" creation state
// instead of the "replace" state
if (!hasExistingValue) {
return inputComponent;
}
if (showInactiveReplaceUi) {
return (
<>
<EuiText size="s" color="subdued">
@ -395,7 +420,7 @@ function SecretInputField({
</EuiText>
<EuiSpacer size="s" />
<EuiButtonEmpty
onClick={() => setEditMode(true)}
onClick={() => setIsReplacing(true)}
color="primary"
iconType="refresh"
iconSide="left"
@ -413,28 +438,11 @@ function SecretInputField({
);
}
const valueIsSecretRef = value && value?.isSecretRef;
const field = getInputComponent({
varDef,
value: editMode && valueIsSecretRef ? '' : value,
onChange,
frozen,
packageName,
packageType,
datastreams,
isEditPage,
isInvalid,
fieldLabel,
fieldTestSelector,
isDirty,
setIsDirty,
});
if (editMode) {
if (isReplacing) {
const cancelButton = (
<EuiButtonEmpty
onClick={() => {
setEditMode(false);
setIsReplacing(false);
setIsDirty(false);
onChange(valueOnFirstRender.current);
}}
@ -455,12 +463,12 @@ function SecretInputField({
return (
<EuiFlexGroup direction="column" gutterSize="s" alignItems="flexStart">
<EuiFlexItem grow={false} style={{ width: '100%' }}>
{field}
{inputComponent}
</EuiFlexItem>
<EuiFlexItem grow={false}>{cancelButton}</EuiFlexItem>
</EuiFlexGroup>
);
}
return field;
return inputComponent;
}

File diff suppressed because it is too large Load diff

View file

@ -221,13 +221,15 @@ export async function extractAndWriteSecrets(opts: {
return { packagePolicy, secretReferences: [] };
}
const secretsToCreate = secretPaths.filter((secretPath) => !!secretPath.value.value);
const secrets = await createSecrets({
esClient,
values: secretPaths.map((secretPath) => secretPath.value.value),
values: secretsToCreate.map((secretPath) => secretPath.value.value),
});
const policyWithSecretRefs = JSON.parse(JSON.stringify(packagePolicy));
secretPaths.forEach((secretPath, i) => {
secretsToCreate.forEach((secretPath, i) => {
set(policyWithSecretRefs, secretPath.path + '.value', toVarSecretRef(secrets[i].id));
});
@ -278,7 +280,7 @@ export async function extractAndUpdateSecrets(opts: {
// check if the previous secret is actually a secret refrerence
// it may be that secrets were not enabled at the time of creation
// in which case they are just stored as plain text
if (secretPath.value.value.isSecretRef) {
if (secretPath.value.value?.isSecretRef) {
secretsToDelete.push({ id: secretPath.value.value.id });
}
});