Security Tab for operator-ui (#804)

Security Tab allow users to set various configurations related to TLS
certificates for MinIO and Console

- Enable/Disable AutoCert
- Add/Delete MinIO and Console Certificates
- Add/Delete MinIO and Console CA Certificates

Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
Lenin Alevski
2021-06-17 17:30:20 -07:00
committed by GitHub
parent 09503ed0c8
commit 85797749ba
38 changed files with 3717 additions and 34 deletions

View File

@@ -1,25 +1,25 @@
{
"files": {
"main.css": "/static/css/main.8cfac526.chunk.css",
"main.js": "/static/js/main.03b04a1b.chunk.js",
"main.js.map": "/static/js/main.03b04a1b.chunk.js.map",
"main.js": "/static/js/main.7915d40b.chunk.js",
"main.js.map": "/static/js/main.7915d40b.chunk.js.map",
"runtime-main.js": "/static/js/runtime-main.43a31377.js",
"runtime-main.js.map": "/static/js/runtime-main.43a31377.js.map",
"static/css/2.60e04a19.chunk.css": "/static/css/2.60e04a19.chunk.css",
"static/js/2.d609c10f.chunk.js": "/static/js/2.d609c10f.chunk.js",
"static/js/2.d609c10f.chunk.js.map": "/static/js/2.d609c10f.chunk.js.map",
"static/js/2.06e58ba0.chunk.js": "/static/js/2.06e58ba0.chunk.js",
"static/js/2.06e58ba0.chunk.js.map": "/static/js/2.06e58ba0.chunk.js.map",
"index.html": "/index.html",
"static/css/2.60e04a19.chunk.css.map": "/static/css/2.60e04a19.chunk.css.map",
"static/css/main.8cfac526.chunk.css.map": "/static/css/main.8cfac526.chunk.css.map",
"static/js/2.d609c10f.chunk.js.LICENSE.txt": "/static/js/2.d609c10f.chunk.js.LICENSE.txt",
"static/js/2.06e58ba0.chunk.js.LICENSE.txt": "/static/js/2.06e58ba0.chunk.js.LICENSE.txt",
"static/media/minio_console_logo.0837460e.svg": "/static/media/minio_console_logo.0837460e.svg",
"static/media/minio_operator_logo.1312b7c9.svg": "/static/media/minio_operator_logo.1312b7c9.svg"
},
"entrypoints": [
"static/js/runtime-main.43a31377.js",
"static/css/2.60e04a19.chunk.css",
"static/js/2.d609c10f.chunk.js",
"static/js/2.06e58ba0.chunk.js",
"static/css/main.8cfac526.chunk.css",
"static/js/main.03b04a1b.chunk.js"
"static/js/main.7915d40b.chunk.js"
]
}

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.60e04a19.chunk.css" rel="stylesheet"><link href="/static/css/main.8cfac526.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.d609c10f.chunk.js"></script><script src="/static/js/main.03b04a1b.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.60e04a19.chunk.css" rel="stylesheet"><link href="/static/css/main.8cfac526.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.06e58ba0.chunk.js"></script><script src="/static/js/main.7915d40b.chunk.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -378,6 +378,10 @@ const Console = ({
component: TenantDetails,
path: "/namespaces/:tenantNamespace/tenants/:tenantName/license",
},
{
component: TenantDetails,
path: "/namespaces/:tenantNamespace/tenants/:tenantName/security",
},
{
component: License,
path: "/license",

View File

@@ -92,6 +92,9 @@ export interface ITenant {
logEnabled: boolean;
monitoringEnabled: boolean;
encryptionEnabled: boolean;
minioTLS: boolean;
consoleTLS: boolean;
consoleEnabled: boolean;
idpAdEnabled: boolean;
idpOicEnabled: boolean;
health_status: string;

View File

@@ -0,0 +1,82 @@
import { ITenant } from "../ListTenants/types";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import {
containerForHeader,
tenantDetailsStyles,
} from "../../Common/FormComponents/common/styleLibrary";
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
LinearProgress,
} from "@material-ui/core";
import React, { useState } from "react";
interface IConfirmationDialog {
classes: any;
open: boolean;
cancelLabel: string;
okLabel: string;
onClose: any;
cancelOnClick: any;
okOnClick: any;
title: string;
description: string;
}
const styles = (theme: Theme) =>
createStyles({
...tenantDetailsStyles,
...containerForHeader(theme.spacing(4)),
});
const ConfirmationDialog = ({
classes,
open,
cancelLabel,
okLabel,
onClose,
cancelOnClick,
okOnClick,
title,
description,
}: IConfirmationDialog) => {
const [isSending, setIsSending] = useState<boolean>(false);
const onClick = () => {
setIsSending(true);
if (okOnClick !== null) {
okOnClick();
}
setIsSending(false);
};
if (!open) return null;
return (
<Dialog
open={open}
onClose={onClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{title}</DialogTitle>
<DialogContent>
{isSending && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
{description}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={cancelOnClick} color="primary" disabled={isSending}>
{cancelLabel || "Cancel"}
</Button>
<Button onClick={onClick} color="secondary" autoFocus>
{okLabel || "Ok"}
</Button>
</DialogActions>
</Dialog>
);
};
export default withStyles(styles)(ConfirmationDialog);

View File

@@ -45,6 +45,7 @@ import PoolsSummary from "./PoolsSummary";
import PodsSummary from "./PodsSummary";
import { AppState } from "../../../../store";
import TenantMetrics from "./TenantMetrics";
import TenantSecurity from "./TenantSecurity";
interface ITenantDetailsProps {
classes: any;
@@ -156,6 +157,7 @@ const TenantDetails = ({
case "pods":
case "metrics":
case "license":
case "security":
setTenantTab(section);
break;
default:
@@ -235,6 +237,7 @@ const TenantDetails = ({
>
<Tab value="summary" label="Summary" />
<Tab value="metrics" label="Metrics" />
<Tab value="security" label="Security" />
<Tab value="pools" label="Pools" />
<Tab value="pods" label="Pods" />
<Tab value="license" label="License" />
@@ -251,6 +254,10 @@ const TenantDetails = ({
path="/namespaces/:tenantNamespace/tenants/:tenantName/metrics"
component={TenantMetrics}
/>
<Route
path="/namespaces/:tenantNamespace/tenants/:tenantName/security"
component={TenantSecurity}
/>
<Route
path="/namespaces/:tenantNamespace/tenants/:tenantName/pools"
component={PoolsSummary}

View File

@@ -0,0 +1,950 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 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 <http://www.gnu.org/licenses/>.
import { ITenant } from "../ListTenants/types";
import { ICertificateInfo, ITenantSecurityResponse } from "../types";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import {
containerForHeader,
tenantDetailsStyles,
} from "../../Common/FormComponents/common/styleLibrary";
import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid";
import Chip from "@material-ui/core/Chip";
import React, { Fragment, useEffect, useState } from "react";
import Moment from "react-moment";
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import {
Button,
CircularProgress,
Divider,
LinearProgress,
Typography,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from "@material-ui/core";
import { KeyPair } from "../ListTenants/utils";
import FileSelector from "../../Common/FormComponents/FileSelector/FileSelector";
import api from "../../../../common/api";
import { setErrorSnackMessage } from "../../../../actions";
import { connect } from "react-redux";
import { AppState } from "../../../../store";
import { setTenantDetailsLoad } from "../actions";
import ConfirmationDialog from "./ConfirmationDialog";
interface ITenantSecurity {
classes: any;
loadingTenant: boolean;
tenant: ITenant | null;
setErrorSnackMessage: typeof setErrorSnackMessage;
setTenantDetailsLoad: typeof setTenantDetailsLoad;
}
const styles = (theme: Theme) =>
createStyles({
...tenantDetailsStyles,
loaderAlign: {
textAlign: "center",
},
title: {
marginTop: 35,
},
bold: { fontWeight: "bold" },
italic: { fontStyle: "italic" },
underline: { textDecorationLine: "underline" },
paperContainer: {
padding: "15px 15px 15px 50px",
},
verifiedIcon: {
width: 96,
position: "absolute",
right: 0,
bottom: 29,
},
noUnderLine: {
textDecoration: "none",
},
certificateInfo: {
height: "auto",
margin: 5,
},
certificateInfoName: {
fontWeight: "bold",
},
...containerForHeader(theme.spacing(4)),
});
const TenantSecurity = ({
classes,
tenant,
loadingTenant,
setErrorSnackMessage,
setTenantDetailsLoad,
}: ITenantSecurity) => {
const [isSending, setIsSending] = useState<boolean>(false);
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
const [enableAutoCert, setEnableAutoCert] = useState<boolean>(false);
const [enableCustomCerts, setEnableCustomCerts] = useState<boolean>(false);
const [certificatesToBeRemoved, setCertificatesToBeRemoved] = useState<
string[]
>([]);
// Console certificates
const [consoleCertificates, setConsoleCertificates] = useState<KeyPair[]>([
{
cert: "",
encoded_cert: "",
encoded_key: "",
id: Date.now().toString(),
key: "",
},
]);
const [consoleCaCertificates, setConsoleCaCertificates] = useState<KeyPair[]>(
[
{
cert: "",
encoded_cert: "",
encoded_key: "",
id: Date.now().toString(),
key: "",
},
]
);
const [consoleTLSCertificateSecrets, setConsoleTLSCertificateSecrets] =
useState<ICertificateInfo[]>([]);
const [consoleTLSCaCertificateSecrets, setConsoleTLSCaCertificateSecrets] =
useState<ICertificateInfo[]>([]);
// MinIO certificates
const [minioCertificates, setMinioCertificates] = useState<KeyPair[]>([
{
cert: "",
encoded_cert: "",
encoded_key: "",
id: Date.now().toString(),
key: "",
},
]);
const [minioCaCertificates, setMinioCaCertificates] = useState<KeyPair[]>([
{
cert: "",
encoded_cert: "",
encoded_key: "",
id: Date.now().toString(),
key: "",
},
]);
const [minioTLSCertificateSecrets, setMinioTLSCertificateSecrets] = useState<
ICertificateInfo[]
>([]);
const [minioTLSCaCertificateSecrets, setMinioTLSCaCertificateSecrets] =
useState<ICertificateInfo[]>([]);
useEffect(() => {
if (tenant) {
getTenantSecurityInfo();
}
}, [tenant]);
const getTenantSecurityInfo = () => {
api
.invoke(
"GET",
`/api/v1/namespaces/${tenant?.namespace}/tenants/${tenant?.name}/security`
)
.then((res: ITenantSecurityResponse) => {
setEnableAutoCert(res.autoCert);
if (
res.customCertificates.minio ||
res.customCertificates.minioCAs ||
res.customCertificates.console ||
res.customCertificates.consoleCAs
) {
setEnableCustomCerts(true);
}
setMinioTLSCertificateSecrets(res.customCertificates.minio || []);
setMinioTLSCaCertificateSecrets(res.customCertificates.minioCAs || []);
setConsoleTLSCertificateSecrets(res.customCertificates.console || []);
setConsoleTLSCaCertificateSecrets(
res.customCertificates.consoleCAs || []
);
})
.catch((err) => {
setErrorSnackMessage(err.message);
});
};
const updateTenantSecurity = () => {
setIsSending(true);
let payload = {
autoCert: enableAutoCert,
customCertificates: {},
};
if (enableCustomCerts) {
payload["customCertificates"] = {
secretsToBeDeleted: certificatesToBeRemoved,
minio: minioCertificates
.map((keyPair: KeyPair) => ({
crt: keyPair.encoded_cert,
key: keyPair.encoded_key,
}))
.filter((cert: any) => cert.crt && cert.key),
minioCAs: minioCaCertificates
.map((keyPair: KeyPair) => keyPair.encoded_cert)
.filter((cert: any) => cert),
console: consoleCertificates
.map((keyPair: KeyPair) => ({
crt: keyPair.encoded_cert,
key: keyPair.encoded_key,
}))
.filter((cert: any) => cert.crt && cert.key),
consoleCAs: consoleCaCertificates
.map((keyPair: KeyPair) => keyPair.encoded_cert)
.filter((cert: any) => cert),
};
} else {
payload["customCertificates"] = {
secretsToBeDeleted: [
...minioTLSCertificateSecrets.map((cert) => cert.name),
...minioTLSCaCertificateSecrets.map((cert) => cert.name),
...consoleTLSCertificateSecrets.map((cert) => cert.name),
...consoleTLSCaCertificateSecrets.map((cert) => cert.name),
],
minio: [],
minioCAs: [],
console: [],
consoleCAs: [],
};
}
api
.invoke(
"POST",
`/api/v1/namespaces/${tenant?.namespace}/tenants/${tenant?.name}/security`,
payload
)
.then(() => {
setIsSending(false);
// Close confirmation modal
setDialogOpen(false);
// Refresh Information and reset forms
setMinioCertificates([
{
cert: "",
encoded_cert: "",
encoded_key: "",
id: Date.now().toString(),
key: "",
},
]);
setMinioCaCertificates([
{
cert: "",
encoded_cert: "",
encoded_key: "",
id: Date.now().toString(),
key: "",
},
]);
setConsoleCertificates([
{
cert: "",
encoded_cert: "",
encoded_key: "",
id: Date.now().toString(),
key: "",
},
]);
setConsoleCaCertificates([
{
cert: "",
encoded_cert: "",
encoded_key: "",
id: Date.now().toString(),
key: "",
},
]);
getTenantSecurityInfo();
})
.catch((err) => {
setErrorSnackMessage(err);
setIsSending(false);
});
};
const removeCertificate = (certificateInfo: ICertificateInfo) => {
// TLS certificate secrets can be referenced MinIO, Console or KES, we need to remove the secret from all list and update
// the arrays
// Add certificate to the global list of secrets to be removed
setCertificatesToBeRemoved([
...certificatesToBeRemoved,
certificateInfo.name,
]);
// Update MinIO TLS certificate secrets
const updatedMinIOTLSCertificateSecrets = minioTLSCertificateSecrets.filter(
(certificateSecret) => certificateSecret.name != certificateInfo.name
);
const updatedMinIOTLSCaCertificateSecrets =
minioTLSCaCertificateSecrets.filter(
(certificateSecret) => certificateSecret.name != certificateInfo.name
);
setMinioTLSCertificateSecrets(updatedMinIOTLSCertificateSecrets);
setMinioTLSCaCertificateSecrets(updatedMinIOTLSCaCertificateSecrets);
// Update Console TLS certificate secrets
const updatedConsoleTLSCertificateSecrets =
consoleTLSCertificateSecrets.filter(
(certificateSecret) => certificateSecret.name != certificateInfo.name
);
const updatedConsoleTLSCaCertificateSecrets =
consoleTLSCaCertificateSecrets.filter(
(certificateSecret) => certificateSecret.name != certificateInfo.name
);
setConsoleTLSCertificateSecrets(updatedConsoleTLSCertificateSecrets);
setConsoleTLSCaCertificateSecrets(updatedConsoleTLSCaCertificateSecrets);
};
const addFileToKeyPair = (
type: string,
id: string,
key: string,
fileName: string,
value: string
) => {
let certificates = minioCertificates;
let updateCertificates: any = () => {};
switch (type) {
case "minio": {
certificates = minioCertificates;
updateCertificates = setMinioCertificates;
break;
}
case "minioCAs": {
certificates = minioCaCertificates;
updateCertificates = setMinioCaCertificates;
break;
}
case "console": {
certificates = consoleCertificates;
updateCertificates = setConsoleCertificates;
break;
}
case "consoleCAs": {
certificates = consoleCaCertificates;
updateCertificates = setConsoleCaCertificates;
break;
}
default:
}
const NCertList = certificates.map((item: KeyPair) => {
if (item.id === id) {
return {
...item,
[key]: fileName,
[`encoded_${key}`]: value,
};
}
return item;
});
updateCertificates(NCertList);
};
const deleteKeyPair = (type: string, id: string) => {
let certificates = minioCertificates;
let updateCertificates: any = () => {};
switch (type) {
case "minio": {
certificates = minioCertificates;
updateCertificates = setMinioCertificates;
break;
}
case "minioCAs": {
certificates = minioCaCertificates;
updateCertificates = setMinioCaCertificates;
break;
}
case "console": {
certificates = consoleCertificates;
updateCertificates = setConsoleCertificates;
break;
}
case "consoleCAs": {
certificates = consoleCaCertificates;
updateCertificates = setConsoleCaCertificates;
break;
}
default:
}
if (certificates.length > 1) {
const cleanCertsList = certificates.filter(
(item: KeyPair) => item.id !== id
);
updateCertificates(cleanCertsList);
}
};
const addKeyPair = (type: string) => {
let certificates = minioCertificates;
let updateCertificates: any = () => {};
switch (type) {
case "minio": {
certificates = minioCertificates;
updateCertificates = setMinioCertificates;
break;
}
case "minioCAs": {
certificates = minioCaCertificates;
updateCertificates = setMinioCaCertificates;
break;
}
case "console": {
certificates = consoleCertificates;
updateCertificates = setConsoleCertificates;
break;
}
case "consoleCAs": {
certificates = consoleCaCertificates;
updateCertificates = setConsoleCaCertificates;
break;
}
default:
}
const updatedCertificates = [
...certificates,
{
id: Date.now().toString(),
key: "",
cert: "",
encoded_key: "",
encoded_cert: "",
},
];
updateCertificates(updatedCertificates);
};
return (
<React.Fragment>
<ConfirmationDialog
open={dialogOpen}
title="Save and Restart"
description="Are you sure you want to save the changes and restart the service?"
onClose={() => setDialogOpen(false)}
cancelOnClick={() => setDialogOpen(false)}
okOnClick={updateTenantSecurity}
cancelLabel="Cancel"
okLabel={"Restart"}
/>
<br />
<Paper className={classes.paperContainer}>
{loadingTenant ? (
<div className={classes.loaderAlign}>
<CircularProgress />
</div>
) : (
<Fragment>
<Grid item xs={12} className={classes.title}>
<FormSwitchWrapper
value="enableAutoCert"
id="enableAutoCert"
name="enableAutoCert"
checked={enableAutoCert}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
setEnableAutoCert(checked);
}}
label={"Enable AutoCert"}
/>
<FormSwitchWrapper
value="enableCustomCerts"
id="enableCustomCerts"
name="enableCustomCerts"
checked={enableCustomCerts}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
setEnableCustomCerts(checked);
}}
label={"Custom Certificates"}
/>
</Grid>
{enableCustomCerts && (
<Fragment>
<Grid container>
<Grid container item xs={12}>
<Typography variant="overline" display="block" gutterBottom>
MinIO Certificates
</Typography>
</Grid>
<Grid container item xs={12}>
{minioTLSCertificateSecrets.map(
(certificateInfo: ICertificateInfo) => (
<Chip
key={certificateInfo.name}
variant="outlined"
color="primary"
className={classes.certificateInfo}
label={
<div>
<Typography
variant="subtitle1"
display="block"
gutterBottom
>
{certificateInfo.name}
</Typography>
<Typography
className={classes.italic}
variant="caption"
display="block"
gutterBottom
>
{certificateInfo.domains.join(", ")}
</Typography>
<Typography
className={classes.bold}
variant="overline"
gutterBottom
>
Expiry:&nbsp;
</Typography>
<Typography variant="caption" gutterBottom>
<Moment format="YYYY-MM-DD">
{certificateInfo.expiry}
</Moment>
</Typography>
</div>
}
onDelete={() => removeCertificate(certificateInfo)}
/>
)
)}
</Grid>
<Grid container item xs={12}>
<br />
</Grid>
<Grid container item xs={12}>
{minioCertificates.map((keyPair) => (
<Fragment key={keyPair.id}>
<Grid item xs={5}>
<FileSelector
onChange={(encodedValue, fileName) =>
addFileToKeyPair(
"minio",
keyPair.id,
"cert",
fileName,
encodedValue
)
}
accept=".cer,.crt,.cert,.pem"
id="tlsCert"
name="tlsCert"
label="Cert"
value={keyPair.cert}
/>
</Grid>
<Grid item xs={5}>
<FileSelector
onChange={(encodedValue, fileName) =>
addFileToKeyPair(
"minio",
keyPair.id,
"key",
fileName,
encodedValue
)
}
accept=".key,.pem"
id="tlsKey"
name="tlsKey"
label="Key"
value={keyPair.key}
/>
</Grid>
<Grid item xs={1}>
<Button
onClick={() => deleteKeyPair("minio", keyPair.id)}
color="secondary"
>
Remove
</Button>
</Grid>
</Fragment>
))}
</Grid>
<Grid container item xs={12}>
<Button onClick={() => addKeyPair("minio")} color="primary">
Add More
</Button>
</Grid>
<Grid container item xs={12}>
<br />
</Grid>
<Grid container item xs={12}>
<Typography variant="overline" display="block" gutterBottom>
MinIO CA Certificates
</Typography>
</Grid>
<Grid container item xs={12}>
{minioTLSCaCertificateSecrets.map(
(certificateInfo: ICertificateInfo) => (
<Chip
key={certificateInfo.name}
variant="outlined"
color="primary"
className={classes.certificateInfo}
label={
<div>
<Typography
variant="subtitle1"
display="block"
gutterBottom
>
{certificateInfo.name}
</Typography>
<Typography
className={classes.italic}
variant="caption"
display="block"
gutterBottom
>
{certificateInfo.domains.join(", ")}
</Typography>
<Typography
className={classes.bold}
variant="overline"
gutterBottom
>
Expiry:&nbsp;
</Typography>
<Typography variant="caption" gutterBottom>
<Moment format="YYYY-MM-DD">
{certificateInfo.expiry}
</Moment>
</Typography>
</div>
}
onDelete={() => removeCertificate(certificateInfo)}
/>
)
)}
</Grid>
<Grid container item xs={12}>
<br />
</Grid>
<Grid container item xs={12}>
{minioCaCertificates.map((keyPair: KeyPair) => (
<Fragment key={keyPair.id}>
<Grid item xs={10}>
<FileSelector
onChange={(encodedValue, fileName) =>
addFileToKeyPair(
"minioCAs",
keyPair.id,
"cert",
fileName,
encodedValue
)
}
accept=".cer,.crt,.cert,.pem"
id="tlsCert"
name="tlsCert"
label="Cert"
value={keyPair.cert}
/>
</Grid>
<Grid item xs={1}>
<Button
onClick={() =>
deleteKeyPair("minioCAs", keyPair.id)
}
color="secondary"
>
Remove
</Button>
</Grid>
</Fragment>
))}
</Grid>
<Grid container item xs={12}>
<Button
onClick={() => addKeyPair("minioCAs")}
color="primary"
>
Add More
</Button>
</Grid>
</Grid>
<Grid container>
<Grid item xs={12}>
<br />
<Divider />
<br />
</Grid>
</Grid>
{tenant?.consoleEnabled ? (
<Fragment>
<Grid container>
<Grid container item xs={12}>
<Typography
variant="overline"
display="block"
gutterBottom
>
Console Certificates
</Typography>
</Grid>
<Grid container item xs={12}>
{consoleTLSCertificateSecrets.map(
(certificateInfo: ICertificateInfo) => (
<Chip
key={certificateInfo.name}
variant="outlined"
color="primary"
className={classes.certificateInfo}
label={
<div>
<Typography
variant="subtitle1"
display="block"
gutterBottom
>
{certificateInfo.name}
</Typography>
<Typography
className={classes.italic}
variant="caption"
display="block"
gutterBottom
>
{certificateInfo.domains.join(", ")}
</Typography>
<Typography
className={classes.bold}
variant="overline"
gutterBottom
>
Expiry:&nbsp;
</Typography>
<Typography variant="caption" gutterBottom>
<Moment format="YYYY-MM-DD">
{certificateInfo.expiry}
</Moment>
</Typography>
</div>
}
onDelete={() =>
removeCertificate(certificateInfo)
}
/>
)
)}
</Grid>
<Grid container item xs={12}>
<br />
</Grid>
<Grid container item xs={12}>
{consoleCertificates.map((keyPair: KeyPair) => (
<Fragment key={keyPair.id}>
<Grid item xs={6}>
<FileSelector
onChange={(encodedValue, fileName) =>
addFileToKeyPair(
"console",
keyPair.id,
"cert",
fileName,
encodedValue
)
}
accept=".cer,.crt,.cert,.pem"
id="consoleCert"
name="consoleCert"
label="Cert"
value={keyPair.cert}
/>
</Grid>
<Grid item xs={6}>
<FileSelector
onChange={(encodedValue, fileName) =>
addFileToKeyPair(
"console",
keyPair.id,
"key",
fileName,
encodedValue
)
}
accept=".key,.pem"
id="consoleKey"
name="consoleKey"
label="Key"
value={keyPair.key}
/>
</Grid>
</Fragment>
))}
</Grid>
<Grid container item xs={12}>
<Typography
variant="overline"
display="block"
gutterBottom
>
Console CA Certificates
</Typography>
</Grid>
<Grid container item xs={12}>
{consoleTLSCaCertificateSecrets.map(
(certificateInfo: ICertificateInfo) => (
<Chip
key={certificateInfo.name}
variant="outlined"
color="primary"
className={classes.certificateInfo}
label={
<div>
<Typography
variant="subtitle1"
display="block"
gutterBottom
>
{certificateInfo.name}
</Typography>
<Typography
className={classes.italic}
variant="caption"
display="block"
gutterBottom
>
{certificateInfo.domains.join(", ")}
</Typography>
<Typography
className={classes.bold}
variant="overline"
gutterBottom
>
Expiry:&nbsp;
</Typography>
<Typography variant="caption" gutterBottom>
<Moment format="YYYY-MM-DD">
{certificateInfo.expiry}
</Moment>
</Typography>
</div>
}
onDelete={() =>
removeCertificate(certificateInfo)
}
/>
)
)}
</Grid>
<Grid container item xs={12}>
<br />
</Grid>
<Grid container item xs={12}>
{consoleCaCertificates.map((keyPair: KeyPair) => (
<Fragment key={keyPair.id}>
<Grid item xs={10}>
<FileSelector
onChange={(encodedValue, fileName) =>
addFileToKeyPair(
"consoleCAs",
keyPair.id,
"cert",
fileName,
encodedValue
)
}
accept=".cer,.crt,.cert,.pem"
id="tlsCert"
name="tlsCert"
label="Cert"
value={keyPair.cert}
/>
</Grid>
<Grid item xs={1}>
<Button
onClick={() =>
deleteKeyPair("consoleCAs", keyPair.id)
}
color="secondary"
>
Remove
</Button>
</Grid>
</Fragment>
))}
</Grid>
<Grid container item xs={12}>
<Button
onClick={() => addKeyPair("consoleCAs")}
color="primary"
>
Add More
</Button>
</Grid>
</Grid>
<Grid container>
<Grid item xs={12}>
<br />
<Divider />
<br />
</Grid>
</Grid>
</Fragment>
) : null}
</Fragment>
)}
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={dialogOpen}
onClick={() => setDialogOpen(true)}
>
Save
</Button>
</Grid>
</Fragment>
)}
</Paper>
</React.Fragment>
);
};
const mapState = (state: AppState) => ({
loadingTenant: state.tenants.tenantDetails.loadingTenant,
selectedTenant: state.tenants.tenantDetails.currentTenant,
tenant: state.tenants.tenantDetails.tenantInfo,
});
const mapDispatchToProps = {
setErrorSnackMessage,
setTenantDetailsLoad,
};
const connector = connect(mapState, mapDispatchToProps);
export default withStyles(styles)(connector(TenantSecurity));

View File

@@ -39,6 +39,9 @@ interface ITenantsSummary {
logEnabled: boolean;
monitoringEnabled: boolean;
encryptionEnabled: boolean;
minioTLS: boolean;
consoleTLS: boolean;
consoleEnabled: boolean;
adEnabled: boolean;
oicEnabled: boolean;
loadingTenant: boolean;
@@ -77,6 +80,9 @@ const TenantSummary = ({
logEnabled,
monitoringEnabled,
encryptionEnabled,
minioTLS,
consoleTLS,
consoleEnabled,
adEnabled,
oicEnabled,
loadingTenant,
@@ -359,6 +365,32 @@ const TenantSummary = ({
</Button>
</td>
</tr>
<tr>
<td className={classes.titleCol}>MinIO TLS:</td>
<td>
<Button
color="primary"
className={classes.anchorButton}
>
{minioTLS ? "Enabled" : "Disabled"}
</Button>
</td>
{consoleEnabled ? (
<Fragment>
<td className={classes.titleCol}>Console TLS:</td>
<td>
<Button
color="primary"
className={classes.anchorButton}
>
{consoleTLS ? "Enabled" : "Disabled"}
</Button>
</td>
</Fragment>
) : (
<td />
)}
</tr>
<tr>
<td className={classes.titleCol}>Encryption:</td>
<td>
@@ -430,6 +462,13 @@ const mapState = (state: AppState) => ({
"encryptionEnabled",
false
),
minioTLS: get(state.tenants.tenantDetails.tenantInfo, "minioTLS", false),
consoleTLS: get(state.tenants.tenantDetails.tenantInfo, "consoleTLS", false),
consoleEnabled: get(
state.tenants.tenantDetails.tenantInfo,
"consoleEnabled",
false
),
adEnabled: get(state.tenants.tenantDetails.tenantInfo, "idpAdEnabled", false),
oicEnabled: get(
state.tenants.tenantDetails.tenantInfo,

View File

@@ -58,6 +58,24 @@ export const TENANT_DETAILS_SET_CURRENT_TENANT =
"TENANT_DETAILS/SET_CURRENT_TENANT";
export const TENANT_DETAILS_SET_TENANT = "TENANT_DETAILS/SET_TENANT";
export const TENANT_DETAILS_SET_TAB = "TENANT_DETAILS/SET_TAB";
export interface ICertificateInfo {
name: string;
serialNumber: string;
domains: string[];
expiry: string;
}
export interface ICustomCertificates {
minio: ICertificateInfo[];
minioCAs: ICertificateInfo[];
console: ICertificateInfo[];
consoleCAs: ICertificateInfo[];
}
export interface ITenantSecurityResponse {
autoCert: boolean;
customCertificates: ICustomCertificates;
}
export interface ICreateTenant {
page: number;