mirror of https://github.com/rancher/dashboard.git
PSA minor changes (#8022)
* Add missing and correct i18n PSA labels * Correct description size by removing helper * Add title to Namespace list tooltip if any PSA * Allow to disable use of checkbox for PSA form * Change timeout for growl to 5s on Pod warning due PSA * Add type label for PSA * Move PSA menu under Advanced * Replace toggling system label with extending the value within the same * Emit initial PSA form values con creation if no checkboxes due lack of interactions
This commit is contained in:
parent
33e428b945
commit
0768d82f16
|
|
@ -3942,10 +3942,20 @@ plugins:
|
|||
title: Remove the Rancher Extensions Custom Resource Definition
|
||||
prompt: There are one or more extensions installed - removing the CRD will require you to manually reinstall these extensions if you subsequently re-enable extensions support.
|
||||
podSecurityAdmission:
|
||||
label: Pod Security Admission
|
||||
name: Pod Security Admission
|
||||
description: Define the admission control mode you want to use for the pod security
|
||||
banner:
|
||||
modifications: 'Note: Modifying a Pod Security Admission Configuration Template will not affect any downstream cluster that references it until there is a cluster update'
|
||||
labels:
|
||||
enforce: Enforce
|
||||
audit: Audit
|
||||
warn: Warn
|
||||
usernames: Usernames
|
||||
runtimeClasses: RuntimeClasses
|
||||
namespaces: Namespaces
|
||||
privileged: privileged
|
||||
baseline: baseline
|
||||
restricted: restricted
|
||||
version:
|
||||
placeholder: 'Version (default: latest)'
|
||||
exemptions:
|
||||
|
|
@ -6213,6 +6223,11 @@ typeLabel:
|
|||
one { Workspace }
|
||||
other { Workspaces }
|
||||
}
|
||||
management.cattle.io.podsecurityadmissionconfigurationtemplate: |-
|
||||
{count, plural,
|
||||
one { Pod Security Admission }
|
||||
other { Pod Security Admissions }
|
||||
}
|
||||
policy.poddisruptionbudget: |-
|
||||
{count, plural,
|
||||
one { Pod Disruption Budget }
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ export default {
|
|||
},
|
||||
|
||||
labels() {
|
||||
if (this.showAllLabels || !this.showFilteredSystemLabels) {
|
||||
if (!this.showFilteredSystemLabels) {
|
||||
return this.value?.labels || {};
|
||||
}
|
||||
|
||||
|
|
@ -249,6 +249,7 @@ export default {
|
|||
v-tooltip="prop ? `${key} : ${prop}` : key"
|
||||
>
|
||||
<span>{{ internalTooltips[key] ? internalTooltips[key] : key }}</span>
|
||||
<span v-if="showAllLabels">: {{ key }}</span>
|
||||
</span>
|
||||
<span v-else>{{ prop ? `${key} : ${prop}` : key }}</span>
|
||||
</Tag>
|
||||
|
|
|
|||
|
|
@ -235,13 +235,17 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
getPSA(row) {
|
||||
/**
|
||||
* Get PSA HTML to be displayed in the tooltips
|
||||
*/
|
||||
getPsaTooltip(row) {
|
||||
const dictionary = row.psaTooltipsDescription;
|
||||
const list = Object.values(dictionary)
|
||||
.sort()
|
||||
.map(text => `<li>${ text }</li>`).join('');
|
||||
const title = `<p>${ this.t('podSecurityAdmission.name') }: </p>`;
|
||||
|
||||
return `<ul class="psa-tooltip">${ list }</ul>`;
|
||||
return `${ title }<ul class="psa-tooltip">${ list }</ul>`;
|
||||
},
|
||||
|
||||
userIsFilteringForSpecificNamespaceOrProject() {
|
||||
|
|
@ -409,7 +413,7 @@ export default {
|
|||
</span>
|
||||
<i
|
||||
v-if="row.hasSystemLabels"
|
||||
v-tooltip="getPSA(row)"
|
||||
v-tooltip="getPsaTooltip(row)"
|
||||
class="icon icon-lock ml-5"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { _VIEW } from '@shell/config/query-params';
|
||||
import { _VIEW, _CREATE } from '@shell/config/query-params';
|
||||
import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
|
||||
import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
|
||||
import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
|
||||
|
|
@ -39,6 +39,11 @@ export default Vue.extend({
|
|||
default: () => ({})
|
||||
},
|
||||
|
||||
labelsAlwaysActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
/**
|
||||
* Map editing capabilities to the component
|
||||
*/
|
||||
|
|
@ -78,7 +83,10 @@ export default Vue.extend({
|
|||
// Generate PSA form controls
|
||||
psaControls: toDictionary(PSAModes, getPsaControl) as Record<PSAMode, PSAControl>,
|
||||
psaExemptionsControls: toDictionary(PSADimensions, getExemptionControl) as Record<PSADimension, PSAExemptionControl>,
|
||||
options: PSALevels,
|
||||
options: PSALevels.map(level => ({
|
||||
value: level,
|
||||
label: this.t(`podSecurityAdmission.labels.${ level }`)
|
||||
})),
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -105,6 +113,12 @@ export default Vue.extend({
|
|||
};
|
||||
|
||||
this.psaExemptionsControls = this.getPsaExemptions();
|
||||
|
||||
// Emit initial value on creation if labels always active, as default predefined values are required
|
||||
if (this.mode === _CREATE && this.labelsAlwaysActive) {
|
||||
this.updateLabels();
|
||||
this.updateExemptions();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
|
@ -114,7 +128,7 @@ export default Vue.extend({
|
|||
updateLabels(): void {
|
||||
const nonPSALabels = pickBy(this.labels, (_, key) => !key.includes(this.labelsPrefix));
|
||||
const labels = PSAModes.reduce((acc, mode) => {
|
||||
return this.psaControls[mode].active ? {
|
||||
return this.psaControls[mode].active || this.labelsAlwaysActive ? {
|
||||
...acc,
|
||||
// Set default level if none
|
||||
[`${ this.labelsPrefix }${ mode }`]: this.psaControls[mode].level || PSADefaultLevel,
|
||||
|
|
@ -126,6 +140,9 @@ export default Vue.extend({
|
|||
this.$emit('updateLabels', labels);
|
||||
},
|
||||
|
||||
/**
|
||||
* Emit active exemptions in required format
|
||||
*/
|
||||
updateExemptions(): void {
|
||||
const exemptions = PSADimensions.reduce((acc, dimension) => {
|
||||
const value = this.psaExemptionsControls[dimension].value.split(',').map(value => value.trim());
|
||||
|
|
@ -175,6 +192,13 @@ export default Vue.extend({
|
|||
}
|
||||
};
|
||||
}, {}) as Record<PSADimension, PSAExemptionControl>;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add checks on input for PSA controls to be active or not, allowing white cases
|
||||
*/
|
||||
isPsaControlDisabled(active: boolean): boolean {
|
||||
return !this.labelsAlwaysActive && (!active || this.isView);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -183,7 +207,7 @@ export default Vue.extend({
|
|||
<template>
|
||||
<div class="psa">
|
||||
<!-- PSA -->
|
||||
<p class="helper-text mb-30">
|
||||
<p class="mb-30">
|
||||
<t k="podSecurityAdmission.description" />
|
||||
</p>
|
||||
|
||||
|
|
@ -194,19 +218,28 @@ export default Vue.extend({
|
|||
>
|
||||
<span class="col span-2">
|
||||
<Checkbox
|
||||
v-if="!labelsAlwaysActive"
|
||||
v-model="psaControl.active"
|
||||
:data-testid="componentTestid + '--psaControl-' + i + '-active'"
|
||||
:label="level"
|
||||
:label-key="`podSecurityAdmission.labels.${ level }`"
|
||||
:disabled="isView"
|
||||
@input="updateLabels()"
|
||||
/>
|
||||
<p v-else>
|
||||
<t :k="`podSecurityAdmission.labels.${level}`" />
|
||||
</p>
|
||||
</span>
|
||||
|
||||
<span class="col span-4">
|
||||
<span
|
||||
class="
|
||||
col
|
||||
span-4"
|
||||
>
|
||||
<LabeledSelect
|
||||
v-model="psaControl.level"
|
||||
:data-testid="componentTestid + '--psaControl-' + i + '-level'"
|
||||
:disabled="(isView || !psaControl.active)"
|
||||
:disabled="isPsaControlDisabled(psaControl.active)"
|
||||
:options="options"
|
||||
:mode="mode"
|
||||
@input="updateLabels()"
|
||||
|
|
@ -217,7 +250,7 @@ export default Vue.extend({
|
|||
<LabeledInput
|
||||
v-model="psaControl.version"
|
||||
:data-testid="componentTestid + '--psaControl-' + i + '-version'"
|
||||
:disabled="(isView || !psaControl.active)"
|
||||
:disabled="isPsaControlDisabled(psaControl.active)"
|
||||
:options="options"
|
||||
:placeholder="t('podSecurityAdmission.version.placeholder', { psaControl: mode })"
|
||||
:mode="mode"
|
||||
|
|
@ -233,7 +266,7 @@ export default Vue.extend({
|
|||
<t k="podSecurityAdmission.exemptions.title" />
|
||||
</h3>
|
||||
</slot>
|
||||
<p class="helper-text mb-30">
|
||||
<p class="mb-30">
|
||||
<t k="podSecurityAdmission.exemptions.description" />
|
||||
</p>
|
||||
|
||||
|
|
@ -247,6 +280,7 @@ export default Vue.extend({
|
|||
v-model="psaExemptionsControl.active"
|
||||
:data-testid="componentTestid + '--psaExemptionsControl-' + i + '-active'"
|
||||
:label="dimension"
|
||||
:label-key="`podSecurityAdmission.labels.${ dimension }`"
|
||||
:disabled="isView"
|
||||
@input="updateExemptions()"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -2,13 +2,31 @@ import { mount } from '@vue/test-utils';
|
|||
import PodSecurityAdmission from '@shell/components/PodSecurityAdmission.vue';
|
||||
|
||||
describe('component: PodSecurityAdmission', () => {
|
||||
it.each([
|
||||
['updateLabels', {
|
||||
audit: 'privileged',
|
||||
'audit-version': 'latest',
|
||||
enforce: 'privileged',
|
||||
'enforce-version': 'latest',
|
||||
warn: 'privileged',
|
||||
'warn-version': 'latest',
|
||||
}],
|
||||
['updateExemptions', {
|
||||
namespaces: [], runtimeClasses: [], usernames: []
|
||||
}]
|
||||
])('should emit %p and exemptions on creation if labels always active', (emission, value) => {
|
||||
const wrapper = mount(PodSecurityAdmission, { propsData: { mode: 'create', labelsAlwaysActive: true } });
|
||||
|
||||
expect(wrapper.emitted(emission)![0][0]).toStrictEqual(value);
|
||||
});
|
||||
|
||||
describe('handling labels', () => {
|
||||
it.each([
|
||||
['true', 'active'],
|
||||
['', 'level'],
|
||||
['', 'version'],
|
||||
])('should display default value %p for input %p', (value, inputId) => {
|
||||
const wrapper = mount(PodSecurityAdmission, { propsData: { mode: 'create' } });
|
||||
const wrapper = mount(PodSecurityAdmission, { propsData: { mode: 'edit' } });
|
||||
|
||||
const input = wrapper.find(`[data-testid="pod-security-admission--psaControl-0-${ inputId }"]`).find('input').element as HTMLInputElement;
|
||||
|
||||
|
|
@ -44,7 +62,7 @@ describe('component: PodSecurityAdmission', () => {
|
|||
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'create',
|
||||
mode: 'edit',
|
||||
labels,
|
||||
labelsPrefix: prefix
|
||||
}
|
||||
|
|
@ -65,7 +83,7 @@ describe('component: PodSecurityAdmission', () => {
|
|||
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'create',
|
||||
mode: 'edit',
|
||||
labels,
|
||||
labelsPrefix: prefix
|
||||
}
|
||||
|
|
@ -88,7 +106,7 @@ describe('component: PodSecurityAdmission', () => {
|
|||
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'create',
|
||||
mode: 'edit',
|
||||
labels,
|
||||
labelsPrefix: prefix
|
||||
}
|
||||
|
|
@ -108,7 +126,7 @@ describe('component: PodSecurityAdmission', () => {
|
|||
};
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'create',
|
||||
mode: 'edit',
|
||||
labels: {},
|
||||
labelsPrefix: prefix
|
||||
},
|
||||
|
|
@ -141,7 +159,7 @@ describe('component: PodSecurityAdmission', () => {
|
|||
};
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'create',
|
||||
mode: 'edit',
|
||||
labels,
|
||||
labelsPrefix: prefix
|
||||
},
|
||||
|
|
@ -177,7 +195,7 @@ describe('component: PodSecurityAdmission', () => {
|
|||
it('should assign default version and level if missing', () => {
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'create',
|
||||
mode: 'edit',
|
||||
labels: {},
|
||||
labelsPrefix: prefix
|
||||
},
|
||||
|
|
@ -204,6 +222,104 @@ describe('component: PodSecurityAdmission', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.each(['level', 'version'])('should keep always %p enabled', (inputId) => {
|
||||
it('given labelsAlwaysActive true and no labels', () => {
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'edit',
|
||||
labelsAlwaysActive: true,
|
||||
labels: {}
|
||||
},
|
||||
});
|
||||
|
||||
const input = wrapper.find(`[data-testid="pod-security-admission--psaControl-0-${ inputId }"]`).find('input').element as HTMLInputElement;
|
||||
|
||||
expect(input.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('given existing values', () => {
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'edit',
|
||||
labels: {
|
||||
[`enforce`]: 'baseline',
|
||||
[`enforce-version`]: '123'
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const input = wrapper.find(`[data-testid="pod-security-admission--psaControl-0-${ inputId }"]`).find('input').element as HTMLInputElement;
|
||||
|
||||
expect(input.disabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe.each(['level', 'version'])('should keep always %p disabled', (inputId) => {
|
||||
it('given labelsAlwaysActive false and no labels', () => {
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'edit',
|
||||
labelsAlwaysActive: false,
|
||||
labels: {}
|
||||
},
|
||||
});
|
||||
|
||||
const input = wrapper.find(`[data-testid="pod-security-admission--psaControl-0-${ inputId }"]`).find('input').element as HTMLInputElement;
|
||||
|
||||
expect(input.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('given disabled active status', () => {
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: { mode: 'edit' },
|
||||
data: () => ({
|
||||
psaControls: {
|
||||
enforce: {
|
||||
active: false,
|
||||
level: '',
|
||||
version: ''
|
||||
}
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
const input = wrapper.find(`[data-testid="pod-security-admission--psaControl-0-${ inputId }"]`).find('input').element as HTMLInputElement;
|
||||
|
||||
expect(input.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('given view mode and provided labels', () => {
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'view',
|
||||
labels: {
|
||||
[`enforce`]: 'baseline',
|
||||
[`enforce-version`]: '123'
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const input = wrapper.find(`[data-testid="pod-security-admission--psaControl-0-${ inputId }"]`).find('input').element as HTMLInputElement;
|
||||
|
||||
expect(input.disabled).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
[true, false],
|
||||
])('should display the checkbox %p', (value) => {
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'edit',
|
||||
labelsAlwaysActive: value
|
||||
}
|
||||
});
|
||||
|
||||
const input = wrapper.find(`[data-testid="pod-security-admission--psaControl-0-active"]`).element as HTMLInputElement;
|
||||
|
||||
expect(!input).toBe(value);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handling exemptions', () => {
|
||||
|
|
@ -228,7 +344,7 @@ describe('component: PodSecurityAdmission', () => {
|
|||
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'create',
|
||||
mode: 'edit',
|
||||
exemptions,
|
||||
}
|
||||
});
|
||||
|
|
@ -243,7 +359,7 @@ describe('component: PodSecurityAdmission', () => {
|
|||
const exemptions = { usernames: [value] };
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'create',
|
||||
mode: 'edit',
|
||||
exemptions
|
||||
}
|
||||
});
|
||||
|
|
@ -266,7 +382,7 @@ describe('component: PodSecurityAdmission', () => {
|
|||
};
|
||||
const wrapper = mount(PodSecurityAdmission, {
|
||||
propsData: {
|
||||
mode: 'create',
|
||||
mode: 'edit',
|
||||
exemptions
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ export default {
|
|||
:on-label="t('labels.labels.show')"
|
||||
/>
|
||||
</div>
|
||||
<p class="helper-text mt-10 mb-10">
|
||||
<p class="mt-10 mb-10">
|
||||
<t k="labels.labels.description" />
|
||||
</p>
|
||||
<div :class="sectionClass">
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import {
|
|||
CAPI,
|
||||
CATALOG,
|
||||
NORMAN,
|
||||
HCI
|
||||
HCI,
|
||||
MANAGEMENT
|
||||
} from '@shell/config/types';
|
||||
import { MULTI_CLUSTER } from '@shell/store/features';
|
||||
import { DSL } from '@shell/store/type-map';
|
||||
|
|
@ -65,7 +66,6 @@ export function init(store) {
|
|||
'cloud-credentials',
|
||||
'drivers',
|
||||
'pod-security-policies',
|
||||
'management.cattle.io.podsecurityadmissionconfigurationtemplate'
|
||||
]);
|
||||
|
||||
configureType(CAPI.RANCHER_CLUSTER, {
|
||||
|
|
@ -120,6 +120,7 @@ export function init(store) {
|
|||
weightType(CAPI.MACHINE_SET, 2, true);
|
||||
weightType(CAPI.MACHINE, 1, true);
|
||||
weightType(CATALOG.CLUSTER_REPO, 0, true);
|
||||
weightType(MANAGEMENT.PSA, 5, true);
|
||||
|
||||
basicType([
|
||||
CAPI.MACHINE_DEPLOYMENT,
|
||||
|
|
@ -127,6 +128,7 @@ export function init(store) {
|
|||
CAPI.MACHINE,
|
||||
CATALOG.CLUSTER_REPO,
|
||||
'pod-security-policies',
|
||||
MANAGEMENT.PSA
|
||||
], 'advanced');
|
||||
|
||||
weightGroup('advanced', -1, true);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ export default (Vue as VueConstructor<Vue & InstanceType<typeof CreateEditView>>
|
|||
/>
|
||||
<PodSecurityAdmission
|
||||
:labels="defaults"
|
||||
:labels-always-active="true"
|
||||
:exemptions="exemptions"
|
||||
:mode="mode"
|
||||
@updateLabels="setDefaults($event)"
|
||||
|
|
|
|||
|
|
@ -238,8 +238,8 @@ export default {
|
|||
</Tab>
|
||||
<Tab
|
||||
name="pod-security-admission"
|
||||
label-key="podSecurityAdmission.label"
|
||||
:label="t('podSecurityAdmission.label')"
|
||||
label-key="podSecurityAdmission.name"
|
||||
:label="t('podSecurityAdmission.name')"
|
||||
>
|
||||
<PodSecurityAdmission
|
||||
:labels="value.labels"
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ export default class Pod extends WorkloadService {
|
|||
this.$dispatch('growl/warning', {
|
||||
title: this.$rootGetters['i18n/t']('growl.podSecurity.title'),
|
||||
message: this.$rootGetters['i18n/t']('growl.podSecurity.message'),
|
||||
timeout: 5000,
|
||||
}, { root: true });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue