mirror of
https://github.com/portainer/portainer.git
synced 2025-07-20 05:49:40 +02:00
feat(secrets): allow creating secrets beyond opaque [EE-2625] (#7709)
This commit is contained in:
parent
3b2f0ff9eb
commit
4e20d70a99
23 changed files with 659 additions and 135 deletions
|
@ -6,11 +6,12 @@ import { Base64 } from 'js-base64';
|
|||
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper';
|
||||
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
|
||||
import { KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
|
||||
import { KubernetesConfigurationKinds, KubernetesSecretTypes } from 'Kubernetes/models/configuration/models';
|
||||
|
||||
class KubernetesConfigurationDataController {
|
||||
/* @ngInject */
|
||||
constructor($async) {
|
||||
this.$async = $async;
|
||||
constructor($async, Notifications) {
|
||||
Object.assign(this, { $async, Notifications });
|
||||
|
||||
this.editorUpdate = this.editorUpdate.bind(this);
|
||||
this.editorUpdateAsync = this.editorUpdateAsync.bind(this);
|
||||
|
@ -18,6 +19,8 @@ class KubernetesConfigurationDataController {
|
|||
this.onFileLoadAsync = this.onFileLoadAsync.bind(this);
|
||||
this.showSimpleMode = this.showSimpleMode.bind(this);
|
||||
this.showAdvancedMode = this.showAdvancedMode.bind(this);
|
||||
this.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
|
||||
this.KubernetesSecretTypes = KubernetesSecretTypes;
|
||||
}
|
||||
|
||||
onChangeKey(entry) {
|
||||
|
@ -25,6 +28,8 @@ class KubernetesConfigurationDataController {
|
|||
return;
|
||||
}
|
||||
|
||||
this.onChangeValidation();
|
||||
|
||||
this.state.duplicateKeys = KubernetesFormValidationHelper.getDuplicates(_.map(this.formValues.Data, (data) => data.Key));
|
||||
this.state.invalidKeys = KubernetesFormValidationHelper.getInvalidKeys(_.map(this.formValues.Data, (data) => data.Key));
|
||||
this.isValid = Object.keys(this.state.duplicateKeys).length === 0 && Object.keys(this.state.invalidKeys).length === 0;
|
||||
|
@ -32,6 +37,85 @@ class KubernetesConfigurationDataController {
|
|||
|
||||
addEntry() {
|
||||
this.formValues.Data.push(new KubernetesConfigurationFormValuesEntry());
|
||||
|
||||
// logic for setting required keys for new entries, based on the secret type
|
||||
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
|
||||
const newDataIndex = this.formValues.Data.length - 1;
|
||||
const typeValue = typeof this.formValues.Type === 'string' ? this.formValues.Type : this.formValues.Type.value;
|
||||
switch (typeValue) {
|
||||
case this.KubernetesSecretTypes.DOCKERCFG.value:
|
||||
this.addMissingKeys(['dockercfg'], newDataIndex);
|
||||
break;
|
||||
case this.KubernetesSecretTypes.DOCKERCONFIGJSON.value:
|
||||
this.addMissingKeys(['.dockerconfigjson'], newDataIndex);
|
||||
break;
|
||||
case this.KubernetesSecretTypes.BASICAUTH.value:
|
||||
// only add a required key if there is no required key out of username and password
|
||||
if (!this.formValues.Data.some((entry) => entry.Key === 'username' || entry.Key === 'password')) {
|
||||
this.addMissingKeys(['username', 'password'], newDataIndex);
|
||||
}
|
||||
break;
|
||||
case this.KubernetesSecretTypes.SSHAUTH.value:
|
||||
this.addMissingKeys(['ssh-privatekey'], newDataIndex);
|
||||
break;
|
||||
case this.KubernetesSecretTypes.TLS.value:
|
||||
this.addMissingKeys(['tls.crt', 'tls.key'], newDataIndex);
|
||||
break;
|
||||
case this.KubernetesSecretTypes.BOOTSTRAPTOKEN.value:
|
||||
this.addMissingKeys(['token-id', 'token-secret'], newDataIndex);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.onChangeValidation();
|
||||
}
|
||||
|
||||
// addMissingKeys adds the keys in the keys array to the entry at the index provided and stops when the first one is added
|
||||
addMissingKeys(keys, newDataIndex) {
|
||||
for (let key of keys) {
|
||||
if (this.formValues.Data.every((entry) => entry.Key !== key)) {
|
||||
this.formValues.Data[newDataIndex].Key = key;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isRequiredKey(key) {
|
||||
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
|
||||
const secretTypeValue = typeof this.formValues.Type === 'string' ? this.formValues.Type : this.formValues.Type.value;
|
||||
switch (secretTypeValue) {
|
||||
case this.KubernetesSecretTypes.DOCKERCONFIGJSON.value:
|
||||
if (key === '.dockerconfigjson') {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case this.KubernetesSecretTypes.DOCKERCFG.value:
|
||||
if (key === '.dockercfg') {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case this.KubernetesSecretTypes.SSHAUTH.value:
|
||||
if (key === 'ssh-privatekey') {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case this.KubernetesSecretTypes.TLS.value:
|
||||
if (key === 'tls.crt' || key === 'tls.key') {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case this.KubernetesSecretTypes.BOOTSTRAPTOKEN.value:
|
||||
if (key === 'token-id' || key === 'token-secret') {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
removeEntry(index, entry) {
|
||||
|
@ -55,24 +139,77 @@ class KubernetesConfigurationDataController {
|
|||
}
|
||||
|
||||
async onFileLoadAsync(event) {
|
||||
const entry = new KubernetesConfigurationFormValuesEntry();
|
||||
const encoding = chardet.detect(Buffer.from(event.target.result));
|
||||
const decoder = new TextDecoder(encoding);
|
||||
|
||||
entry.Key = event.target.fileName;
|
||||
entry.IsBinary = KubernetesConfigurationHelper.isBinary(encoding);
|
||||
|
||||
if (!entry.IsBinary) {
|
||||
entry.Value = decoder.decode(event.target.result);
|
||||
} else {
|
||||
const stringValue = decoder.decode(event.target.result);
|
||||
entry.Value = Base64.encode(stringValue);
|
||||
// exit if the file is too big
|
||||
const maximumFileSizeBytes = 1024 * 1024; // 1MB
|
||||
if (event.target.result.byteLength > maximumFileSizeBytes) {
|
||||
this.Notifications.error('File size is too big', 'File size is too big', 'Select a file that is 1MB or smaller.');
|
||||
return;
|
||||
}
|
||||
|
||||
const entry = new KubernetesConfigurationFormValuesEntry();
|
||||
try {
|
||||
const encoding = chardet.detect(Buffer.from(event.target.result));
|
||||
const decoder = new TextDecoder(encoding);
|
||||
|
||||
entry.IsBinary = KubernetesConfigurationHelper.isBinary(encoding);
|
||||
|
||||
if (!entry.IsBinary) {
|
||||
entry.Value = decoder.decode(event.target.result);
|
||||
} else {
|
||||
const stringValue = decoder.decode(event.target.result);
|
||||
entry.Value = Base64.encode(stringValue);
|
||||
}
|
||||
} catch (error) {
|
||||
this.Notifications.error('Failed to upload file', error, 'Failed to upload file');
|
||||
return;
|
||||
}
|
||||
|
||||
entry.Key = event.target.fileName;
|
||||
|
||||
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
|
||||
if (this.isDockerConfig) {
|
||||
if (this.formValues.Type.name === this.KubernetesSecretTypes.DOCKERCFG.name) {
|
||||
entry.Key = '.dockercfg';
|
||||
} else {
|
||||
entry.Key = '.dockerconfigjson';
|
||||
}
|
||||
}
|
||||
|
||||
if (this.formValues.Type.name === this.KubernetesSecretTypes.TLS.name) {
|
||||
const isCrt = entry.Value.indexOf('BEGIN CERTIFICATE') !== -1;
|
||||
if (isCrt) {
|
||||
entry.Key = 'tls.crt';
|
||||
}
|
||||
const isKey = entry.Value.indexOf('PRIVATE KEY') !== -1;
|
||||
if (isKey) {
|
||||
entry.Key = 'tls.key';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if this.formValues.Data has a key that matches an existing key, then replace it
|
||||
const existingEntryIndex = this.formValues.Data.findIndex((data) => data.Key === entry.Key || (data.Value === '' && data.Key === ''));
|
||||
if (existingEntryIndex !== -1) {
|
||||
this.formValues.Data[existingEntryIndex] = entry;
|
||||
} else {
|
||||
this.formValues.Data.push(entry);
|
||||
}
|
||||
|
||||
this.formValues.Data.push(entry);
|
||||
this.onChangeKey();
|
||||
}
|
||||
|
||||
isEntryRequired() {
|
||||
if (this.formValues.Kind === this.KubernetesConfigurationKinds.SECRET) {
|
||||
const typeValue = typeof this.formValues.Type === 'string' ? this.formValues.Type : this.formValues.Type.value;
|
||||
if (this.formValues.Data.length === 1) {
|
||||
if (typeValue !== this.KubernetesSecretTypes.SERVICEACCOUNTTOKEN.value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
onFileLoad(event) {
|
||||
return this.$async(this.onFileLoadAsync, event);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue