Add Audit Log and Log DB security context selector and split screen into tabs (#2156)

* Added security context selector for logging and log DB to Audit Log screen, split Audit Log screen into tabs
* Cleaned up tab titles, disabled Save button while loading
This commit is contained in:
jinapurapu
2022-06-30 14:28:08 -07:00
committed by GitHub
parent 10c56a91da
commit a8bc58a420
13 changed files with 1335 additions and 615 deletions

View File

@@ -54,6 +54,9 @@ type TenantLogs struct {
// db node selector
DbNodeSelector []*NodeSelector `json:"dbNodeSelector"`
// db security context
DbSecurityContext *SecurityContext `json:"dbSecurityContext,omitempty"`
// db service account name
DbServiceAccountName string `json:"dbServiceAccountName,omitempty"`
@@ -84,6 +87,9 @@ type TenantLogs struct {
// node selector
NodeSelector []*NodeSelector `json:"nodeSelector"`
// security context
SecurityContext *SecurityContext `json:"securityContext,omitempty"`
// service account name
ServiceAccountName string `json:"serviceAccountName,omitempty"`
}
@@ -108,6 +114,10 @@ func (m *TenantLogs) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validateDbSecurityContext(formats); err != nil {
res = append(res, err)
}
if err := m.validateLabels(formats); err != nil {
res = append(res, err)
}
@@ -116,6 +126,10 @@ func (m *TenantLogs) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validateSecurityContext(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
@@ -226,6 +240,25 @@ func (m *TenantLogs) validateDbNodeSelector(formats strfmt.Registry) error {
return nil
}
func (m *TenantLogs) validateDbSecurityContext(formats strfmt.Registry) error {
if swag.IsZero(m.DbSecurityContext) { // not required
return nil
}
if m.DbSecurityContext != nil {
if err := m.DbSecurityContext.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("dbSecurityContext")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("dbSecurityContext")
}
return err
}
}
return nil
}
func (m *TenantLogs) validateLabels(formats strfmt.Registry) error {
if swag.IsZero(m.Labels) { // not required
return nil
@@ -278,6 +311,25 @@ func (m *TenantLogs) validateNodeSelector(formats strfmt.Registry) error {
return nil
}
func (m *TenantLogs) validateSecurityContext(formats strfmt.Registry) error {
if swag.IsZero(m.SecurityContext) { // not required
return nil
}
if m.SecurityContext != nil {
if err := m.SecurityContext.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("securityContext")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("securityContext")
}
return err
}
}
return nil
}
// ContextValidate validate this tenant logs based on the context it is used
func (m *TenantLogs) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
var res []error
@@ -298,6 +350,10 @@ func (m *TenantLogs) ContextValidate(ctx context.Context, formats strfmt.Registr
res = append(res, err)
}
if err := m.contextValidateDbSecurityContext(ctx, formats); err != nil {
res = append(res, err)
}
if err := m.contextValidateLabels(ctx, formats); err != nil {
res = append(res, err)
}
@@ -306,6 +362,10 @@ func (m *TenantLogs) ContextValidate(ctx context.Context, formats strfmt.Registr
res = append(res, err)
}
if err := m.contextValidateSecurityContext(ctx, formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
@@ -392,6 +452,22 @@ func (m *TenantLogs) contextValidateDbNodeSelector(ctx context.Context, formats
return nil
}
func (m *TenantLogs) contextValidateDbSecurityContext(ctx context.Context, formats strfmt.Registry) error {
if m.DbSecurityContext != nil {
if err := m.DbSecurityContext.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("dbSecurityContext")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("dbSecurityContext")
}
return err
}
}
return nil
}
func (m *TenantLogs) contextValidateLabels(ctx context.Context, formats strfmt.Registry) error {
for i := 0; i < len(m.Labels); i++ {
@@ -432,6 +508,22 @@ func (m *TenantLogs) contextValidateNodeSelector(ctx context.Context, formats st
return nil
}
func (m *TenantLogs) contextValidateSecurityContext(ctx context.Context, formats strfmt.Registry) error {
if m.SecurityContext != nil {
if err := m.SecurityContext.ContextValidate(ctx, formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("securityContext")
} else if ce, ok := err.(*errors.CompositeError); ok {
return ce.ValidateName("securityContext")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *TenantLogs) MarshalBinary() ([]byte, error) {
if m == nil {

View File

@@ -4206,6 +4206,10 @@ func init() {
"$ref": "#/definitions/nodeSelector"
}
},
"dbSecurityContext": {
"type": "object",
"$ref": "#/definitions/securityContext"
},
"dbServiceAccountName": {
"type": "string"
},
@@ -4242,6 +4246,10 @@ func init() {
"$ref": "#/definitions/nodeSelector"
}
},
"securityContext": {
"type": "object",
"$ref": "#/definitions/securityContext"
},
"serviceAccountName": {
"type": "string"
}
@@ -9568,6 +9576,10 @@ func init() {
"$ref": "#/definitions/nodeSelector"
}
},
"dbSecurityContext": {
"type": "object",
"$ref": "#/definitions/securityContext"
},
"dbServiceAccountName": {
"type": "string"
},
@@ -9604,6 +9616,10 @@ func init() {
"$ref": "#/definitions/nodeSelector"
}
},
"securityContext": {
"type": "object",
"$ref": "#/definitions/securityContext"
},
"serviceAccountName": {
"type": "string"
}

View File

@@ -1357,7 +1357,7 @@ func getTenantUsageResponse(session *models.Principal, params operator_api.GetTe
return info, nil
}
// getTenantLogsResponse returns the logs of a tenant
// getTenantLogsResponse returns the Audit Log and Log DB configuration of a tenant
func getTenantLogsResponse(session *models.Principal, params operator_api.GetTenantLogsParams) (*models.TenantLogs, *models.Error) {
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
defer cancel()
@@ -1392,11 +1392,9 @@ func getTenantLogsResponse(session *models.Principal, params operator_api.GetTen
for k, v := range minTenant.Spec.Log.NodeSelector {
nodeSelector = append(nodeSelector, &models.NodeSelector{Key: k, Value: v})
}
if minTenant.Spec.Log.Db == nil {
minTenant.Spec.Log.Db = &miniov2.LogDbConfig{}
}
dbAnnotations := []*models.Annotation{}
for k, v := range minTenant.Spec.Log.Db.Annotations {
dbAnnotations = append(dbAnnotations, &models.Annotation{Key: k, Value: v})
@@ -1409,6 +1407,15 @@ func getTenantLogsResponse(session *models.Principal, params operator_api.GetTen
for k, v := range minTenant.Spec.Log.Db.NodeSelector {
dbNodeSelector = append(dbNodeSelector, &models.NodeSelector{Key: k, Value: v})
}
var logSecurityContext *models.SecurityContext
var logDBSecurityContext *models.SecurityContext
if minTenant.Spec.Log.SecurityContext != nil {
logSecurityContext = convertK8sSCToModelSC(minTenant.Spec.Log.SecurityContext)
}
if minTenant.Spec.Log.Db.SecurityContext != nil {
logDBSecurityContext = convertK8sSCToModelSC(minTenant.Spec.Log.Db.SecurityContext)
}
if minTenant.Spec.Log.Audit == nil || minTenant.Spec.Log.Audit.DiskCapacityGB == nil {
minTenant.Spec.Log.Audit = &miniov2.AuditConfig{DiskCapacityGB: swag.Int(0)}
@@ -1421,12 +1428,14 @@ func getTenantLogsResponse(session *models.Principal, params operator_api.GetTen
Labels: labels,
NodeSelector: nodeSelector,
ServiceAccountName: minTenant.Spec.Log.ServiceAccountName,
SecurityContext: logSecurityContext,
DbImage: minTenant.Spec.Log.Db.Image,
DbInitImage: minTenant.Spec.Log.Db.InitImage,
DbAnnotations: dbAnnotations,
DbLabels: dbLabels,
DbNodeSelector: dbNodeSelector,
DbServiceAccountName: minTenant.Spec.Log.Db.ServiceAccountName,
DbSecurityContext: logDBSecurityContext,
Disabled: false,
}
@@ -1454,7 +1463,7 @@ func getTenantLogsResponse(session *models.Principal, params operator_api.GetTen
return tenantLoggingConfiguration, nil
}
// setTenantLogsResponse returns the logs of a tenant
// setTenantLogsResponse updates the Audit Log and Log DB configuration for the tenant
func setTenantLogsResponse(session *models.Principal, params operator_api.SetTenantLogsParams) (bool, *models.Error) {
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
defer cancel()
@@ -1474,44 +1483,54 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
}
labels := make(map[string]string)
for i := 0; i < len(params.Data.Labels); i++ {
if params.Data.Labels[i] != nil {
labels[params.Data.Labels[i].Key] = params.Data.Labels[i].Value
if params.Data.Labels != nil {
for i := 0; i < len(params.Data.Labels); i++ {
if params.Data.Labels[i] != nil {
labels[params.Data.Labels[i].Key] = params.Data.Labels[i].Value
}
}
minTenant.Spec.Log.Labels = labels
}
minTenant.Spec.Log.Labels = labels
annotations := make(map[string]string)
for i := 0; i < len(params.Data.Annotations); i++ {
if params.Data.Annotations[i] != nil {
annotations[params.Data.Annotations[i].Key] = params.Data.Annotations[i].Value
if params.Data.Annotations != nil {
annotations := make(map[string]string)
for i := 0; i < len(params.Data.Annotations); i++ {
if params.Data.Annotations[i] != nil {
annotations[params.Data.Annotations[i].Key] = params.Data.Annotations[i].Value
}
}
minTenant.Spec.Log.Annotations = annotations
}
minTenant.Spec.Log.Annotations = annotations
nodeSelector := make(map[string]string)
for i := 0; i < len(params.Data.NodeSelector); i++ {
if params.Data.NodeSelector[i] != nil {
nodeSelector[params.Data.NodeSelector[i].Key] = params.Data.NodeSelector[i].Value
if params.Data.NodeSelector != nil {
nodeSelector := make(map[string]string)
for i := 0; i < len(params.Data.NodeSelector); i++ {
if params.Data.NodeSelector[i] != nil {
nodeSelector[params.Data.NodeSelector[i].Key] = params.Data.NodeSelector[i].Value
}
}
minTenant.Spec.Log.NodeSelector = nodeSelector
}
minTenant.Spec.Log.NodeSelector = nodeSelector
logResourceRequest := make(corev1.ResourceList)
if reflect.TypeOf(params.Data.LogCPURequest).Kind() == reflect.String && params.Data.LogCPURequest != "0Gi" && params.Data.LogCPURequest != "" {
cpuQuantity, err := resource.ParseQuantity(params.Data.LogCPURequest)
if err != nil {
return false, restapi.ErrorWithContext(ctx, err)
if len(params.Data.LogCPURequest) > 0 {
if reflect.TypeOf(params.Data.LogCPURequest).Kind() == reflect.String && params.Data.LogCPURequest != "0Gi" && params.Data.LogCPURequest != "" {
cpuQuantity, err := resource.ParseQuantity(params.Data.LogCPURequest)
if err != nil {
return false, restapi.ErrorWithContext(ctx, err)
}
logResourceRequest["cpu"] = cpuQuantity
minTenant.Spec.Log.Resources.Requests = logResourceRequest
}
logResourceRequest["cpu"] = cpuQuantity
minTenant.Spec.Log.Resources.Requests = logResourceRequest
}
if reflect.TypeOf(params.Data.LogMemRequest).Kind() == reflect.String {
memQuantity, err := resource.ParseQuantity(params.Data.LogMemRequest)
if err != nil {
return false, restapi.ErrorWithContext(ctx, err)
}
if len(params.Data.LogMemRequest) > 0 {
if reflect.TypeOf(params.Data.LogMemRequest).Kind() == reflect.String && params.Data.LogMemRequest != "" {
memQuantity, err := resource.ParseQuantity(params.Data.LogMemRequest)
if err != nil {
return false, restapi.ErrorWithContext(ctx, err)
}
logResourceRequest["memory"] = memQuantity
minTenant.Spec.Log.Resources.Requests = logResourceRequest
logResourceRequest["memory"] = memQuantity
minTenant.Spec.Log.Resources.Requests = logResourceRequest
}
}
modified := false
@@ -1519,97 +1538,122 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
modified = true
}
dbLabels := make(map[string]string)
for i := 0; i < len(params.Data.DbLabels); i++ {
if params.Data.DbLabels[i] != nil {
dbLabels[params.Data.DbLabels[i].Key] = params.Data.DbLabels[i].Value
if params.Data.DbLabels != nil {
for i := 0; i < len(params.Data.DbLabels); i++ {
if params.Data.DbLabels[i] != nil {
dbLabels[params.Data.DbLabels[i].Key] = params.Data.DbLabels[i].Value
}
modified = true
}
modified = true
}
dbAnnotations := make(map[string]string)
for i := 0; i < len(params.Data.DbAnnotations); i++ {
if params.Data.DbAnnotations[i] != nil {
dbAnnotations[params.Data.DbAnnotations[i].Key] = params.Data.DbAnnotations[i].Value
if params.Data.DbAnnotations != nil {
for i := 0; i < len(params.Data.DbAnnotations); i++ {
if params.Data.DbAnnotations[i] != nil {
dbAnnotations[params.Data.DbAnnotations[i].Key] = params.Data.DbAnnotations[i].Value
}
modified = true
}
modified = true
}
dbNodeSelector := make(map[string]string)
for i := 0; i < len(params.Data.DbNodeSelector); i++ {
if params.Data.DbNodeSelector[i] != nil {
dbNodeSelector[params.Data.DbNodeSelector[i].Key] = params.Data.DbNodeSelector[i].Value
}
modified = true
}
logDBResourceRequest := make(corev1.ResourceList)
if reflect.TypeOf(params.Data.LogDBCPURequest).Kind() == reflect.String && params.Data.LogDBCPURequest != "0Gi" && params.Data.LogDBCPURequest != "" {
dbCPUQuantity, err := resource.ParseQuantity(params.Data.LogDBCPURequest)
if err != nil {
return false, restapi.ErrorWithContext(ctx, err)
}
logDBResourceRequest["cpu"] = dbCPUQuantity
minTenant.Spec.Log.Db.Resources.Requests = logDBResourceRequest
}
if reflect.TypeOf(params.Data.LogDBMemRequest).Kind() == reflect.String {
dbMemQuantity, err := resource.ParseQuantity(params.Data.LogDBMemRequest)
if err != nil {
return false, restapi.ErrorWithContext(ctx, err)
}
logDBResourceRequest["memory"] = dbMemQuantity
minTenant.Spec.Log.Db.Resources.Requests = logDBResourceRequest
}
minTenant.Spec.Log.Image = params.Data.Image
diskCapacityGB, err := strconv.Atoi(params.Data.DiskCapacityGB)
if err == nil {
if minTenant.Spec.Log.Audit != nil && minTenant.Spec.Log.Audit.DiskCapacityGB != nil {
*minTenant.Spec.Log.Audit.DiskCapacityGB = diskCapacityGB
} else {
minTenant.Spec.Log.Audit = &miniov2.AuditConfig{DiskCapacityGB: swag.Int(diskCapacityGB)}
}
}
minTenant.Spec.Log.ServiceAccountName = params.Data.ServiceAccountName
if params.Data.DbImage != "" || params.Data.DbServiceAccountName != "" {
modified = true
}
if modified {
if minTenant.Spec.Log.Db == nil {
// Default class name for Log search
diskSpaceFromAPI := int64(5) * humanize.GiByte // Default is 5Gi
logSearchStorageClass := "standard"
logSearchDiskSpace := resource.NewQuantity(diskSpaceFromAPI, resource.DecimalExponent)
minTenant.Spec.Log.Db = &miniov2.LogDbConfig{
VolumeClaimTemplate: &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: params.Tenant + "-log",
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{
corev1.ReadWriteOnce,
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: *logSearchDiskSpace,
},
},
StorageClassName: &logSearchStorageClass,
},
},
Labels: dbLabels,
Annotations: dbAnnotations,
NodeSelector: dbNodeSelector,
Image: params.Data.DbImage,
ServiceAccountName: params.Data.DbServiceAccountName,
Resources: corev1.ResourceRequirements{
Requests: minTenant.Spec.Log.Db.Resources.Requests,
},
if params.Data.DbNodeSelector != nil {
for i := 0; i < len(params.Data.DbNodeSelector); i++ {
if params.Data.DbNodeSelector[i] != nil {
dbNodeSelector[params.Data.DbNodeSelector[i].Key] = params.Data.DbNodeSelector[i].Value
}
modified = true
}
}
logDBResourceRequest := make(corev1.ResourceList)
if len(params.Data.LogDBCPURequest) > 0 {
if reflect.TypeOf(params.Data.LogDBCPURequest).Kind() == reflect.String && params.Data.LogDBCPURequest != "0Gi" && params.Data.LogDBCPURequest != "" {
dbCPUQuantity, err := resource.ParseQuantity(params.Data.LogDBCPURequest)
if err != nil {
return false, restapi.ErrorWithContext(ctx, err)
}
logDBResourceRequest["cpu"] = dbCPUQuantity
minTenant.Spec.Log.Db.Resources.Requests = logDBResourceRequest
}
}
if len(params.Data.LogDBMemRequest) > 0 {
if reflect.TypeOf(params.Data.LogDBMemRequest).Kind() == reflect.String && params.Data.LogDBMemRequest != "" {
dbMemQuantity, err := resource.ParseQuantity(params.Data.LogDBMemRequest)
if err != nil {
return false, restapi.ErrorWithContext(ctx, err)
}
logDBResourceRequest["memory"] = dbMemQuantity
minTenant.Spec.Log.Db.Resources.Requests = logDBResourceRequest
}
}
if len(params.Data.Image) > 0 {
minTenant.Spec.Log.Image = params.Data.Image
}
if params.Data.SecurityContext != nil {
minTenant.Spec.Log.SecurityContext, err = convertModelSCToK8sSC(params.Data.SecurityContext)
if err != nil {
return false, restapi.ErrorWithContext(ctx, err)
}
}
if len(params.Data.DiskCapacityGB) > 0 {
diskCapacityGB, err := strconv.Atoi(params.Data.DiskCapacityGB)
if err == nil {
if minTenant.Spec.Log.Audit != nil && minTenant.Spec.Log.Audit.DiskCapacityGB != nil {
*minTenant.Spec.Log.Audit.DiskCapacityGB = diskCapacityGB
} else {
minTenant.Spec.Log.Audit = &miniov2.AuditConfig{DiskCapacityGB: swag.Int(diskCapacityGB)}
}
}
}
if params.Data.DbLabels != nil {
minTenant.Spec.Log.ServiceAccountName = params.Data.ServiceAccountName
if params.Data.DbImage != "" || params.Data.DbServiceAccountName != "" {
modified = true
}
if modified {
if minTenant.Spec.Log.Db == nil {
// Default class name for Log search
diskSpaceFromAPI := int64(5) * humanize.GiByte // Default is 5Gi
logSearchStorageClass := "standard"
logSearchDiskSpace := resource.NewQuantity(diskSpaceFromAPI, resource.DecimalExponent)
minTenant.Spec.Log.Db = &miniov2.LogDbConfig{
VolumeClaimTemplate: &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: params.Tenant + "-log",
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{
corev1.ReadWriteOnce,
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: *logSearchDiskSpace,
},
},
StorageClassName: &logSearchStorageClass,
},
},
Labels: dbLabels,
Annotations: dbAnnotations,
NodeSelector: dbNodeSelector,
Image: params.Data.DbImage,
ServiceAccountName: params.Data.DbServiceAccountName,
Resources: corev1.ResourceRequirements{
Requests: minTenant.Spec.Log.Db.Resources.Requests,
},
}
} else {
minTenant.Spec.Log.Db.Labels = dbLabels
minTenant.Spec.Log.Db.Annotations = dbAnnotations
minTenant.Spec.Log.Db.NodeSelector = dbNodeSelector
minTenant.Spec.Log.Db.Image = params.Data.DbImage
minTenant.Spec.Log.Db.ServiceAccountName = params.Data.DbServiceAccountName
minTenant.Spec.Log.Db.SecurityContext, err = convertModelSCToK8sSC(params.Data.DbSecurityContext)
if err != nil {
return false, restapi.ErrorWithContext(ctx, err)
}
}
} else {
minTenant.Spec.Log.Db.Labels = dbLabels
minTenant.Spec.Log.Db.Annotations = dbAnnotations
minTenant.Spec.Log.Db.NodeSelector = dbNodeSelector
minTenant.Spec.Log.Db.Image = params.Data.DbImage
minTenant.Spec.Log.Db.ServiceAccountName = params.Data.DbServiceAccountName
}
}

View File

@@ -795,6 +795,16 @@ export const wizardCommon = {
alignItems: "center" as const,
justifyContent: "flex-start" as const,
},
multiContainerStackNarrow: {
display: "flex" ,
alignItems: "center" ,
justifyContent: "flex-start",
gap: "8px",
"@media (max-width: 750px)": {
flexFlow: "column",
flexDirection: "column",
},
},
sizeFactorContainer: {
marginLeft: 8,
alignSelf: "flex-start" as const,

View File

@@ -238,6 +238,8 @@ export interface ITenantLogsStruct {
logMemRequest: string;
logDBCPURequest: string;
logDBMemRequest: string;
securityContext: ISecurityContext;
dbSecurityContext: ISecurityContext;
}
export interface ValueUnit {
@@ -273,3 +275,10 @@ export interface IEditPoolRequest {
export interface IPlotBarValues {
[key: string]: CapacityValue;
}
export interface ITenantAuditLogs {
classes: any;
labels: IKeyValue[];
annotations: IKeyValue[];
nodeSelector: IKeyValue[];
}

View File

@@ -0,0 +1,346 @@
// 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 <http://www.gnu.org/licenses/>.
//import { ISecurityContext} from "../types";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import {
containerForHeader,
createTenantCommon,
formFieldStyles,
modalBasic,
spacingUtils,
tenantDetailsStyles,
wizardCommon,
} from "../../Common/FormComponents/common/styleLibrary";
import React, { Fragment, useState } from "react";
import { useSelector } from "react-redux";
import { AppState, useAppDispatch } from "../../../../store";
import api from "../../../../common/api";
import { ErrorResponseHandler } from "../../../../common/types";
import { useParams } from "react-router-dom";
import Grid from "@mui/material/Grid";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { Button } from "@mui/material";
import {
setErrorSnackMessage,
setSnackBarMessage,
} from "../../../../systemSlice";
import { IKeyValue, ITenantAuditLogs } from "../ListTenants/types";
import KeyPairEdit from "./KeyPairEdit";
import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import {
setDBImage,
setDBInitImage,
setDBCPURequest,
setDBMemRequest,
setDBRunAsUser,
setDBFSGroup,
setDBRunAsGroup,
setDBRunAsNonRoot,
setRefreshLoggingInfo,
} from "../TenantDetails/tenantAuditLogSlice";
import SecurityContextSelector from "../securityContextSelector";
import { clearValidationError } from "../utils";
const styles = (theme: Theme) =>
createStyles({
...tenantDetailsStyles,
...spacingUtils,
bold: { fontWeight: "bold" },
italic: { fontStyle: "italic" },
fileItem: {
marginRight: 10,
display: "flex",
"& div label": {
minWidth: 50,
},
"@media (max-width: 900px)": {
flexFlow: "column",
},
},
...containerForHeader(theme.spacing(4)),
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
});
const LoggingDBDetails = ({ classes, labels, annotations, nodeSelector }: ITenantAuditLogs) => {
const dispatch = useAppDispatch();
const { tenantName, tenantNamespace } = useParams();
const dbImage = useSelector(
(state: AppState) => state.editTenantLogging.dbImage
);
const dbInitImage = useSelector(
(state: AppState) => state.editTenantLogging.dbInitImage
);
const dbCpuRequest = useSelector(
(state: AppState) => state.editTenantLogging.dbCPURequest
);
const dbMemRequest = useSelector(
(state: AppState) => state.editTenantLogging.dbMemRequest
);
const dbServiceAccountName = useSelector(
(state: AppState) => state.editTenantLogging.dbServiceAccountName
);
const dbRunAsGroup = useSelector(
(state: AppState) => state.editTenantLogging.dbSecurityContext.runAsGroup
);
const dbRunAsUser = useSelector(
(state: AppState) => state.editTenantLogging.dbSecurityContext.runAsUser
)
const dbFSGroup = useSelector(
(state: AppState) => state.editTenantLogging.dbSecurityContext.fsGroup
)
const dbRunAsNonRoot = useSelector(
(state: AppState) => state.editTenantLogging.dbSecurityContext.runAsNonRoot
)
const [validationErrors, setValidationErrors] = useState<any>({});
const [dbLabels, setDBLabels] = useState<IKeyValue[]>((labels != null && labels.length > 0) ? labels : [{ key: "", value: "" }]);
const [dbAnnotations, setDBAnnotations] = useState<IKeyValue[]>((annotations != null && annotations.length > 0)? annotations : [{ key: "", value: "" }]);
const [dbNodeSelector, setDBNodeSelector] = useState<IKeyValue[]>((nodeSelector != null && nodeSelector.length > 0)? nodeSelector :[{ key: "", value: "" }]);
const [dbLabelsError, setDBLabelsError] = useState<any>({});
const [dbAnnotationsError, setDBAnnotationsError] = useState<any>({});
const [dbNodeSelectorError, setDBNodeSelectorError] = useState<any>({});
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
const trim = (x: IKeyValue[]): IKeyValue[] => {
let retval: IKeyValue[] = [];
for (let i = 0; i < x.length; i++) {
if (x[i].key !== "") {
retval.push(x[i]);
}
}
return retval;
};
const checkValid = (): boolean => {
if (
Object.keys(validationErrors).length !== 0 ||
Object.keys(dbNodeSelectorError).length !== 0 ||
Object.keys(dbAnnotationsError).length !== 0 ||
Object.keys(dbLabelsError).length !== 0
) {
let err: ErrorResponseHandler = {
errorMessage: "Invalid entry",
detailedError: "",
};
dispatch(setErrorSnackMessage(err));
return false;
} else {
return true;
}
};
const submitLoggingInfo = () => {
if (checkValid()) {
const dbSecurityContext = {
runAsGroup: dbRunAsGroup != null ? dbRunAsGroup : "",
runAsUser: dbRunAsUser != null ? dbRunAsUser : "",
fsGroup: dbFSGroup != null ? dbFSGroup : "",
runAsNonRoot: dbRunAsNonRoot != null ? dbRunAsNonRoot : true,
}
api
.invoke(
"PUT",
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/log`,
{
dbLabels: trim(dbLabels),
dbAnnotations: trim(dbAnnotations),
dbNodeSelector: trim(dbNodeSelector),
dbImage: dbImage,
dbInitImage: dbInitImage,
dbServiceAccountName: dbServiceAccountName,
logDBCPURequest: dbCpuRequest,
logDBMemRequest: dbMemRequest,
dbSecurityContext: dbSecurityContext,
}
)
.then(() => {
setRefreshLoggingInfo(true);
dispatch(setSnackBarMessage(`Audit Log DB configuration updated.`));
})
.catch((err: ErrorResponseHandler) => {
setErrorSnackMessage(err);
});
}
};
return (
<Fragment>
<Fragment>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbImage`}
label={"DB Postgres Image"}
placeholder={"library/postgres:13"}
name={`dbImage`}
value={dbImage}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBImage(event.target.value));
}
cleanValidation(`dbImage`);
}}
key={`dbImage`}
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
error={validationErrors[`dbImage`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbInitImage`}
label={"DB Init Image"}
placeholder={"library/busybox:1.33.1"}
name={`dbInitImage`}
value={dbInitImage}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBInitImage(event.target.value));
}
cleanValidation(`dbInitImage`);
}}
key={`dbInitImage`}
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
error={validationErrors[`dbInitImage`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbCPURequest`}
label={"DB CPU Request"}
placeholder={"DB CPU Request"}
name={`dbCPURequest`}
value={dbCpuRequest}
pattern={"[0-9]*"}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBCPURequest(event.target.value));
}
cleanValidation(`dbCPURequest`);
}}
key={`dbCPURequest`}
error={validationErrors[`dbCPURequest`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbMemRequest`}
label={"DB Memory Request"}
placeholder={"DB Memory request"}
name={`dbMemRequest`}
value={dbMemRequest}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBMemRequest(event.target.value));
}
cleanValidation(`dbMemRequest`);
}}
pattern={"[0-9]*"}
key={`dbMemRequest`}
error={validationErrors[`dbMemRequest`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>DB Labels</span>
<KeyPairEdit
newValues={dbLabels}
setNewValues={setDBLabels}
paramName={"dbLabels"}
error={dbLabelsError}
setError={setDBLabelsError}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>DB Annotations</span>
<KeyPairEdit
newValues={dbAnnotations}
setNewValues={setDBAnnotations}
paramName={"dbAnnotations"}
error={dbAnnotationsError}
setError={setDBAnnotationsError}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>DB Node Selector</span>
<KeyPairEdit
newValues={dbNodeSelector}
setNewValues={setDBNodeSelector}
paramName={"DB Node Selector"}
error={dbNodeSelectorError}
setError={setDBNodeSelectorError}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<SecurityContextSelector classes={classes}
runAsGroup={dbRunAsGroup}
runAsUser={dbRunAsUser}
fsGroup={dbFSGroup}
runAsNonRoot={dbRunAsNonRoot}
setFSGroup={(value : string)=>dispatch(setDBFSGroup(value))}
setRunAsUser={(value : string)=>dispatch(setDBRunAsUser(value))}
setRunAsGroup={(value : string)=>dispatch(setDBRunAsGroup(value))}
setRunAsNonRoot={(value : boolean)=>dispatch(setDBRunAsNonRoot(value))}
/></Grid>
<Grid item xs={12} textAlign={"right"}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={!checkValid()}
onClick={() => submitLoggingInfo()}
>
Save
</Button>
</Grid>
</Fragment>
</Fragment>
);
};
export default withStyles(styles)(LoggingDBDetails);

View File

@@ -0,0 +1,379 @@
// 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 <http://www.gnu.org/licenses/>.
//import { ISecurityContext} from "../types";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import {
containerForHeader,
createTenantCommon,
formFieldStyles,
modalBasic,
spacingUtils,
tenantDetailsStyles,
wizardCommon,
} from "../../Common/FormComponents/common/styleLibrary";
import React, { Fragment, useState } from "react";
import { useSelector } from "react-redux";
import { AppState, useAppDispatch } from "../../../../store";
import api from "../../../../common/api";
import { ErrorResponseHandler } from "../../../../common/types";
import { useParams } from "react-router-dom";
import Grid from "@mui/material/Grid";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { Button } from "@mui/material";
import {
setErrorSnackMessage,
setSnackBarMessage,
} from "../../../../systemSlice";
import { IKeyValue, ITenantAuditLogs } from "../ListTenants/types";
import KeyPairEdit from "./KeyPairEdit";
import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import SecurityContextSelector from "../securityContextSelector";
import { clearValidationError } from "../utils";
import {
setImage,
setDiskCapacityGB,
setServiceAccountName,
setCPURequest,
setMemRequest,
setRunAsUser,
setFSGroup,
setRunAsGroup,
setRunAsNonRoot,
setRefreshLoggingInfo,
} from "../TenantDetails/tenantAuditLogSlice";
const styles = (theme: Theme) =>
createStyles({
...tenantDetailsStyles,
...spacingUtils,
bold: { fontWeight: "bold" },
italic: { fontStyle: "italic" },
fileItem: {
marginRight: 10,
display: "flex",
"& div label": {
minWidth: 50,
},
"@media (max-width: 900px)": {
flexFlow: "column",
},
},
...containerForHeader(theme.spacing(4)),
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
});
const TenantAuditLogging = ({ classes, labels, annotations, nodeSelector }: ITenantAuditLogs) => {
const dispatch = useAppDispatch();
const { tenantName, tenantNamespace } = useParams();
const auditLoggingEnabled = useSelector(
(state: AppState) => state.editTenantLogging.auditLoggingEnabled
);
const image = useSelector(
(state: AppState) => state.editTenantLogging.image
);
const diskCapacityGB = useSelector(
(state: AppState) => state.editTenantLogging.diskCapacityGB
);
const cpuRequest = useSelector(
(state: AppState) => state.editTenantLogging.cpuRequest
);
const memRequest = useSelector(
(state: AppState) => state.editTenantLogging.memRequest
);
const serviceAccountName = useSelector(
(state: AppState) => state.editTenantLogging.serviceAccountName
);
const runAsGroup = useSelector(
(state: AppState) => state.editTenantLogging.securityContext.runAsGroup
);
const runAsUser = useSelector(
(state: AppState) => state.editTenantLogging.securityContext.runAsUser
)
const fsGroup = useSelector(
(state: AppState) => state.editTenantLogging.securityContext.fsGroup
)
const runAsNonRoot = useSelector(
(state: AppState) => state.editTenantLogging.securityContext.runAsNonRoot
)
const [validationErrors, setValidationErrors] = useState<any>({});
const [loading, setLoading] = useState<boolean>(false);
const [logLabels, setLabels] = useState<IKeyValue[]>((labels != null && labels.length > 0) ? labels : [{ key: "", value: "" }]);
const [logAnnotations, setAnnotations] = useState<IKeyValue[]>((annotations != null && annotations.length > 0)? annotations : [{ key: "", value: "" }]);
const [logNodeSelector, setNodeSelector] = useState<IKeyValue[]>((nodeSelector != null && nodeSelector.length > 0)? nodeSelector :[{ key: "", value: "" }]);
const [labelsError, setLabelsError] = useState<any>({});
const [annotationsError, setAnnotationsError] = useState<any>({});
const [nodeSelectorError, setNodeSelectorError] = useState<any>({});
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
const trim = (x: IKeyValue[]): IKeyValue[] => {
let retval: IKeyValue[] = [];
for (let i = 0; i < x.length; i++) {
if (x[i].key !== "") {
retval.push(x[i]);
}
}
return retval;
};
const checkValid = (): boolean => {
if (
Object.keys(validationErrors).length !== 0 ||
Object.keys(labelsError).length !== 0 ||
Object.keys(annotationsError).length !== 0 ||
Object.keys(nodeSelectorError).length !== 0
) {
let err: ErrorResponseHandler = {
errorMessage: "Invalid entry",
detailedError: "",
};
dispatch(setErrorSnackMessage(err));
return false;
} else {
return true;
}
};
const submitLoggingInfo = () => {
if (checkValid()) {
setLoading(true);
const securityContext = {
runAsGroup: runAsGroup != null ? runAsGroup : "",
runAsUser: runAsUser != null ? runAsUser : "",
fsGroup: fsGroup != null ? fsGroup : "",
runAsNonRoot: runAsNonRoot != null ? runAsNonRoot : true,
}
api
.invoke(
"PUT",
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/log`,
{
labels: trim(logLabels),
annotations: trim(logAnnotations),
nodeSelector: trim(logNodeSelector),
image: image,
diskCapacityGB: diskCapacityGB.toString(),
serviceAccountName: serviceAccountName,
logCPURequest: cpuRequest,
logMemRequest: memRequest,
securityContext: securityContext,
}
)
.then(() => {
setRefreshLoggingInfo(true);
dispatch(setSnackBarMessage(`Audit Log configuration updated.`));
setLoading(false);
})
.catch((err: ErrorResponseHandler) => {
setErrorSnackMessage(err);
setLoading(false);
});
}
};
return (
<Fragment>
{auditLoggingEnabled && (
<Fragment>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`image`}
label={"Image"}
placeholder={"minio/operator:v4.4.22"}
name={`image`}
value={image}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setImage(event.target.value));
}
cleanValidation(`image`);
}}
key={`image`}
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
error={validationErrors[`image`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`diskCapacityGB`}
label={"Disk Capacity"}
placeholder={"Disk Capacity"}
name={`diskCapacityGB`}
value={!isNaN(diskCapacityGB) ? diskCapacityGB.toString() : "0"}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDiskCapacityGB(parseInt(event.target.value)));
}
cleanValidation(`diskCapacityGB`);
}}
key={`diskCapacityGB`}
pattern={"[0-9]*"}
error={validationErrors[`diskCapacityGB`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`cpuRequest`}
label={"CPU Request"}
placeholder={"CPU Request"}
name={`cpuRequest`}
value={cpuRequest}
pattern={"[0-9]*"}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setCPURequest(event.target.value));
}
cleanValidation(`cpuRequest`);
}}
key={`cpuRequest`}
error={validationErrors[`cpuRequest`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`memRequest`}
label={"Memory Request"}
placeholder={"Memory request"}
name={`memRequest`}
value={memRequest}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setMemRequest(event.target.value));
}
cleanValidation(`memRequest`);
}}
pattern={"[0-9]*"}
key={`memRequest`}
error={validationErrors[`memRequest`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`serviceAccountName`}
label={"Service Account"}
placeholder={"Service Account Name"}
name={`serviceAccountName`}
value={serviceAccountName}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setServiceAccountName(event.target.value));
}
cleanValidation(`serviceAccountName`);
}}
key={`serviceAccountName`}
pattern={"^[a-zA-Z0-9-.]{1,253}$"}
error={validationErrors[`serviceAccountName`] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<SecurityContextSelector classes={classes}
runAsGroup={runAsGroup}
runAsUser={runAsUser}
fsGroup={fsGroup}
runAsNonRoot={runAsNonRoot}
setFSGroup={(value : string)=>dispatch(setFSGroup(value))}
setRunAsUser={(value : string)=>dispatch(setRunAsUser(value))}
setRunAsGroup={(value : string)=>dispatch(setRunAsGroup(value))}
setRunAsNonRoot={(value : boolean)=>dispatch(setRunAsNonRoot(value))}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Labels</span>
<KeyPairEdit
newValues={logLabels}
setNewValues={setLabels}
paramName={"Labels"}
error={labelsError}
setError={setLabelsError}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Annotations</span>
<KeyPairEdit
newValues={logAnnotations }
setNewValues={setAnnotations}
paramName={"Annotations"}
error={annotationsError}
setError={setAnnotationsError}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Node Selector</span>
<KeyPairEdit
newValues={logNodeSelector}
setNewValues={setNodeSelector}
paramName={"Node Selector"}
error={nodeSelectorError}
setError={setNodeSelectorError}
/>
</Grid>
<Grid item xs={12} textAlign={"right"}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={loading || !checkValid()}
onClick={() => submitLoggingInfo()}
>
Save
</Button>
</Grid>
</Fragment>
)}
</Fragment>
);
};
export default withStyles(styles)(TenantAuditLogging);

View File

@@ -1,5 +1,6 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
// 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
@@ -14,220 +15,129 @@
// 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 { ISecurityContext} from "../types";
import React, { Fragment, useEffect, useState } from "react";
import { Theme } from "@mui/material/styles";
import { useParams } from "react-router-dom";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import {
containerForHeader,
createTenantCommon,
formFieldStyles,
modalBasic,
spacingUtils,
tenantDetailsStyles,
wizardCommon,
} from "../../Common/FormComponents/common/styleLibrary";
import React, { Fragment, useEffect, useState } from "react";
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
import Grid from "@mui/material/Grid";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { DialogContentText } from "@mui/material";
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
import api from "../../../../common/api";
import { useSelector } from "react-redux";
import { AppState, useAppDispatch } from "../../../../store";
import api from "../../../../common/api";
import { ErrorResponseHandler } from "../../../../common/types";
import { useParams } from "react-router-dom";
import { setErrorSnackMessage} from "../../../../systemSlice";
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import Grid from "@mui/material/Grid";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { Button, DialogContentText } from "@mui/material";
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
import {
setErrorSnackMessage,
setSnackBarMessage,
} from "../../../../systemSlice";
import { IKeyValue } from "../ListTenants/types";
import KeyPairEdit from "./KeyPairEdit";
import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import { ITenantLogsStruct } from "../ListTenants/types";
import { IKeyValue } from "../ListTenants/types";
import LoggingDetails from "./LoggingDetails";
import LoggingDBDetails from "./LoggingDBDetails";
import {
setAuditLoggingEnabled,
setAuditLoggingEnabled,
setDBImage,
setDBInitImage,
setDBServiceAccountName,
setDBCPURequest,
setDBMemRequest,
setDBRunAsUser,
setDBFSGroup,
setDBRunAsGroup,
setDBRunAsNonRoot,
setImage,
setDBImage,
setDBInitImage,
setDiskCapacityGB,
setServiceAccountName,
setDBServiceAccountName,
setCPURequest,
setMemRequest,
setDBCPURequest,
setDBMemRequest,
setRunAsUser,
setFSGroup,
setRunAsGroup,
setRunAsNonRoot,
resetAuditLogForm,
} from "../TenantDetails/tenantAuditLogSlice";
import { clearValidationError } from "../utils";
interface ITenantAuditLogs {
interface ILoggingScreenProps {
classes: any;
}
const styles = (theme: Theme) =>
createStyles({
...tenantDetailsStyles,
...spacingUtils,
bold: { fontWeight: "bold" },
italic: { fontStyle: "italic" },
fileItem: {
marginRight: 10,
display: "flex",
"& div label": {
minWidth: 50,
},
"@media (max-width: 900px)": {
flexFlow: "column",
},
breadcrumLink: {
textDecoration: "none",
color: "black",
},
...containerForHeader(theme.spacing(4)),
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
});
const TenantAuditLogging = ({ classes }: ITenantAuditLogs) => {
const LoggingScreen = ({ classes }: ILoggingScreenProps) => {
const { tenantNamespace, tenantName } = useParams();
const [curTab, setCurTab] = useState<number>(0);
const [loading, setLoading] = useState<boolean>(true);
const [toggleConfirmOpen, setToggleConfirmOpen] = useState<boolean>(false);
const [refreshLoggingInfo, setRefreshLoggingInfo] = useState<boolean>(true);
const [dbLabels, setDBLabels] = useState<IKeyValue[]>([{ key: "", value: "" }]);
const [dbAnnotations, setDBAnnotations] = useState<IKeyValue[]>([{ key: "", value: "" }]);
const [dbNodeSelector, setDBNodeSelector] = useState<IKeyValue[]>([{ key: "", value: "" }]);
const [labels, setLabels] = useState<IKeyValue[]>([{ key: "", value: "" }]);
const [annotations, setAnnotations] = useState<IKeyValue[]>([{ key: "", value: "" }]);
const [nodeSelector, setNodeSelector] = useState<IKeyValue[]>([{ key: "", value: "" }]);
const dispatch = useAppDispatch();
const { tenantName, tenantNamespace } = useParams();
const auditLoggingEnabled = useSelector(
(state: AppState) => state.editTenantLogging.auditLoggingEnabled
);
const image = useSelector(
(state: AppState) => state.editTenantLogging.image
);
const dbImage = useSelector(
(state: AppState) => state.editTenantLogging.dbImage
);
const dbInitImage = useSelector(
(state: AppState) => state.editTenantLogging.dbInitImage
);
const diskCapacityGB = useSelector(
(state: AppState) => state.editTenantLogging.diskCapacityGB
);
const cpuRequest = useSelector(
(state: AppState) => state.editTenantLogging.cpuRequest
);
const memRequest = useSelector(
(state: AppState) => state.editTenantLogging.memRequest
);
const dbCpuRequest = useSelector(
(state: AppState) => state.editTenantLogging.dbCPURequest
);
const dbMemRequest = useSelector(
(state: AppState) => state.editTenantLogging.dbMemRequest
);
const serviceAccountName = useSelector(
(state: AppState) => state.editTenantLogging.serviceAccountName
);
const dbServiceAccountName = useSelector(
(state: AppState) => state.editTenantLogging.dbServiceAccountName
);
const [validationErrors, setValidationErrors] = useState<any>({});
const [toggleConfirmOpen, setToggleConfirmOpen] = useState<boolean>(false);
const [labels, setLabels] = useState<IKeyValue[]>([{ key: "", value: "" }]);
const [annotations, setAnnotations] = useState<IKeyValue[]>([
{ key: "", value: "" },
]);
const [nodeSelector, setNodeSelector] = useState<IKeyValue[]>([
{ key: "", value: "" },
]);
const [dbLabels, setDBLabels] = useState<IKeyValue[]>([{ key: "", value: "" }]);
const [dbAnnotations, setDBAnnotations] = useState<IKeyValue[]>([
{ key: "", value: "" },
]);
const [dbNodeSelector, setDBNodeSelector] = useState<IKeyValue[]>([
{ key: "", value: "" },
]);
const [refreshLoggingInfo, setRefreshLoggingInfo] =
useState<boolean>(true);
const [labelsError, setLabelsError] = useState<any>({});
const [annotationsError, setAnnotationsError] = useState<any>({});
const [nodeSelectorError, setNodeSelectorError] = useState<any>({});
const [dbLabelsError, setDBLabelsError] = useState<any>({});
const [dbAnnotationsError, setDBAnnotationsError] = useState<any>({});
const [dbNodeSelectorError, setDBNodeSelectorError] = useState<any>({});
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
function a11yProps(index: any) {
return {
id: `simple-tab-${index}`,
"aria-controls": `simple-tabpanel-${index}`,
};
}
const setLoggingInfo = (res: ITenantLogsStruct) => {
dispatch(setAuditLoggingEnabled(!res.disabled))
dispatch(setImage(res.image));
dispatch(setServiceAccountName(res.serviceAccountName));
dispatch(setDBServiceAccountName(res.dbServiceAccountName));
dispatch(setDBImage(res.dbImage));
dispatch(setDBInitImage(res.dbInitImage));
dispatch(setCPURequest(res.logCPURequest));
dispatch(setDBCPURequest(res.logDBCPURequest));
if (res.logMemRequest) {
dispatch(
setMemRequest(
Math.floor(
parseInt(res.logMemRequest, 10) / 1000000000
).toString()
)
);
} else {
dispatch(setMemRequest("0"));
}
if (res !== null) {
dispatch(setAuditLoggingEnabled(res !== null && !res.disabled));
res.dbServiceAccountName != null && dispatch(setDBServiceAccountName(res.dbServiceAccountName));
res.dbImage != null && dispatch(setDBImage(res.dbImage));
res.dbInitImage != null && dispatch(setDBInitImage(res.dbInitImage));
res.logDBCPURequest != null && dispatch(setDBCPURequest(res.logDBCPURequest));
if (res.logDBMemRequest) {
dispatch(
setDBMemRequest(
Math.floor(
parseInt(res.logDBMemRequest, 10) / 1000000000
).toString()
)
);
dispatch(setDBMemRequest(Math.floor(parseInt(res.logDBMemRequest, 10)).toString()));
} else {
dispatch(setDBMemRequest("0"));
}
dispatch(setDiskCapacityGB(res.diskCapacityGB));
res.labels.length > 0 ? setLabels(res.labels) : setLabels([{ key: "test", value: "test" }]);
res.annotations.length > 0 ? setAnnotations(res.annotations) : setAnnotations([{ key: "", value: "" }]);
res.nodeSelector.length > 0 ? setNodeSelector(res.nodeSelector) : setNodeSelector([{ key: "", value: "" }]);
res.dbLabels.length > 0 ? setDBLabels(res.dbLabels) : setDBLabels([{ key: "", value: "" }]);
res.dbAnnotations.length > 0 ? setDBAnnotations(res.dbAnnotations) : setDBAnnotations([{ key: "", value: "" }]);
res.dbNodeSelector.length > 0 ? setDBNodeSelector(res.dbNodeSelector) : setDBNodeSelector([{ key: "", value: "" }]);
};
const trim = (x: IKeyValue[]): IKeyValue[] => {
let retval: IKeyValue[] = [];
for (let i = 0; i < x.length; i++) {
if (x[i].key !== "") {
retval.push(x[i]);
}
}
return retval;
};
const checkValid = (): boolean => {
if (
Object.keys(validationErrors).length !== 0 ||
Object.keys(labelsError).length !== 0 ||
Object.keys(annotationsError).length !== 0 ||
Object.keys(nodeSelectorError).length !== 0 ||
Object.keys(dbNodeSelectorError).length !== 0 ||
Object.keys(dbAnnotationsError).length !== 0 ||
Object.keys(dbLabelsError).length !== 0
) {
let err: ErrorResponseHandler = {
errorMessage: "Invalid entry",
detailedError: "",
};
dispatch(setErrorSnackMessage(err));
return false;
} else {
return true;
}
if (res.dbSecurityContext) {
dispatch(setDBRunAsGroup(res.dbSecurityContext.runAsGroup));
dispatch(setDBRunAsUser(res.dbSecurityContext.runAsUser));
dispatch(setDBFSGroup(res.dbSecurityContext.fsGroup));
dispatch(setDBRunAsNonRoot(res.dbSecurityContext.runAsNonRoot));
}
res.image != null && dispatch(setImage(res.image));
res.serviceAccountName != null && dispatch(setServiceAccountName(res.serviceAccountName));
res.logCPURequest != null && dispatch(setCPURequest(res.logCPURequest));
if (res.logMemRequest) {
dispatch(setMemRequest(Math.floor(parseInt(res.logMemRequest, 10)).toString()));
} else {
dispatch(setMemRequest("0"));
}
if (res.securityContext) {
dispatch(setRunAsGroup(res.securityContext.runAsGroup));
dispatch(setRunAsUser(res.securityContext.runAsUser));
dispatch(setFSGroup(res.securityContext.fsGroup));
dispatch(setRunAsNonRoot(res.securityContext.runAsNonRoot));
}
res.diskCapacityGB != null && dispatch(setDiskCapacityGB(res.diskCapacityGB));
res.labels != null ? setLabels(res.labels) : setLabels([{ key: "", value: "" }]);
res.annotations != null ? setAnnotations(res.annotations) : setAnnotations([{ key: "", value: "" }]);
res.nodeSelector != null ? setNodeSelector(res.nodeSelector) : setNodeSelector([{ key: "", value: "" }]);
res.dbLabels != null ? setDBLabels(res.dbLabels) : setDBLabels([{ key: "", value: "" }]);
res.dbAnnotations != null ? setDBAnnotations(res.dbAnnotations) : setDBAnnotations([{ key: "", value: "" }]);
res.dbNodeSelector != null ? setDBNodeSelector(res.dbNodeSelector) : setDBNodeSelector([{ key: "", value: "" }]);
setRefreshLoggingInfo(false);
}
};
useEffect(() => {
@@ -238,9 +148,11 @@ const TenantAuditLogging = ({ classes }: ITenantAuditLogs) => {
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/log`
)
.then((res: ITenantLogsStruct) => {
if (res !== null) {
dispatch(setAuditLoggingEnabled(res.auditLoggingEnabled));
setLoggingInfo(res);
setRefreshLoggingInfo(false);
}
})
.catch((err: ErrorResponseHandler) => {
dispatch(setErrorSnackMessage(err));
@@ -250,42 +162,14 @@ const TenantAuditLogging = ({ classes }: ITenantAuditLogs) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [refreshLoggingInfo]);
const submitLoggingInfo = () => {
if (checkValid()) {
api
.invoke(
"PUT",
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/log`,
{
labels: trim(labels),
annotations: trim(annotations),
nodeSelector: trim(nodeSelector),
image: image,
diskCapacityGB: diskCapacityGB.toString(),
serviceAccountName: serviceAccountName,
dbLabels: trim(dbLabels),
dbAnnotations: trim(dbAnnotations),
dbNodeSelector: trim(dbNodeSelector),
dbImage: dbImage,
dbInitImage: dbInitImage,
dbServiceAccountName: dbServiceAccountName,
logCPURequest: cpuRequest,
logMemRequest: memRequest + "Gi",
logDBCPURequest: dbCpuRequest,
logDBMemRequest: dbMemRequest + "Gi",
}
)
.then(() => {
setRefreshLoggingInfo(true);
dispatch(setSnackBarMessage(`Audit Log configuration updated.`));
})
.catch((err: ErrorResponseHandler) => {
setErrorSnackMessage(err);
});
useEffect(() => {
if (loading) {
setLoading(false);
}
};
}, [loading, refreshLoggingInfo]);
const toggleLogging = () => {
dispatch(resetAuditLogForm());
if(!auditLoggingEnabled) {
api
.invoke(
@@ -295,6 +179,7 @@ const TenantAuditLogging = ({ classes }: ITenantAuditLogs) => {
.then(() => {
setRefreshLoggingInfo(true);
setToggleConfirmOpen(false);
setAuditLoggingEnabled(true);
})
.catch((err: ErrorResponseHandler) => {
dispatch(
@@ -311,8 +196,10 @@ const TenantAuditLogging = ({ classes }: ITenantAuditLogs) => {
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/disable-logging`
)
.then(() => {
setAuditLoggingEnabled(false);
setRefreshLoggingInfo(true);
setToggleConfirmOpen(false);
dispatch(resetAuditLogForm());
})
.catch((err: ErrorResponseHandler) => {
dispatch(
@@ -324,9 +211,13 @@ const TenantAuditLogging = ({ classes }: ITenantAuditLogs) => {
});
};
};
return (
<Fragment>
{toggleConfirmOpen && (
<Fragment>
<Grid item xs>
{toggleConfirmOpen && (
<ConfirmDialog
isOpen={toggleConfirmOpen}
title={
@@ -347,11 +238,12 @@ const TenantAuditLogging = ({ classes }: ITenantAuditLogs) => {
}
/>
)}
<Grid container spacing={1}>
<Grid item xs>
</Grid>
<Grid container >
<Grid item xs>
<h1 className={classes.sectionTitle}>Audit Logs</h1>
</Grid>
<Grid item xs={7} justifyContent={"end"} textAlign={"right"}>
<Grid >
<FormSwitchWrapper
label={""}
indicatorLabels={["Enabled", "Disabled"]}
@@ -365,291 +257,50 @@ const TenantAuditLogging = ({ classes }: ITenantAuditLogs) => {
description=""
/>
</Grid>
<Grid xs={12}>
<hr className={classes.hrClass} />
</Grid>
<Grid container>
{auditLoggingEnabled && (
<Fragment>
<Grid item xs={9}>
<Tabs
value={curTab}
onChange={(e: React.ChangeEvent<{}>, newValue: number) => {
setCurTab(newValue);
}}
indicatorColor="primary"
textColor="primary"
aria-label="cluster-tabs"
variant="scrollable"
scrollButtons="auto"
>
<Tab label="Configuration" {...a11yProps(0)} />
<Tab label="DB Configuration" {...a11yProps(1)} />
</Tabs>
</Grid>
</Grid>
{auditLoggingEnabled && (
<Fragment>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`image`}
label={"Image"}
placeholder={"minio/operator:v4.4.22"}
name={`image`}
value={image}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setImage(event.target.value));
}
cleanValidation(`image`);
}}
key={`image`}
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
error={validationErrors[`image`] || ""}
<Grid item xs={12}>
<hr className={classes.hrClass} />
</Grid>
{curTab === 0 && (
<LoggingDetails
classes={classes}
labels={labels}
annotations={annotations}
nodeSelector={nodeSelector}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbImage`}
label={"DB Postgres Image"}
placeholder={"library/postgres:13"}
name={`dbImage`}
value={dbImage}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBImage(event.target.value));
}
cleanValidation(`dbImage`);
}}
key={`dbImage`}
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
error={validationErrors[`dbImage`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbInitImage`}
label={"DB Init Image"}
placeholder={"library/busybox:1.33.1"}
name={`dbInitImage`}
value={dbInitImage}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBInitImage(event.target.value));
}
cleanValidation(`dbInitImage`);
}}
key={`dbInitImage`}
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
error={validationErrors[`dbInitImage`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`diskCapacityGB`}
label={"Disk Capacity"}
placeholder={"Disk Capacity"}
name={`diskCapacityGB`}
value={!isNaN(diskCapacityGB) ? diskCapacityGB.toString() : "0"}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDiskCapacityGB(parseInt(event.target.value)));
}
cleanValidation(`diskCapacityGB`);
}}
key={`diskCapacityGB`}
pattern={"[0-9]*"}
error={validationErrors[`diskCapacityGB`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`cpuRequest`}
label={"CPU Request"}
placeholder={"CPU Request"}
name={`cpuRequest`}
value={cpuRequest}
pattern={"[0-9]*"}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setCPURequest(event.target.value));
}
cleanValidation(`cpuRequest`);
}}
key={`cpuRequest`}
error={validationErrors[`cpuRequest`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbCPURequest`}
label={"DB CPU Request"}
placeholder={"DB CPU Request"}
name={`dbCPURequest`}
value={dbCpuRequest}
pattern={"[0-9]*"}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBCPURequest(event.target.value));
}
cleanValidation(`dbCPURequest`);
}}
key={`dbCPURequest`}
error={validationErrors[`dbCPURequest`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`memRequest`}
label={"Memory Request"}
placeholder={"Memory request"}
name={`memRequest`}
value={memRequest}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setMemRequest(event.target.value));
}
cleanValidation(`memRequest`);
}}
pattern={"[0-9]*"}
key={`memRequest`}
error={validationErrors[`memRequest`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbMemRequest`}
label={"DB Memory Request"}
placeholder={"DB Memory request"}
name={`dbMemRequest`}
value={dbMemRequest}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBMemRequest(event.target.value));
}
cleanValidation(`dbMemRequest`);
}}
pattern={"[0-9]*"}
key={`dbMemRequest`}
error={validationErrors[`dbMemRequest`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`serviceAccountName`}
label={"Service Account"}
placeholder={"Service Account Name"}
name={`serviceAccountName`}
value={serviceAccountName}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setServiceAccountName(event.target.value));
}
cleanValidation(`serviceAccountName`);
}}
key={`serviceAccountName`}
pattern={"^[a-zA-Z0-9-.]{1,253}$"}
error={validationErrors[`serviceAccountName`] || ""}
/>
</Grid>
{labels !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Labels</span>
<KeyPairEdit
newValues={labels}
setNewValues={setLabels}
paramName={"Labels"}
error={labelsError}
setError={setLabelsError}
/>
</Grid>
)}
{annotations !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Annotations</span>
<KeyPairEdit
newValues={annotations}
setNewValues={setAnnotations}
paramName={"Annotations"}
error={annotationsError}
setError={setAnnotationsError}
/>
</Grid>
)}
{nodeSelector !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Node Selector</span>
<KeyPairEdit
newValues={nodeSelector}
setNewValues={setNodeSelector}
paramName={"Node Selector"}
error={nodeSelectorError}
setError={setNodeSelectorError}
/>
</Grid>
)}
{dbLabels !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>DB Labels</span>
<KeyPairEdit
newValues={dbLabels}
setNewValues={setDBLabels}
paramName={"dbLabels"}
error={dbLabelsError}
setError={setDBLabelsError}
/>
</Grid>
)}
{dbAnnotations !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>DB Annotations</span>
<KeyPairEdit
newValues={dbAnnotations}
setNewValues={setDBAnnotations}
paramName={"dbAnnotations"}
error={dbAnnotationsError}
setError={setDBAnnotationsError}
/>
</Grid>
)}
{dbNodeSelector !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>DB Node Selector</span>
<KeyPairEdit
newValues={dbNodeSelector}
setNewValues={setDBNodeSelector}
paramName={"DB Node Selector"}
error={dbNodeSelectorError}
setError={setDBNodeSelectorError}
/>
</Grid>
)}
<Grid item xs={12} textAlign={"right"}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={!checkValid()}
onClick={() => submitLoggingInfo()}
>
Save
</Button>
</Grid>
)}
{curTab === 1 && (
<LoggingDBDetails classes={classes}
labels={dbLabels}
annotations={dbAnnotations}
nodeSelector={dbNodeSelector}
/>
)}
</Fragment>
)}
)}
</Grid>
</Fragment>
);
};
export default withStyles(styles)(TenantAuditLogging);
export default withStyles(styles)(LoggingScreen);

View File

@@ -15,6 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { IKeyValue } from "../ListTenants/types";
import { ISecurityContext } from "../types";
export interface IEditTenantAuditLogging {
auditLoggingEnabled: boolean;
@@ -34,6 +35,9 @@ export interface IEditTenantAuditLogging {
memRequest: string;
dbCPURequest: string;
dbMemRequest: string;
securityContext: ISecurityContext;
dbSecurityContext: ISecurityContext;
refreshLoggingInfo: boolean;
}
const initialState: IEditTenantAuditLogging = {
@@ -54,6 +58,19 @@ const initialState: IEditTenantAuditLogging = {
dbServiceAccountName: "",
cpuRequest: "",
memRequest: "",
securityContext: {
runAsUser: "1000",
runAsGroup: "1000",
fsGroup: "1000",
runAsNonRoot: false,
},
dbSecurityContext: {
runAsUser: "1000",
runAsGroup: "1000",
fsGroup: "1000",
runAsNonRoot: false,
},
refreshLoggingInfo: true
};
export const editTenantAuditLoggingSlice = createSlice({
@@ -111,6 +128,34 @@ export const editTenantAuditLoggingSlice = createSlice({
setDBMemRequest: (state, action: PayloadAction<string>) => {
state.dbMemRequest = action.payload;
},
setRunAsUser: (state, action: PayloadAction<string>) => {
state.securityContext.runAsUser = action.payload;
},
setRunAsGroup: (state, action: PayloadAction<string>) => {
state.securityContext.runAsGroup = action.payload;
},
setFSGroup: (state, action: PayloadAction<string>) => {
state.securityContext.fsGroup = action.payload;
},
setRunAsNonRoot: (state, action: PayloadAction<boolean>) => {
state.securityContext.runAsNonRoot = action.payload;
},
setDBRunAsUser: (state, action: PayloadAction<string>) => {
state.dbSecurityContext.runAsUser = action.payload;
},
setDBRunAsGroup: (state, action: PayloadAction<string>) => {
state.dbSecurityContext.runAsGroup = action.payload;
},
setDBFSGroup: (state, action: PayloadAction<string>) => {
state.dbSecurityContext.fsGroup = action.payload;
},
setDBRunAsNonRoot: (state, action: PayloadAction<boolean>) => {
state.dbSecurityContext.runAsNonRoot = action.payload;
},
setRefreshLoggingInfo: (state, action: PayloadAction<boolean>) => {
state.refreshLoggingInfo = action.payload;
},
resetAuditLogForm: () => initialState,
},
});
@@ -132,6 +177,16 @@ export const {
setMemRequest,
setDBCPURequest,
setDBMemRequest,
setRunAsUser,
setFSGroup,
setRunAsGroup,
setRunAsNonRoot,
setDBRunAsUser,
setDBFSGroup,
setDBRunAsGroup,
setDBRunAsNonRoot,
setRefreshLoggingInfo,
resetAuditLogForm,
} = editTenantAuditLoggingSlice.actions;
export default editTenantAuditLoggingSlice.reducer;

View File

@@ -0,0 +1,110 @@
// 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 <http://www.gnu.org/licenses/>.
import React, { Fragment } from "react";
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import { Grid } from "@mui/material";
import { useDispatch } from "react-redux";
interface IEditSecurityContextProps {
classes: any;
runAsUser: string;
runAsGroup: string;
fsGroup: string;
runAsNonRoot: boolean;
setRunAsUser: any;
setRunAsGroup: any;
setFSGroup: any;
setRunAsNonRoot: any;
}
const SecurityContextSelector = ({ classes, runAsGroup, runAsUser, fsGroup, runAsNonRoot, setRunAsUser, setRunAsGroup, setFSGroup, setRunAsNonRoot }: IEditSecurityContextProps ) => {
const dispatch = useDispatch();
return (
<Fragment>
<fieldset
className={`${classes.fieldGroup} ${classes.fieldSpaceTop} `}
>
<legend className={classes.descriptionText}>
Security Context
</legend>
<Grid item xs={12}>
<div
className={`${classes.multiContainerStackNarrow} `}
>
<div className={classes.configSectionItem}>
<InputBoxWrapper
type="number"
id="securityContext_runAsUser"
name="securityContext_runAsUser"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
dispatch(setRunAsUser(e.target.value));
}}
label="Run As User"
value={runAsUser}
required
min="0"
/>
</div>
<div className={classes.configSectionItem}>
<InputBoxWrapper
type="number"
id="securityContext_runAsGroup"
name="securityContext_runAsGroup"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => { dispatch(setRunAsGroup(e.target.value));
}}
label="Run As Group"
value={runAsGroup}
required
min="0"
/>
</div>
<div className={classes.configSectionItem}>
<InputBoxWrapper
type="number"
id="securityContext_fsGroup"
name="securityContext_fsGroup"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {dispatch(setFSGroup(e.target.value)); }}
label="FsGroup"
value={fsGroup}
required
min="0"
/>
</div>
</div>
</Grid>
<br />
<Grid item xs={12}>
<div className={classes.multiContainer} >
<FormSwitchWrapper
value="prometheusSecurityContextRunAsNonRoot"
id="prometheus_securityContext_runAsNonRoot"
name="prometheus_securityContext_runAsNonRoot"
checked={runAsNonRoot}
onChange={() => {
dispatch(setRunAsNonRoot(!runAsNonRoot));
}
}
label={"Do not run as Root"}
/>
</div>
</Grid>
</fieldset>
</Fragment>
);
};
export default SecurityContextSelector;

View File

@@ -338,3 +338,4 @@ export interface ITenantIdentityProviderResponse {
user_dn_search_filter: string;
};
}

View File

@@ -34,6 +34,7 @@ import editPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/EditP
import editTenantMonitoringReducer from "./screens/Console/Tenants/TenantDetails/tenantMonitoringSlice";
import editTenantAuditLoggingReducer from "./screens/Console/Tenants/TenantDetails/tenantAuditLogSlice";
const rootReducer = combineReducers({
system: systemReducer,
trace: traceReducer,

View File

@@ -2908,6 +2908,12 @@ definitions:
type: string
logDBMemRequest:
type: string
securityContext:
type: object
$ref: "#/definitions/securityContext"
dbSecurityContext:
type: object
$ref: "#/definitions/securityContext"
listPVCsResponse: