diff --git a/portal-ui/src/screens/Console/Common/GenericWizard/WizardPage.tsx b/portal-ui/src/screens/Console/Common/GenericWizard/WizardPage.tsx index 955be2e3c..38c80f6b1 100644 --- a/portal-ui/src/screens/Console/Common/GenericWizard/WizardPage.tsx +++ b/portal-ui/src/screens/Console/Common/GenericWizard/WizardPage.tsx @@ -103,6 +103,9 @@ const WizardPage = ({ >
{page.buttons.map((btn) => { + if (btn.componentRender) { + return btn.componentRender; + } return ( + ); +}; + +export default CreateTenantButton; diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/AWSKMSAdd.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/AWSKMSAdd.tsx index 2e4c09fb4..3e5313447 100644 --- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/AWSKMSAdd.tsx +++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/AWSKMSAdd.tsx @@ -48,12 +48,6 @@ const AWSKMSAdd = () => { const dispatch = useDispatch(); const classes = useStyles(); - const enableEncryption = useSelector( - (state: AppState) => state.createTenant.fields.encryption.enableEncryption - ); - const encryptionType = useSelector( - (state: AppState) => state.createTenant.fields.encryption.encryptionType - ); const awsEndpoint = useSelector( (state: AppState) => state.createTenant.fields.encryption.awsEndpoint ); @@ -78,33 +72,29 @@ const AWSKMSAdd = () => { useEffect(() => { let encryptionValidation: IValidation[] = []; - if (enableEncryption) { - if (encryptionType === "aws") { - encryptionValidation = [ - ...encryptionValidation, - { - fieldKey: "aws_endpoint", - required: true, - value: awsEndpoint, - }, - { - fieldKey: "aws_region", - required: true, - value: awsRegion, - }, - { - fieldKey: "aws_accessKey", - required: true, - value: awsAccessKey, - }, - { - fieldKey: "aws_secretKey", - required: true, - value: awsSecretKey, - }, - ]; - } - } + encryptionValidation = [ + ...encryptionValidation, + { + fieldKey: "aws_endpoint", + required: true, + value: awsEndpoint, + }, + { + fieldKey: "aws_region", + required: true, + value: awsRegion, + }, + { + fieldKey: "aws_accessKey", + required: true, + value: awsAccessKey, + }, + { + fieldKey: "aws_secretKey", + required: true, + value: awsSecretKey, + }, + ]; const commonVal = commonFormValidation(encryptionValidation); @@ -116,15 +106,7 @@ const AWSKMSAdd = () => { ); setValidationErrors(commonVal); - }, [ - enableEncryption, - encryptionType, - awsEndpoint, - awsRegion, - awsSecretKey, - awsAccessKey, - dispatch, - ]); + }, [awsEndpoint, awsRegion, awsSecretKey, awsAccessKey, dispatch]); // Common const updateField = useCallback( diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/AzureKMSAdd.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/AzureKMSAdd.tsx index 49f2a0487..06105bbfd 100644 --- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/AzureKMSAdd.tsx +++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/AzureKMSAdd.tsx @@ -48,13 +48,6 @@ const AzureKMSAdd = () => { const dispatch = useDispatch(); const classes = useStyles(); - const enableEncryption = useSelector( - (state: AppState) => state.createTenant.fields.encryption.enableEncryption - ); - const encryptionType = useSelector( - (state: AppState) => state.createTenant.fields.encryption.encryptionType - ); - const azureEndpoint = useSelector( (state: AppState) => state.createTenant.fields.encryption.azureEndpoint ); @@ -74,33 +67,29 @@ const AzureKMSAdd = () => { useEffect(() => { let encryptionValidation: IValidation[] = []; - if (enableEncryption) { - if (encryptionType === "azure") { - encryptionValidation = [ - ...encryptionValidation, - { - fieldKey: "azure_endpoint", - required: true, - value: azureEndpoint, - }, - { - fieldKey: "azure_tenant_id", - required: true, - value: azureTenantID, - }, - { - fieldKey: "azure_client_id", - required: true, - value: azureClientID, - }, - { - fieldKey: "azure_client_secret", - required: true, - value: azureClientSecret, - }, - ]; - } - } + encryptionValidation = [ + ...encryptionValidation, + { + fieldKey: "azure_endpoint", + required: true, + value: azureEndpoint, + }, + { + fieldKey: "azure_tenant_id", + required: true, + value: azureTenantID, + }, + { + fieldKey: "azure_client_id", + required: true, + value: azureClientID, + }, + { + fieldKey: "azure_client_secret", + required: true, + value: azureClientSecret, + }, + ]; const commonVal = commonFormValidation(encryptionValidation); @@ -113,8 +102,6 @@ const AzureKMSAdd = () => { setValidationErrors(commonVal); }, [ - enableEncryption, - encryptionType, azureEndpoint, azureTenantID, azureClientID, diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/GemaltoKMSAdd.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/GemaltoKMSAdd.tsx index ac55192ef..b262b42be 100644 --- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/GemaltoKMSAdd.tsx +++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/GemaltoKMSAdd.tsx @@ -53,12 +53,6 @@ const GemaltoKMSAdd = () => { const dispatch = useDispatch(); const classes = useStyles(); - const enableEncryption = useSelector( - (state: AppState) => state.createTenant.fields.encryption.enableEncryption - ); - const encryptionType = useSelector( - (state: AppState) => state.createTenant.fields.encryption.encryptionType - ); const gemaltoCA = useSelector( (state: AppState) => state.createTenant.certificates.gemaltoCA ); @@ -81,35 +75,31 @@ const GemaltoKMSAdd = () => { useEffect(() => { let encryptionValidation: IValidation[] = []; - if (enableEncryption) { - if (encryptionType === "gemalto") { - encryptionValidation = [ - ...encryptionValidation, - { - fieldKey: "gemalto_endpoint", - required: true, - value: gemaltoEndpoint, - }, - { - fieldKey: "gemalto_token", - required: true, - value: gemaltoToken, - }, - { - fieldKey: "gemalto_domain", - required: true, - value: gemaltoDomain, - }, - { - fieldKey: "gemalto_retry", - required: false, - value: gemaltoRetry, - customValidation: parseInt(gemaltoRetry) < 0, - customValidationMessage: "Value needs to be 0 or greater", - }, - ]; - } - } + encryptionValidation = [ + ...encryptionValidation, + { + fieldKey: "gemalto_endpoint", + required: true, + value: gemaltoEndpoint, + }, + { + fieldKey: "gemalto_token", + required: true, + value: gemaltoToken, + }, + { + fieldKey: "gemalto_domain", + required: true, + value: gemaltoDomain, + }, + { + fieldKey: "gemalto_retry", + required: false, + value: gemaltoRetry, + customValidation: parseInt(gemaltoRetry) < 0, + customValidationMessage: "Value needs to be 0 or greater", + }, + ]; const commonVal = commonFormValidation(encryptionValidation); @@ -121,15 +111,7 @@ const GemaltoKMSAdd = () => { ); setValidationErrors(commonVal); - }, [ - enableEncryption, - encryptionType, - gemaltoEndpoint, - gemaltoToken, - gemaltoDomain, - gemaltoRetry, - dispatch, - ]); + }, [gemaltoEndpoint, gemaltoToken, gemaltoDomain, gemaltoRetry, dispatch]); // Common const updateField = useCallback( diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/VaultKMSAdd.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/VaultKMSAdd.tsx index ee4183218..7b622f545 100644 --- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/VaultKMSAdd.tsx +++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption/VaultKMSAdd.tsx @@ -55,13 +55,6 @@ const VaultKMSAdd = () => { const dispatch = useDispatch(); const classes = useStyles(); - const enableEncryption = useSelector( - (state: AppState) => state.createTenant.fields.encryption.enableEncryption - ); - const encryptionType = useSelector( - (state: AppState) => state.createTenant.fields.encryption.encryptionType - ); - const vaultEndpoint = useSelector( (state: AppState) => state.createTenant.fields.encryption.vaultEndpoint ); @@ -102,42 +95,38 @@ const VaultKMSAdd = () => { useEffect(() => { let encryptionValidation: IValidation[] = []; - if (enableEncryption) { - if (encryptionType === "vault") { - encryptionValidation = [ - ...encryptionValidation, - { - fieldKey: "vault_endpoint", - required: true, - value: vaultEndpoint, - }, - { - fieldKey: "vault_id", - required: true, - value: vaultId, - }, - { - fieldKey: "vault_secret", - required: true, - value: vaultSecret, - }, - { - fieldKey: "vault_ping", - required: false, - value: vaultPing, - customValidation: parseInt(vaultPing) < 0, - customValidationMessage: "Value needs to be 0 or greater", - }, - { - fieldKey: "vault_retry", - required: false, - value: vaultRetry, - customValidation: parseInt(vaultRetry) < 0, - customValidationMessage: "Value needs to be 0 or greater", - }, - ]; - } - } + encryptionValidation = [ + ...encryptionValidation, + { + fieldKey: "vault_endpoint", + required: true, + value: vaultEndpoint, + }, + { + fieldKey: "vault_id", + required: true, + value: vaultId, + }, + { + fieldKey: "vault_secret", + required: true, + value: vaultSecret, + }, + { + fieldKey: "vault_ping", + required: false, + value: vaultPing, + customValidation: parseInt(vaultPing) < 0, + customValidationMessage: "Value needs to be 0 or greater", + }, + { + fieldKey: "vault_retry", + required: false, + value: vaultRetry, + customValidation: parseInt(vaultRetry) < 0, + customValidationMessage: "Value needs to be 0 or greater", + }, + ]; const commonVal = commonFormValidation(encryptionValidation); @@ -150,8 +139,6 @@ const VaultKMSAdd = () => { setValidationErrors(commonVal); }, [ - enableEncryption, - encryptionType, vaultEndpoint, vaultEngine, vaultId, diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/NameTenantMain.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/NameTenantMain.tsx index a03395be3..859b9653c 100644 --- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/NameTenantMain.tsx +++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/NameTenantMain.tsx @@ -58,6 +58,7 @@ import { setLimitSize, setStorageClassesList, setStorageType, + setTenantName, updateAddField, } from "../../createTenantSlice"; import { selFeatures } from "../../../../consoleSlice"; @@ -76,6 +77,31 @@ const styles = (theme: Theme) => ...wizardCommon, }); +const NameTenantField = () => { + const dispatch = useDispatch(); + const tenantName = useSelector( + (state: AppState) => state.createTenant.fields.nameTenant.tenantName + ); + + const tenantNameError = useSelector( + (state: AppState) => state.createTenant.validationErrors["tenant-name"] + ); + + return ( + ) => { + dispatch(setTenantName(e.target.value)); + }} + label="Name" + value={tenantName} + required + error={tenantNameError || ""} + /> + ); +}; + interface INameTenantMainScreen { classes: any; formToRender?: IMkEnvs; @@ -84,9 +110,6 @@ interface INameTenantMainScreen { const NameTenantMain = ({ classes, formToRender }: INameTenantMainScreen) => { const dispatch = useDispatch(); - const tenantName = useSelector( - (state: AppState) => state.createTenant.fields.nameTenant.tenantName - ); const namespace = useSelector( (state: AppState) => state.createTenant.fields.nameTenant.namespace ); @@ -219,14 +242,6 @@ const NameTenantMain = ({ classes, formToRender }: INameTenantMainScreen) => { } const commonValidation = commonFormValidation([ - { - fieldKey: "tenant-name", - required: true, - pattern: /^[a-z0-9-]{3,63}$/, - customPatternMessage: - "Name only can contain lowercase letters, numbers and '-'. Min. Length: 3", - value: tenantName, - }, { fieldKey: "namespace", required: true, @@ -237,7 +252,6 @@ const NameTenantMain = ({ classes, formToRender }: INameTenantMainScreen) => { ]); const isValid = - !("tenant-name" in commonValidation) && !("namespace" in commonValidation) && ((formToRender === IMkEnvs.default && storageClasses.length > 0) || (formToRender !== IMkEnvs.default && selectedStorageType !== "")); @@ -248,7 +262,6 @@ const NameTenantMain = ({ classes, formToRender }: INameTenantMainScreen) => { }, [ storageClasses, namespace, - tenantName, dispatch, emptyNamespace, loadingNamespaceInfo, @@ -293,18 +306,7 @@ const NameTenantMain = ({ classes, formToRender }: INameTenantMainScreen) => {
- ) => { - updateField("tenantName", e.target.value); - frmValidationCleanup("tenant-name"); - }} - label="Name" - value={tenantName} - required - error={validationErrors["tenant-name"] || ""} - /> +
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/common.ts b/portal-ui/src/screens/Console/Tenants/AddTenant/common.ts new file mode 100644 index 000000000..554c0647b --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/AddTenant/common.ts @@ -0,0 +1,24 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +export const requiredPages = [ + "nameTenant", + "tenantSize", + "configure", + "affinity", + "identityProvider", + "security", + "encryption", +]; diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/createTenantAPI.ts b/portal-ui/src/screens/Console/Tenants/AddTenant/createTenantAPI.ts new file mode 100644 index 000000000..b2a341b8f --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/AddTenant/createTenantAPI.ts @@ -0,0 +1,61 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import api from "../../../../common/api"; +import get from "lodash/get"; +import { NewServiceAccount } from "../../Common/CredentialsPrompt/types"; +import { ErrorResponseHandler, ITenantCreator } from "../../../../common/types"; + +export const createTenantCall = (dataSend: ITenantCreator) => { + return new Promise((resolve, reject) => { + api + .invoke("POST", `/api/v1/tenants`, dataSend) + .then((res) => { + const consoleSAList = get(res, "console", []); + + let newSrvAcc: NewServiceAccount = { + idp: get(res, "externalIDP", false), + console: [], + }; + + if (consoleSAList) { + if (Array.isArray(consoleSAList)) { + newSrvAcc.console = consoleSAList.map((consoleKey) => { + return { + accessKey: consoleKey.access_key, + secretKey: consoleKey.secret_key, + api: "s3v4", + path: "auto", + url: consoleKey.url, + }; + }); + } else { + newSrvAcc = { + console: { + accessKey: res.console.access_key, + secretKey: res.console.secret_key, + url: res.console.url, + }, + }; + } + } + resolve(newSrvAcc); + }) + .catch((err: ErrorResponseHandler) => { + reject(err); + }); + }); +}; diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/createTenantSlice.ts b/portal-ui/src/screens/Console/Tenants/AddTenant/createTenantSlice.ts index fedfb8600..db83c0554 100644 --- a/portal-ui/src/screens/Console/Tenants/AddTenant/createTenantSlice.ts +++ b/portal-ui/src/screens/Console/Tenants/AddTenant/createTenantSlice.ts @@ -33,19 +33,29 @@ import { } from "./Steps/TenantResources/utils"; import { getBytesNumber } from "../../../../common/utils"; import { CertificateFile, FileValue, KeyFileValue } from "../tenantsSlice"; +import { NewServiceAccount } from "../../Common/CredentialsPrompt/types"; +import { createTenantAsync } from "./thunks/createTenantThunk"; +import { commonFormValidation } from "../../../../utils/validationFunctions"; +import { flipValidPageInState } from "./sliceUtils"; export interface ICreateTenant { + addingTenant: boolean; page: number; validPages: string[]; + validationErrors: { [key: string]: string }; storageClasses: Opts[]; limitSize: any; fields: IFieldStore; certificates: ICertificatesItems; nodeSelectorPairs: LabelKeyPair[]; tolerations: ITolerationModel[]; + // after creation states + createdAccount: NewServiceAccount | null; + showNewCredentials: boolean; } const initialState: ICreateTenant = { + addingTenant: false, page: 0, // We can assume all the other pages are valid with default configuration except for 'nameTenant' // because the user still have to choose a namespace and a name for the tenant @@ -57,6 +67,7 @@ const initialState: ICreateTenant = { "security", "encryption", ], + validationErrors: {}, storageClasses: [], limitSize: {}, fields: { @@ -340,6 +351,8 @@ const initialState: ICreateTenant = { operator: ITolerationOperator.Equal, }, ], + createdAccount: null, + showNewCredentials: false, }; export const createTenantSlice = createSlice({ @@ -382,7 +395,6 @@ export const createTenantSlice = createSlice({ }> ) => { let originValidPages = state.validPages; - if (action.payload.valid) { if (!originValidPages.includes(action.payload.pageName)) { originValidPages.push(action.payload.pageName); @@ -393,7 +405,6 @@ export const createTenantSlice = createSlice({ const newSetOfPages = originValidPages.filter( (elm) => elm !== action.payload.pageName ); - state.validPages = [...newSetOfPages]; } }, @@ -658,302 +669,304 @@ export const createTenantSlice = createSlice({ }; }, resetAddTenantForm: (state) => { - state = { - page: 0, - // We can assume all the other pages are valid with default configuration except for 'nameTenant' - // because the user still have to choose a namespace and a name for the tenant - validPages: [ - "tenantSize", - "configure", - "affinity", - "identityProvider", - "security", - "encryption", - ], - storageClasses: [], - limitSize: {}, - fields: { - nameTenant: { - tenantName: "", - namespace: "", - selectedStorageClass: "", - selectedStorageType: "", + state.addingTenant = false; + state.page = 0; + // We can assume all the other pages are valid with default configuration except for 'nameTenant' + // because the user still have to choose a namespace and a name for the tenant + state.validPages = [ + "tenantSize", + "configure", + "affinity", + "identityProvider", + "security", + "encryption", + ]; + state.validationErrors = {}; + state.storageClasses = []; + state.limitSize = {}; + state.fields = { + nameTenant: { + tenantName: "", + namespace: "", + selectedStorageClass: "", + selectedStorageType: "", + }, + configure: { + customImage: false, + imageName: "", + customDockerhub: false, + imageRegistry: "", + imageRegistryUsername: "", + imageRegistryPassword: "", + exposeMinIO: true, + exposeConsole: true, + tenantCustom: false, + logSearchEnabled: true, + prometheusEnabled: true, + logSearchVolumeSize: "5", + logSearchSizeFactor: "Gi", + logSearchSelectedStorageClass: "default", + logSearchImage: "", + kesImage: "", + logSearchPostgresImage: "", + logSearchPostgresInitImage: "", + prometheusVolumeSize: "5", + prometheusSizeFactor: "Gi", + prometheusSelectedStorageClass: "default", + prometheusImage: "", + prometheusSidecarImage: "", + prometheusInitImage: "", + setDomains: false, + consoleDomain: "", + minioDomains: [""], + tenantSecurityContext: { + runAsUser: "1000", + runAsGroup: "1000", + fsGroup: "1000", + runAsNonRoot: true, }, - configure: { - customImage: false, - imageName: "", - customDockerhub: false, - imageRegistry: "", - imageRegistryUsername: "", - imageRegistryPassword: "", - exposeMinIO: true, - exposeConsole: true, - tenantCustom: false, - logSearchEnabled: true, - prometheusEnabled: true, - logSearchVolumeSize: "5", - logSearchSizeFactor: "Gi", - logSearchSelectedStorageClass: "default", - logSearchImage: "", - kesImage: "", - logSearchPostgresImage: "", - logSearchPostgresInitImage: "", - prometheusVolumeSize: "5", - prometheusSizeFactor: "Gi", - prometheusSelectedStorageClass: "default", - prometheusImage: "", - prometheusSidecarImage: "", - prometheusInitImage: "", - setDomains: false, - consoleDomain: "", - minioDomains: [""], - tenantSecurityContext: { - runAsUser: "1000", - runAsGroup: "1000", - fsGroup: "1000", - runAsNonRoot: true, - }, - logSearchSecurityContext: { - runAsUser: "1000", - runAsGroup: "1000", - fsGroup: "1000", - runAsNonRoot: true, - }, - logSearchPostgresSecurityContext: { - runAsUser: "999", - runAsGroup: "999", - fsGroup: "999", - runAsNonRoot: true, - }, - prometheusSecurityContext: { - runAsUser: "1000", - runAsGroup: "1000", - fsGroup: "1000", - runAsNonRoot: true, - }, + logSearchSecurityContext: { + runAsUser: "1000", + runAsGroup: "1000", + fsGroup: "1000", + runAsNonRoot: true, }, - identityProvider: { - idpSelection: "Built-in", - accessKeys: [getRandomString(16)], - secretKeys: [getRandomString(32)], - openIDConfigurationURL: "", - openIDClientID: "", - openIDSecretID: "", - openIDCallbackURL: "", - openIDClaimName: "", - openIDScopes: "", - ADURL: "", - ADSkipTLS: false, - ADServerInsecure: false, - ADGroupSearchBaseDN: "", - ADGroupSearchFilter: "", - ADUserDNs: [""], - ADLookupBindDN: "", - ADLookupBindPassword: "", - ADUserDNSearchBaseDN: "", - ADUserDNSearchFilter: "", - ADServerStartTLS: false, + logSearchPostgresSecurityContext: { + runAsUser: "999", + runAsGroup: "999", + fsGroup: "999", + runAsNonRoot: true, }, - security: { - enableAutoCert: true, - enableCustomCerts: false, - enableTLS: true, - }, - encryption: { - enableEncryption: false, - encryptionType: "vault", - gemaltoEndpoint: "", - gemaltoToken: "", - gemaltoDomain: "", - gemaltoRetry: "0", - awsEndpoint: "", - awsRegion: "", - awsKMSKey: "", - awsAccessKey: "", - awsSecretKey: "", - awsToken: "", - vaultEndpoint: "", - vaultEngine: "", - vaultNamespace: "", - vaultPrefix: "", - vaultAppRoleEngine: "", - vaultId: "", - vaultSecret: "", - vaultRetry: "0", - vaultPing: "0", - azureEndpoint: "", - azureTenantID: "", - azureClientID: "", - azureClientSecret: "", - gcpProjectID: "", - gcpEndpoint: "", - gcpClientEmail: "", - gcpClientID: "", - gcpPrivateKeyID: "", - gcpPrivateKey: "", - enableCustomCertsForKES: false, - replicas: "1", - kesSecurityContext: { - runAsUser: "1000", - runAsGroup: "1000", - fsGroup: "1000", - runAsNonRoot: true, - }, - }, - tenantSize: { - volumeSize: "1024", - sizeFactor: "Gi", - drivesPerServer: "4", - nodes: "4", - memoryNode: "2", - ecParity: "", - ecParityChoices: [], - cleanECChoices: [], - untouchedECField: true, - distribution: { - error: "", - nodes: 0, - persistentVolumes: 0, - disks: 0, - }, - ecParityCalc: { - error: 0, - defaultEC: "", - erasureCodeSet: 0, - maxEC: "", - rawCapacity: "0", - storageFactors: [], - }, - limitSize: {}, - cpuToUse: "0", - // resource request - resourcesSpecifyLimit: false, - resourcesCPURequestError: "", - resourcesCPURequest: "", - resourcesCPULimitError: "", - resourcesCPULimit: "", - resourcesMemoryRequestError: "", - resourcesMemoryRequest: "", - resourcesMemoryLimitError: "", - resourcesMemoryLimit: "", - resourcesSize: { - error: "", - memoryRequest: 0, - memoryLimit: 0, - cpuRequest: 0, - cpuLimit: 0, - }, - maxAllocatableResources: { - min_allocatable_mem: 0, - min_allocatable_cpu: 0, - cpu_priority: { - max_allocatable_cpu: 0, - max_allocatable_mem: 0, - }, - mem_priority: { - max_allocatable_cpu: 0, - max_allocatable_mem: 0, - }, - }, - maxCPUsUse: "0", - maxMemorySize: "0", - integrationSelection: { - driveSize: { driveSize: "0", sizeUnit: "B" }, - CPU: 0, - typeSelection: "", - memory: 0, - drivesPerServer: 0, - storageClass: "", - }, - }, - affinity: { - nodeSelectorLabels: "", - podAffinity: "default", - withPodAntiAffinity: true, + prometheusSecurityContext: { + runAsUser: "1000", + runAsGroup: "1000", + fsGroup: "1000", + runAsNonRoot: true, }, }, - certificates: { - minioCertificates: [ - { - id: Date.now().toString(), - key: "", - cert: "", - encoded_key: "", - encoded_cert: "", - }, - ], - caCertificates: [ - { - id: Date.now().toString(), - key: "", - cert: "", - encoded_key: "", - encoded_cert: "", - }, - ], - consoleCaCertificates: [ - { - id: Date.now().toString(), - key: "", - cert: "", - encoded_key: "", - encoded_cert: "", - }, - ], - consoleCertificate: { - id: "console_cert_pair", - key: "", - cert: "", - encoded_key: "", - encoded_cert: "", - }, - serverCertificate: { - id: "encryptionServerCertificate", - key: "", - cert: "", - encoded_key: "", - encoded_cert: "", - }, - clientCertificate: { - id: "encryptionClientCertificate", - key: "", - cert: "", - encoded_key: "", - encoded_cert: "", - }, - vaultCertificate: { - id: "encryptionVaultCertificate", - key: "", - cert: "", - encoded_key: "", - encoded_cert: "", - }, - vaultCA: { - id: "encryptionVaultCA", - key: "", - cert: "", - encoded_key: "", - encoded_cert: "", - }, - gemaltoCA: { - id: "encryptionGemaltoCA", - key: "", - cert: "", - encoded_key: "", - encoded_cert: "", + identityProvider: { + idpSelection: "Built-in", + accessKeys: [getRandomString(16)], + secretKeys: [getRandomString(32)], + openIDConfigurationURL: "", + openIDClientID: "", + openIDSecretID: "", + openIDCallbackURL: "", + openIDClaimName: "", + openIDScopes: "", + ADURL: "", + ADSkipTLS: false, + ADServerInsecure: false, + ADGroupSearchBaseDN: "", + ADGroupSearchFilter: "", + ADUserDNs: [""], + ADLookupBindDN: "", + ADLookupBindPassword: "", + ADUserDNSearchBaseDN: "", + ADUserDNSearchFilter: "", + ADServerStartTLS: false, + }, + security: { + enableAutoCert: true, + enableCustomCerts: false, + enableTLS: true, + }, + encryption: { + enableEncryption: false, + encryptionType: "vault", + gemaltoEndpoint: "", + gemaltoToken: "", + gemaltoDomain: "", + gemaltoRetry: "0", + awsEndpoint: "", + awsRegion: "", + awsKMSKey: "", + awsAccessKey: "", + awsSecretKey: "", + awsToken: "", + vaultEndpoint: "", + vaultEngine: "", + vaultNamespace: "", + vaultPrefix: "", + vaultAppRoleEngine: "", + vaultId: "", + vaultSecret: "", + vaultRetry: "0", + vaultPing: "0", + azureEndpoint: "", + azureTenantID: "", + azureClientID: "", + azureClientSecret: "", + gcpProjectID: "", + gcpEndpoint: "", + gcpClientEmail: "", + gcpClientID: "", + gcpPrivateKeyID: "", + gcpPrivateKey: "", + enableCustomCertsForKES: false, + replicas: "1", + kesSecurityContext: { + runAsUser: "1000", + runAsGroup: "1000", + fsGroup: "1000", + runAsNonRoot: true, }, }, - nodeSelectorPairs: [{ key: "", value: "" }], - tolerations: [ - { - key: "", - tolerationSeconds: { seconds: 0 }, - value: "", - effect: ITolerationEffect.NoSchedule, - operator: ITolerationOperator.Equal, + tenantSize: { + volumeSize: "1024", + sizeFactor: "Gi", + drivesPerServer: "4", + nodes: "4", + memoryNode: "2", + ecParity: "", + ecParityChoices: [], + cleanECChoices: [], + untouchedECField: true, + distribution: { + error: "", + nodes: 0, + persistentVolumes: 0, + disks: 0, }, - ], + ecParityCalc: { + error: 0, + defaultEC: "", + erasureCodeSet: 0, + maxEC: "", + rawCapacity: "0", + storageFactors: [], + }, + limitSize: {}, + cpuToUse: "0", + // resource request + resourcesSpecifyLimit: false, + resourcesCPURequestError: "", + resourcesCPURequest: "", + resourcesCPULimitError: "", + resourcesCPULimit: "", + resourcesMemoryRequestError: "", + resourcesMemoryRequest: "", + resourcesMemoryLimitError: "", + resourcesMemoryLimit: "", + resourcesSize: { + error: "", + memoryRequest: 0, + memoryLimit: 0, + cpuRequest: 0, + cpuLimit: 0, + }, + maxAllocatableResources: { + min_allocatable_mem: 0, + min_allocatable_cpu: 0, + cpu_priority: { + max_allocatable_cpu: 0, + max_allocatable_mem: 0, + }, + mem_priority: { + max_allocatable_cpu: 0, + max_allocatable_mem: 0, + }, + }, + maxCPUsUse: "0", + maxMemorySize: "0", + integrationSelection: { + driveSize: { driveSize: "0", sizeUnit: "B" }, + CPU: 0, + typeSelection: "", + memory: 0, + drivesPerServer: 0, + storageClass: "", + }, + }, + affinity: { + nodeSelectorLabels: "", + podAffinity: "default", + withPodAntiAffinity: true, + }, }; + state.certificates = { + minioCertificates: [ + { + id: Date.now().toString(), + key: "", + cert: "", + encoded_key: "", + encoded_cert: "", + }, + ], + caCertificates: [ + { + id: Date.now().toString(), + key: "", + cert: "", + encoded_key: "", + encoded_cert: "", + }, + ], + consoleCaCertificates: [ + { + id: Date.now().toString(), + key: "", + cert: "", + encoded_key: "", + encoded_cert: "", + }, + ], + consoleCertificate: { + id: "console_cert_pair", + key: "", + cert: "", + encoded_key: "", + encoded_cert: "", + }, + serverCertificate: { + id: "encryptionServerCertificate", + key: "", + cert: "", + encoded_key: "", + encoded_cert: "", + }, + clientCertificate: { + id: "encryptionClientCertificate", + key: "", + cert: "", + encoded_key: "", + encoded_cert: "", + }, + vaultCertificate: { + id: "encryptionVaultCertificate", + key: "", + cert: "", + encoded_key: "", + encoded_cert: "", + }, + vaultCA: { + id: "encryptionVaultCA", + key: "", + cert: "", + encoded_key: "", + encoded_cert: "", + }, + gemaltoCA: { + id: "encryptionGemaltoCA", + key: "", + cert: "", + encoded_key: "", + encoded_cert: "", + }, + }; + state.nodeSelectorPairs = [{ key: "", value: "" }]; + state.tolerations = [ + { + key: "", + tolerationSeconds: { seconds: 0 }, + value: "", + effect: ITolerationEffect.NoSchedule, + operator: ITolerationOperator.Equal, + }, + ]; + state.createdAccount = null; + state.showNewCredentials = false; }, setKeyValuePairs: (state, action: PayloadAction) => { state.nodeSelectorPairs = action.payload; @@ -1057,6 +1070,45 @@ export const createTenantSlice = createSlice({ setIDP: (state, action: PayloadAction) => { state.fields.identityProvider.idpSelection = action.payload; }, + setTenantName: (state, action: PayloadAction) => { + state.fields.nameTenant.tenantName = action.payload; + delete state.validationErrors["tenant-name"]; + + const commonValidation = commonFormValidation([ + { + fieldKey: "tenant-name", + required: true, + pattern: /^[a-z0-9-]{3,63}$/, + customPatternMessage: + "Name only can contain lowercase letters, numbers and '-'. Min. Length: 3", + value: action.payload, + }, + ]); + + let isValid = false; + if ("tenant-name" in commonValidation) { + isValid = true; + state.validationErrors["tenant-name"] = commonValidation["tenant-name"]; + } + + flipValidPageInState(state, "nameTenant", isValid); + }, + }, + extraReducers: (builder) => { + builder + .addCase(createTenantAsync.pending, (state, action) => { + state.addingTenant = true; + state.createdAccount = null; + state.showNewCredentials = false; + }) + .addCase(createTenantAsync.rejected, (state, action) => { + state.addingTenant = false; + }) + .addCase(createTenantAsync.fulfilled, (state, action) => { + state.addingTenant = false; + state.createdAccount = action.payload; + state.showNewCredentials = true; + }); }, }); @@ -1097,6 +1149,7 @@ export const { addIDPADUsrAtIndex, removeIDPADUsrAtIndex, setIDP, + setTenantName, } = createTenantSlice.actions; export default createTenantSlice.reducer; diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/sliceUtils.ts b/portal-ui/src/screens/Console/Tenants/AddTenant/sliceUtils.ts new file mode 100644 index 000000000..56b72e075 --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/AddTenant/sliceUtils.ts @@ -0,0 +1,36 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { ICreateTenant } from "./createTenantSlice"; +import { Draft } from "@reduxjs/toolkit"; + +export const flipValidPageInState = ( + state: Draft, + pageName: string, + valid: boolean +) => { + let originValidPages = state.validPages; + if (valid) { + if (!originValidPages.includes(pageName)) { + originValidPages.push(pageName); + + state.validPages = [...originValidPages]; + } + } else { + const newSetOfPages = originValidPages.filter((elm) => elm !== pageName); + state.validPages = [...newSetOfPages]; + } +}; diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/thunks/createTenantThunk.ts b/portal-ui/src/screens/Console/Tenants/AddTenant/thunks/createTenantThunk.ts new file mode 100644 index 000000000..5ba2bc62c --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/AddTenant/thunks/createTenantThunk.ts @@ -0,0 +1,590 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { createAsyncThunk } from "@reduxjs/toolkit"; +import { AppState } from "../../../../../store"; +import { generatePoolName, getBytes } from "../../../../../common/utils"; +import { getDefaultAffinity, getNodeSelector } from "../../TenantDetails/utils"; +import { ITenantCreator } from "../../../../../common/types"; +import { KeyPair } from "../../ListTenants/utils"; +import { createTenantCall } from "../createTenantAPI"; +import { setErrorSnackMessage } from "../../../../../systemSlice"; + +export const createTenantAsync = createAsyncThunk( + "createTenant/createTenantAsync", + async (_, { getState, rejectWithValue, dispatch }) => { + const state = getState() as AppState; + + let fields = state.createTenant.fields; + let certificates = state.createTenant.certificates; + + const tenantName = fields.nameTenant.tenantName; + const selectedStorageClass = fields.nameTenant.selectedStorageClass; + const imageName = fields.configure.imageName; + const customDockerhub = fields.configure.customDockerhub; + const imageRegistry = fields.configure.imageRegistry; + const imageRegistryUsername = fields.configure.imageRegistryUsername; + const imageRegistryPassword = fields.configure.imageRegistryPassword; + const exposeMinIO = fields.configure.exposeMinIO; + const exposeConsole = fields.configure.exposeConsole; + const idpSelection = fields.identityProvider.idpSelection; + const openIDConfigurationURL = + fields.identityProvider.openIDConfigurationURL; + const openIDClientID = fields.identityProvider.openIDClientID; + const openIDClaimName = fields.identityProvider.openIDClaimName; + const openIDCallbackURL = fields.identityProvider.openIDCallbackURL; + const openIDScopes = fields.identityProvider.openIDScopes; + const openIDSecretID = fields.identityProvider.openIDSecretID; + const ADURL = fields.identityProvider.ADURL; + const ADSkipTLS = fields.identityProvider.ADSkipTLS; + const ADServerInsecure = fields.identityProvider.ADServerInsecure; + const ADGroupSearchBaseDN = fields.identityProvider.ADGroupSearchBaseDN; + const ADGroupSearchFilter = fields.identityProvider.ADGroupSearchFilter; + const ADUserDNs = fields.identityProvider.ADUserDNs; + const ADLookupBindDN = fields.identityProvider.ADLookupBindDN; + const ADLookupBindPassword = fields.identityProvider.ADLookupBindPassword; + const ADUserDNSearchBaseDN = fields.identityProvider.ADUserDNSearchBaseDN; + const ADUserDNSearchFilter = fields.identityProvider.ADUserDNSearchFilter; + const ADServerStartTLS = fields.identityProvider.ADServerStartTLS; + const accessKeys = fields.identityProvider.accessKeys; + const secretKeys = fields.identityProvider.secretKeys; + const minioCertificates = certificates.minioCertificates; + const caCertificates = certificates.caCertificates; + const consoleCaCertificates = certificates.consoleCaCertificates; + const consoleCertificate = certificates.consoleCertificate; + const serverCertificate = certificates.serverCertificate; + const clientCertificate = certificates.clientCertificate; + const vaultCertificate = certificates.vaultCertificate; + const vaultCA = certificates.vaultCA; + const gemaltoCA = certificates.gemaltoCA; + const enableEncryption = fields.encryption.enableEncryption; + const encryptionType = fields.encryption.encryptionType; + const gemaltoEndpoint = fields.encryption.gemaltoEndpoint; + const gemaltoToken = fields.encryption.gemaltoToken; + const gemaltoDomain = fields.encryption.gemaltoDomain; + const gemaltoRetry = fields.encryption.gemaltoRetry; + const awsEndpoint = fields.encryption.awsEndpoint; + const awsRegion = fields.encryption.awsRegion; + const awsKMSKey = fields.encryption.awsKMSKey; + const awsAccessKey = fields.encryption.awsAccessKey; + const awsSecretKey = fields.encryption.awsSecretKey; + const awsToken = fields.encryption.awsToken; + const vaultEndpoint = fields.encryption.vaultEndpoint; + const vaultEngine = fields.encryption.vaultEngine; + const vaultNamespace = fields.encryption.vaultNamespace; + const vaultPrefix = fields.encryption.vaultPrefix; + const vaultAppRoleEngine = fields.encryption.vaultAppRoleEngine; + const vaultId = fields.encryption.vaultId; + const vaultSecret = fields.encryption.vaultSecret; + const vaultRetry = fields.encryption.vaultRetry; + const vaultPing = fields.encryption.vaultPing; + const azureEndpoint = fields.encryption.azureEndpoint; + const azureTenantID = fields.encryption.azureTenantID; + const azureClientID = fields.encryption.azureClientID; + const azureClientSecret = fields.encryption.azureClientSecret; + const gcpProjectID = fields.encryption.gcpProjectID; + const gcpEndpoint = fields.encryption.gcpEndpoint; + const gcpClientEmail = fields.encryption.gcpClientEmail; + const gcpClientID = fields.encryption.gcpClientID; + const gcpPrivateKeyID = fields.encryption.gcpPrivateKeyID; + const gcpPrivateKey = fields.encryption.gcpPrivateKey; + const enableAutoCert = fields.security.enableAutoCert; + const enableTLS = fields.security.enableTLS; + const ecParity = fields.tenantSize.ecParity; + const distribution = fields.tenantSize.distribution; + const tenantCustom = fields.configure.tenantCustom; + const logSearchEnabled = fields.configure.logSearchEnabled; + const prometheusEnabled = fields.configure.prometheusEnabled; + const logSearchVolumeSize = fields.configure.logSearchVolumeSize; + const logSearchSelectedStorageClass = + fields.configure.logSearchSelectedStorageClass; + const logSearchImage = fields.configure.logSearchImage; + const kesImage = fields.configure.kesImage; + const logSearchPostgresImage = fields.configure.logSearchPostgresImage; + const logSearchPostgresInitImage = + fields.configure.logSearchPostgresInitImage; + const prometheusImage = fields.configure.prometheusImage; + const prometheusSidecarImage = fields.configure.prometheusSidecarImage; + const prometheusInitImage = fields.configure.prometheusInitImage; + const prometheusSelectedStorageClass = + fields.configure.prometheusSelectedStorageClass; + const prometheusVolumeSize = fields.configure.prometheusVolumeSize; + const affinityType = fields.affinity.podAffinity; + const nodeSelectorLabels = fields.affinity.nodeSelectorLabels; + const withPodAntiAffinity = fields.affinity.withPodAntiAffinity; + + const tenantSecurityContext = fields.configure.tenantSecurityContext; + const logSearchSecurityContext = fields.configure.logSearchSecurityContext; + const logSearchPostgresSecurityContext = + fields.configure.logSearchPostgresSecurityContext; + const prometheusSecurityContext = + fields.configure.prometheusSecurityContext; + const kesSecurityContext = fields.encryption.kesSecurityContext; + const kesReplicas = fields.encryption.replicas; + const setDomains = fields.configure.setDomains; + const minioDomains = fields.configure.minioDomains; + const consoleDomain = fields.configure.consoleDomain; + + let tolerations = state.createTenant.tolerations; + let namespace = state.createTenant.fields.nameTenant.namespace; + + const tolerationValues = tolerations.filter( + (toleration) => toleration.key.trim() !== "" + ); + + const poolName = generatePoolName([]); + + let affinityObject = {}; + + switch (affinityType) { + case "default": + affinityObject = { + affinity: getDefaultAffinity(tenantName, poolName), + }; + break; + case "nodeSelector": + affinityObject = { + affinity: getNodeSelector( + nodeSelectorLabels, + withPodAntiAffinity, + tenantName, + poolName + ), + }; + break; + } + + const erasureCode = ecParity.split(":")[1]; + + let dataSend: ITenantCreator = { + name: tenantName, + namespace: namespace, + access_key: "", + secret_key: "", + access_keys: [], + secret_keys: [], + enable_tls: enableTLS && enableAutoCert, + enable_console: true, + enable_prometheus: true, + service_name: "", + image: imageName, + expose_minio: exposeMinIO, + expose_console: exposeConsole, + pools: [ + { + name: poolName, + servers: distribution.nodes, + volumes_per_server: distribution.disks, + volume_configuration: { + size: distribution.pvSize, + storage_class_name: selectedStorageClass, + }, + securityContext: tenantCustom ? tenantSecurityContext : null, + ...affinityObject, + tolerations: tolerationValues, + }, + ], + erasureCodingParity: parseInt(erasureCode, 10), + }; + + // Set Resources + if ( + fields.tenantSize.resourcesCPURequest !== "" || + fields.tenantSize.resourcesCPULimit !== "" || + fields.tenantSize.resourcesMemoryRequest !== "" || + fields.tenantSize.resourcesMemoryLimit !== "" + ) { + dataSend.pools[0].resources = {}; + // requests + if ( + fields.tenantSize.resourcesCPURequest !== "" || + fields.tenantSize.resourcesMemoryRequest !== "" + ) { + dataSend.pools[0].resources.requests = {}; + if (fields.tenantSize.resourcesCPURequest !== "") { + dataSend.pools[0].resources.requests.cpu = parseInt( + fields.tenantSize.resourcesCPURequest + ); + } + if (fields.tenantSize.resourcesMemoryRequest !== "") { + dataSend.pools[0].resources.requests.memory = parseInt( + getBytes(fields.tenantSize.resourcesMemoryRequest, "Gi", true) + ); + } + } + // limits + if ( + fields.tenantSize.resourcesCPULimit !== "" || + fields.tenantSize.resourcesMemoryLimit !== "" + ) { + dataSend.pools[0].resources.limits = {}; + if (fields.tenantSize.resourcesCPULimit !== "") { + dataSend.pools[0].resources.limits.cpu = parseInt( + fields.tenantSize.resourcesCPULimit + ); + } + if (fields.tenantSize.resourcesMemoryLimit !== "") { + dataSend.pools[0].resources.limits.memory = parseInt( + getBytes(fields.tenantSize.resourcesMemoryLimit, "Gi", true) + ); + } + } + } + if (customDockerhub) { + dataSend = { + ...dataSend, + image_registry: { + registry: imageRegistry, + username: imageRegistryUsername, + password: imageRegistryPassword, + }, + }; + } + + if (logSearchEnabled) { + dataSend = { + ...dataSend, + logSearchConfiguration: { + storageClass: + logSearchSelectedStorageClass === "default" + ? "" + : logSearchSelectedStorageClass, + storageSize: parseInt(logSearchVolumeSize), + image: logSearchImage, + postgres_image: logSearchPostgresImage, + postgres_init_image: logSearchPostgresInitImage, + securityContext: logSearchSecurityContext, + postgres_securityContext: logSearchPostgresSecurityContext, + }, + }; + } + + if (prometheusEnabled) { + dataSend = { + ...dataSend, + prometheusConfiguration: { + storageClass: + prometheusSelectedStorageClass === "default" + ? "" + : prometheusSelectedStorageClass, + storageSize: parseInt(prometheusVolumeSize), + image: prometheusImage, + sidecar_image: prometheusSidecarImage, + init_image: prometheusInitImage, + securityContext: prometheusSecurityContext, + }, + }; + } + + let tenantCerts: any = null; + let consoleCerts: any = null; + let caCerts: any = null; + let consoleCaCerts: any = null; + + if (caCertificates.length > 0) { + caCerts = { + ca_certificates: caCertificates + .map((keyPair: KeyPair) => keyPair.encoded_cert) + .filter((keyPair) => keyPair), + }; + } + + if (consoleCaCertificates.length > 0) { + consoleCaCerts = { + console_ca_certificates: consoleCaCertificates + .map((keyPair: KeyPair) => keyPair.encoded_cert) + .filter((keyPair) => keyPair), + }; + } + + if (enableTLS && minioCertificates.length > 0) { + tenantCerts = { + minio: minioCertificates + .map((keyPair: KeyPair) => ({ + crt: keyPair.encoded_cert, + key: keyPair.encoded_key, + })) + .filter((keyPair) => keyPair.crt && keyPair.key), + }; + } + + if ( + enableTLS && + consoleCertificate.encoded_cert !== "" && + consoleCertificate.encoded_key !== "" + ) { + consoleCerts = { + console: { + crt: consoleCertificate.encoded_cert, + key: consoleCertificate.encoded_key, + }, + }; + } + + if (tenantCerts || consoleCerts || caCerts || consoleCaCerts) { + dataSend = { + ...dataSend, + tls: { + ...tenantCerts, + ...consoleCerts, + ...caCerts, + ...consoleCaCerts, + }, + }; + } + + if (enableEncryption) { + let insertEncrypt = {}; + + switch (encryptionType) { + case "gemalto": + let gemaltoCAIntroduce = {}; + + if (gemaltoCA.encoded_cert !== "") { + gemaltoCAIntroduce = { + ca: gemaltoCA.encoded_cert, + }; + } + insertEncrypt = { + gemalto: { + keysecure: { + endpoint: gemaltoEndpoint, + credentials: { + token: gemaltoToken, + domain: gemaltoDomain, + retry: parseInt(gemaltoRetry), + }, + tls: { + ...gemaltoCAIntroduce, + }, + }, + }, + }; + break; + case "aws": + insertEncrypt = { + aws: { + secretsmanager: { + endpoint: awsEndpoint, + region: awsRegion, + kmskey: awsKMSKey, + credentials: { + accesskey: awsAccessKey, + secretkey: awsSecretKey, + token: awsToken, + }, + }, + }, + }; + break; + case "azure": + insertEncrypt = { + azure: { + keyvault: { + endpoint: azureEndpoint, + credentials: { + tenant_id: azureTenantID, + client_id: azureClientID, + client_secret: azureClientSecret, + }, + }, + }, + }; + break; + case "gcp": + insertEncrypt = { + gcp: { + secretmanager: { + project_id: gcpProjectID, + endpoint: gcpEndpoint, + credentials: { + client_email: gcpClientEmail, + client_id: gcpClientID, + private_key_id: gcpPrivateKeyID, + private_key: gcpPrivateKey, + }, + }, + }, + }; + break; + case "vault": + let vaultKeyPair = null; + let vaultCAInsert = null; + if ( + vaultCertificate.encoded_key !== "" && + vaultCertificate.encoded_cert !== "" + ) { + vaultKeyPair = { + key: vaultCertificate.encoded_key, + crt: vaultCertificate.encoded_cert, + }; + } + if (vaultCA.encoded_cert !== "") { + vaultCAInsert = { + ca: vaultCA.encoded_cert, + }; + } + let vaultTLS = null; + if (vaultKeyPair || vaultCAInsert) { + vaultTLS = { + tls: { + ...vaultKeyPair, + ...vaultCAInsert, + }, + }; + } + insertEncrypt = { + vault: { + endpoint: vaultEndpoint, + engine: vaultEngine, + namespace: vaultNamespace, + prefix: vaultPrefix, + approle: { + engine: vaultAppRoleEngine, + id: vaultId, + secret: vaultSecret, + retry: parseInt(vaultRetry), + }, + ...vaultTLS, + status: { + ping: parseInt(vaultPing), + }, + }, + }; + break; + } + + let encryptionServerKeyPair: any = {}; + let encryptionClientKeyPair: any = {}; + + if ( + clientCertificate.encoded_key !== "" && + clientCertificate.encoded_cert !== "" + ) { + encryptionClientKeyPair = { + client: { + key: clientCertificate.encoded_key, + crt: clientCertificate.encoded_cert, + }, + }; + } + + if ( + serverCertificate.encoded_key !== "" && + serverCertificate.encoded_cert !== "" + ) { + encryptionServerKeyPair = { + server: { + key: serverCertificate.encoded_key, + crt: serverCertificate.encoded_cert, + }, + }; + } + + dataSend = { + ...dataSend, + encryption: { + replicas: kesReplicas, + securityContext: kesSecurityContext, + image: kesImage, + ...encryptionClientKeyPair, + ...encryptionServerKeyPair, + ...insertEncrypt, + }, + }; + } + + let dataIDP: any = {}; + switch (idpSelection) { + case "Built-in": + let keyarray = []; + for (let i = 0; i < accessKeys.length; i++) { + keyarray.push({ + access_key: accessKeys[i], + secret_key: secretKeys[i], + }); + } + dataIDP = { + keys: keyarray, + }; + break; + case "OpenID": + dataIDP = { + oidc: { + configuration_url: openIDConfigurationURL, + client_id: openIDClientID, + secret_id: openIDSecretID, + claim_name: openIDClaimName, + callback_url: openIDCallbackURL, + scopes: openIDScopes, + }, + }; + break; + case "AD": + dataIDP = { + active_directory: { + url: ADURL, + skip_tls_verification: ADSkipTLS, + server_insecure: ADServerInsecure, + group_search_base_dn: ADGroupSearchBaseDN, + group_search_filter: ADGroupSearchFilter, + user_dns: ADUserDNs, + lookup_bind_dn: ADLookupBindDN, + lookup_bind_password: ADLookupBindPassword, + user_dn_search_base_dn: ADUserDNSearchBaseDN, + user_dn_search_filter: ADUserDNSearchFilter, + server_start_tls: ADServerStartTLS, + }, + }; + break; + } + + let domains: any = {}; + let sendDomain: any = {}; + + if (setDomains) { + if (consoleDomain !== "") { + domains.console = consoleDomain; + } + + const filteredDomains = minioDomains.filter((dom) => dom.trim() !== ""); + + if (filteredDomains.length > 0) { + domains.minio = filteredDomains; + } + + if (Object.keys(domains).length > 0) { + sendDomain.domains = domains; + } + } + + dataSend = { + ...dataSend, + ...sendDomain, + idp: { ...dataIDP }, + }; + + const response = createTenantCall(dataSend) + .then((resp) => { + return resp; + }) + .catch((err) => { + dispatch(setErrorSnackMessage(err)); + return rejectWithValue(err); + }); + return response; + } +); diff --git a/portal-ui/src/screens/Console/Tenants/types.ts b/portal-ui/src/screens/Console/Tenants/types.ts index 85d0ab120..4fecd3a9f 100644 --- a/portal-ui/src/screens/Console/Tenants/types.ts +++ b/portal-ui/src/screens/Console/Tenants/types.ts @@ -384,5 +384,3 @@ export interface ITenantIdentityProviderResponse { user_dn_search_filter: string; }; } - -export type FieldsToHandle = INameTenantFields; diff --git a/restapi/errors.go b/restapi/errors.go index dec5c577e..50f5ab07b 100644 --- a/restapi/errors.go +++ b/restapi/errors.go @@ -27,7 +27,7 @@ import ( ) var ( - ErrDefault = errors.New("an errors occurred, please try again") + ErrDefault = errors.New("an error occurred, please try again") ErrInvalidLogin = errors.New("invalid Login") ErrForbidden = errors.New("403 Forbidden") ErrFileTooLarge = errors.New("413 File too Large")