Added Color customization to embedded object browser (#2246)

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2022-08-17 11:06:10 -05:00
committed by GitHub
parent 697910c7b2
commit 7036d1328e
24 changed files with 512 additions and 142 deletions

View File

@@ -46,6 +46,9 @@ type Principal struct {
// account access key
AccountAccessKey string `json:"accountAccessKey,omitempty"`
// custom style ob
CustomStyleOb string `json:"customStyleOb,omitempty"`
// hm
Hm bool `json:"hm,omitempty"`

View File

@@ -41,6 +41,9 @@ type SessionResponse struct {
// allow resources
AllowResources []*PermissionResource `json:"allowResources"`
// custom styles
CustomStyles string `json:"customStyles,omitempty"`
// distributed mode
DistributedMode bool `json:"distributedMode,omitempty"`

View File

@@ -68,6 +68,7 @@ type TokenClaims struct {
AccountAccessKey string `json:"accountAccessKey,omitempty"`
HideMenu bool `json:"hm,omitempty"`
ObjectBrowser bool `json:"ob,omitempty"`
CustomStyleOB string `json:"customStyleOb,omitempty"`
}
// STSClaims claims struct for STS Token
@@ -79,6 +80,7 @@ type STSClaims struct {
type SessionFeatures struct {
HideMenu bool
ObjectBrowser bool
CustomStyleOB string
}
// SessionTokenAuthenticate takes a session token, decode it, extract claims and validate the signature
@@ -123,7 +125,9 @@ func NewEncryptedTokenForClient(credentials *credentials.Value, accountAccessKey
if features != nil {
tokenClaims.HideMenu = features.HideMenu
tokenClaims.ObjectBrowser = features.ObjectBrowser
tokenClaims.CustomStyleOB = features.CustomStyleOB
}
encryptedClaims, err := encryptClaims(tokenClaims)
if err != nil {
return "", err

View File

@@ -45,6 +45,7 @@
"react-window-infinite-loader": "^1.0.7",
"recharts": "^2.1.1",
"superagent": "^6.1.0",
"tinycolor2": "^1.4.2",
"websocket": "^1.0.31"
},
"scripts": {

View File

@@ -27,12 +27,14 @@ import {
globalSetDistributedSetup,
operatorMode,
selOpMode,
setOverrideStyles,
setSiteReplicationInfo,
userLogged,
} from "./systemSlice";
import { SRInfoStateType } from "./types";
import { AppState, useAppDispatch } from "./store";
import { saveSessionResponse } from "./screens/Console/consoleSlice";
import { getOverrideColorVariants } from "./utils/stylesUtils";
interface ProtectedRouteProps {
Component: any;
@@ -67,6 +69,16 @@ const ProtectedRoute = ({ Component }: ProtectedRouteProps) => {
dispatch(directPVMode(!!res.directPV));
document.title = "MinIO Operator";
}
if (res.customStyles && res.customStyles !== "") {
const overrideColorVariants = getOverrideColorVariants(
res.customStyles
);
if (overrideColorVariants !== false) {
dispatch(setOverrideStyles(overrideColorVariants));
}
}
})
.catch(() => setSessionLoading(false));
}, [dispatch]);

View File

@@ -0,0 +1,168 @@
// 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 React, { Fragment } from "react";
import {
StyledEngineProvider,
Theme,
ThemeProvider,
} from "@mui/material/styles";
import withStyles from "@mui/styles/withStyles";
import theme from "./theme/main";
import "react-virtualized/styles.css";
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";
import { generateOverrideTheme } from "./utils/stylesUtils";
import "./index.css";
import { useSelector } from "react-redux";
import { AppState } from "./store";
declare module "@mui/styles/defaultTheme" {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface DefaultTheme extends Theme {}
}
interface IStyleHandler {
children: React.ReactNode;
}
const StyleHandler = ({ children }: IStyleHandler) => {
const colorVariants = useSelector(
(state: AppState) => state.system.overrideStyles
);
let thm = theme;
let globalBody: any = {};
let rowColor: any = { color: "#393939" };
let detailsListPanel: any = { backgroundColor: "#fff" };
if (colorVariants) {
thm = generateOverrideTheme(colorVariants);
globalBody = { backgroundColor: colorVariants.backgroundColor };
rowColor = { color: colorVariants.fontColor };
detailsListPanel = {
backgroundColor: colorVariants.backgroundColor,
color: colorVariants.fontColor,
};
}
const GlobalCss = withStyles({
// @global is handled by jss-plugin-global.
"@global": {
body: {
height: "100vh",
width: "100vw",
fontFamily: "Lato, sans-serif",
...globalBody,
},
"#root": {
height: "100%",
width: "100%",
display: "flex",
flexFlow: "column",
alignItems: "stretch",
},
".min-icon": {
width: 26,
},
".MuiButton-endIcon": {
"& .min-icon": {
width: 16,
},
},
// You should target [class*="MuiButton-root"] instead if you nest themes.
".MuiButton-root:not(.noDefaultHeight)": {
height: 38,
},
".MuiButton-contained": {
fontSize: "14px",
textTransform: "capitalize",
padding: "15px 25px 15px 25px",
borderRadius: 3,
},
".MuiButton-sizeSmall": {
padding: "4px 10px",
fontSize: "0.8125rem",
},
".MuiTableCell-head": {
borderRadius: "3px 3px 0px 0px",
fontSize: 13,
},
".MuiPaper-root": {
borderRadius: 3,
},
".MuiDrawer-paperAnchorDockedLeft": {
borderRight: 0,
},
".MuiDrawer-root": {
"& .MuiPaper-root": {
borderRadius: 0,
},
},
".rowLine": {
...rowColor,
},
".detailsListPanel": {
...detailsListPanel,
},
hr: {
borderTop: 0,
borderLeft: 0,
borderRight: 0,
borderColor: "#999999",
backgroundColor: "transparent" as const,
},
ul: {
paddingLeft: 20,
listStyle: "none" /* Remove default bullets */,
"& li::before:not(.Mui*)": {
content: '"■"',
color: "#2781B0",
fontSize: 20,
display:
"inline-block" /* Needed to add space between the bullet and the text */,
width: "1em" /* Also needed for space (tweak if needed) */,
marginLeft: "-1em" /* Also needed for space (tweak if needed) */,
},
"& ul": {
listStyle: "none" /* Remove default bullets */,
"& li::before:not(.Mui*)": {
content: '"○"',
color: "#2781B0",
fontSize: 20,
display:
"inline-block" /* Needed to add space between the bullet and the text */,
width: "1em" /* Also needed for space (tweak if needed) */,
marginLeft: "-1em" /* Also needed for space (tweak if needed) */,
},
},
},
},
})(() => null);
return (
<Fragment>
<GlobalCss />
<StyledEngineProvider injectFirst>
<ThemeProvider theme={thm}>{children}</ThemeProvider>
</StyledEngineProvider>
</Fragment>
);
};
export default StyleHandler;

View File

@@ -462,3 +462,18 @@ export interface IBytesCalc {
total: number;
unit: string;
}
export interface IEmbeddedCustomButton {
backgroundColor?: string;
textColor?: string;
hoverColor?: string;
hoverText?: string;
activeColor?: string;
activeText?: string;
}
export interface IEmbeddedCustomStyles {
backgroundColor: string;
fontColor: string;
buttonStyles: IEmbeddedCustomButton;
}

View File

@@ -15,117 +15,12 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import * as serviceWorker from "./serviceWorker";
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import { store } from "./store";
import * as serviceWorker from "./serviceWorker";
import {
StyledEngineProvider,
Theme,
ThemeProvider,
} from "@mui/material/styles";
import withStyles from "@mui/styles/withStyles";
import "react-virtualized/styles.css";
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";
import "./index.css";
import theme from "./theme/main";
import MainRouter from "./MainRouter";
declare module "@mui/styles/defaultTheme" {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface DefaultTheme extends Theme {}
}
const GlobalCss = withStyles({
// @global is handled by jss-plugin-global.
"@global": {
body: {
height: "100vh",
width: "100vw",
fontFamily: "Lato, sans-serif",
},
"#root": {
height: "100%",
width: "100%",
display: "flex",
flexFlow: "column",
alignItems: "stretch",
},
".min-icon": {
// height: 26,
width: 26,
},
".MuiButton-endIcon": {
"& .min-icon": {
// height: 26,
width: 16,
},
},
// You should target [class*="MuiButton-root"] instead if you nest themes.
".MuiButton-root:not(.noDefaultHeight)": {
height: 38,
},
".MuiButton-contained": {
fontSize: "14px",
textTransform: "capitalize",
padding: "15px 25px 15px 25px",
borderRadius: 3,
},
".MuiButton-sizeSmall": {
padding: "4px 10px",
fontSize: "0.8125rem",
},
".MuiTableCell-head": {
borderRadius: "3px 3px 0px 0px",
fontSize: 13,
},
".MuiPaper-root": {
borderRadius: 3,
},
".MuiDrawer-paperAnchorDockedLeft": {
borderRight: 0,
},
".MuiDrawer-root": {
"& .MuiPaper-root": {
borderRadius: 0,
},
},
hr: {
borderTop: 0,
borderLeft: 0,
borderRight: 0,
borderColor: "#999999",
backgroundColor: "transparent" as const,
},
ul: {
paddingLeft: 20,
listStyle: "none" /* Remove default bullets */,
"& li::before:not(.Mui*)": {
content: '"■"',
color: "#2781B0",
fontSize: 20,
display:
"inline-block" /* Needed to add space between the bullet and the text */,
width: "1em" /* Also needed for space (tweak if needed) */,
marginLeft: "-1em" /* Also needed for space (tweak if needed) */,
},
"& ul": {
listStyle: "none" /* Remove default bullets */,
"& li::before:not(.Mui*)": {
content: '"○"',
color: "#2781B0",
fontSize: 20,
display:
"inline-block" /* Needed to add space between the bullet and the text */,
width: "1em" /* Also needed for space (tweak if needed) */,
marginLeft: "-1em" /* Also needed for space (tweak if needed) */,
},
},
},
},
})(() => null);
import StyleHandler from "./StyleHandler";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
@@ -134,12 +29,9 @@ const root = ReactDOM.createRoot(
root.render(
<React.StrictMode>
<Provider store={store}>
<GlobalCss />
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<MainRouter />
</ThemeProvider>
</StyledEngineProvider>
<StyleHandler>
<MainRouter />
</StyleHandler>
</Provider>
</React.StrictMode>
);

View File

@@ -43,7 +43,7 @@ import {
} from "../../ObjectBrowser/objectBrowserSlice";
import SearchBox from "../../Common/SearchBox";
import { selFeatures } from "../../consoleSlice";
import { LoginMinIOLogo } from "../../../../icons";
import AutoColorIcon from "../../Common/Components/AutoColorIcon";
const styles = (theme: Theme) =>
createStyles({
@@ -150,14 +150,7 @@ const BrowserHandler = () => {
}}
>
<Grid>
<LoginMinIOLogo
style={{
width: 105,
marginRight: 30,
marginTop: 10,
fill: "#081C42",
}}
/>
<AutoColorIcon marginRight={30} marginTop={10} />
</Grid>
<Grid item xs>
{searchBar}

View File

@@ -27,7 +27,6 @@ import {
AddIcon,
BucketsIcon,
LifecycleConfigIcon,
LoginMinIOLogo,
SelectAllIcon,
} from "../../../../icons";
import {
@@ -60,6 +59,7 @@ import { setErrorSnackMessage } from "../../../../systemSlice";
import { useAppDispatch } from "../../../../store";
import { useSelector } from "react-redux";
import { selFeatures } from "../../consoleSlice";
import AutoColorIcon from "../../Common/Components/AutoColorIcon";
const styles = (theme: Theme) =>
createStyles({
@@ -223,14 +223,7 @@ const ListBuckets = ({ classes }: IListBucketsProps) => {
<Grid item xs={12} className={classes.actionsTray} display="flex">
{obOnly && (
<Grid item xs>
<LoginMinIOLogo
style={{
width: 105,
marginRight: 15,
marginTop: 10,
fill: "#081C42",
}}
/>
<AutoColorIcon marginRight={15} marginTop={10} />
</Grid>
)}
<SearchBox

View File

@@ -17,23 +17,21 @@
import React from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Grid, IconButton } from "@mui/material";
import { ClosePanelIcon } from "../../../../../../icons";
import makeStyles from "@mui/styles/makeStyles";
interface IDetailsListPanel {
classes: any;
open: boolean;
className?: string;
closePanel: () => void;
children: React.ReactNode;
}
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
detailsList: {
borderColor: "#EAEDEE",
backgroundColor: "#fff",
borderWidth: 0,
borderStyle: "solid",
borderRadius: 3,
@@ -68,19 +66,23 @@ const styles = (theme: Theme) =>
width: 14,
},
},
});
})
);
const DetailsListPanel = ({
classes,
open,
closePanel,
className = "",
children,
}: IDetailsListPanel) => {
const classes = useStyles();
return (
<Grid
item
className={`${classes.detailsList} ${open ? "open" : ""} ${className}`}
className={`${classes.detailsList} ${
open ? "open" : ""
} ${className} detailsListPanel`}
>
<IconButton onClick={closePanel} className={classes.closePanel}>
<ClosePanelIcon />
@@ -90,4 +92,4 @@ const DetailsListPanel = ({
);
};
export default withStyles(styles)(DetailsListPanel);
export default DetailsListPanel;

View File

@@ -0,0 +1,49 @@
// 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 from "react";
import { LoginMinIOLogo } from "../../../../icons";
import { useSelector } from "react-redux";
import { AppState } from "../../../../store";
interface IAutoColorIcon {
marginRight: number;
marginTop: number;
}
const AutoColorIcon = ({ marginRight, marginTop }: IAutoColorIcon) => {
let tinycolor = require("tinycolor2");
const colorVariants = useSelector(
(state: AppState) => state.system.overrideStyles
);
const isDark =
tinycolor(colorVariants?.backgroundColor || "#fff").getBrightness() <= 128;
return (
<LoginMinIOLogo
style={{
width: 105,
marginRight,
marginTop,
fill: isDark ? "#fff" : "#081C42",
}}
/>
);
};
export default AutoColorIcon;

View File

@@ -172,7 +172,6 @@ const styles = () =>
".rowLine": {
borderBottom: `1px solid ${borderColor}`,
height: 40,
color: "#393939",
fontSize: 14,
transitionDuration: 0.3,
"&:focus": {

View File

@@ -30,6 +30,7 @@ const initialState: ConsoleState = {
distributedMode: false,
permissions: {},
allowResources: null,
customStyles: null,
},
};

View File

@@ -32,4 +32,5 @@ export interface ISessionResponse {
distributedMode: boolean;
permissions: ISessionPermissions;
allowResources: IAllowResources[] | null;
customStyles?: string | null;
}

View File

@@ -15,7 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { snackBarMessage, SRInfoStateType } from "./types";
import { ErrorResponseHandler } from "./common/types";
import { ErrorResponseHandler, IEmbeddedCustomStyles } from "./common/types";
import { AppState } from "./store";
import { SubnetInfo } from "./screens/Console/License/types";
@@ -42,6 +42,7 @@ export interface SystemState {
distributedSetup: boolean;
siteReplicationInfo: SRInfoStateType;
licenseInfo: null | SubnetInfo;
overrideStyles: null | IEmbeddedCustomStyles;
}
const initialState: SystemState = {
@@ -70,6 +71,7 @@ const initialState: SystemState = {
serverDiagnosticStatus: "",
distributedSetup: false,
licenseInfo: null,
overrideStyles: null,
};
export const systemSlice = createSlice({
@@ -151,6 +153,12 @@ export const systemSlice = createSlice({
setLicenseInfo: (state, action: PayloadAction<SubnetInfo | null>) => {
state.licenseInfo = action.payload;
},
setOverrideStyles: (
state,
action: PayloadAction<IEmbeddedCustomStyles>
) => {
state.overrideStyles = action.payload;
},
},
});
@@ -172,6 +180,7 @@ export const {
globalSetDistributedSetup,
setSiteReplicationInfo,
setLicenseInfo,
setOverrideStyles,
} = systemSlice.actions;
export const selDistSet = (state: AppState) => state.system.distributedSetup;

View File

@@ -0,0 +1,152 @@
// 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 { IEmbeddedCustomStyles } from "../common/types";
import { createTheme } from "@mui/material";
export const getOverrideColorVariants: (
customStyles: string
) => false | IEmbeddedCustomStyles = (customStyles) => {
try {
return JSON.parse(atob(customStyles)) as IEmbeddedCustomStyles;
} catch (e) {
console.error("Error processing override styles, skipping.", e);
return false;
}
};
export const generateOverrideTheme = (overrideVars: IEmbeddedCustomStyles) => {
const theme = createTheme({
palette: {
primary: {
light: overrideVars.buttonStyles.hoverColor || "#073052",
main: overrideVars.buttonStyles.backgroundColor || "#081C42",
dark: overrideVars.buttonStyles.activeColor || "#05122B",
contrastText: overrideVars.buttonStyles.textColor || "#fff",
},
secondary: {
light: "#ff7961",
main: "#f44336",
dark: "#ba000d",
contrastText: "#000",
},
background: {
default: overrideVars.backgroundColor,
},
success: {
main: "#4ccb92",
},
warning: {
main: "#FFBD62",
},
error: {
light: "#e03a48",
main: "#C83B51",
contrastText: "#fff",
},
},
typography: {
fontFamily: ["Lato", "sans-serif"].join(","),
h1: {
fontWeight: "bold",
color: overrideVars.fontColor,
},
h2: {
fontWeight: "bold",
color: overrideVars.fontColor,
},
h3: {
fontWeight: "bold",
color: overrideVars.fontColor,
},
h4: {
fontWeight: "bold",
color: overrideVars.fontColor,
},
h5: {
fontWeight: "bold",
color: overrideVars.fontColor,
},
h6: {
fontWeight: "bold",
color: overrideVars.fontColor,
},
},
components: {
MuiButton: {
styleOverrides: {
root: {
textTransform: "none",
borderRadius: 3,
height: 40,
padding: "0 20px",
fontSize: 14,
fontWeight: 600,
boxShadow: "none",
"& .min-icon": {
maxHeight: 18,
},
"&.MuiButton-contained.Mui-disabled": {
backgroundColor: "#EAEDEE",
fontWeight: 600,
color: "#767676",
},
"& .MuiButton-iconSizeMedium > *:first-of-type": {
fontSize: 12,
},
},
},
},
MuiPaper: {
styleOverrides: {
root: {
backgroundColor: overrideVars.backgroundColor,
color: overrideVars.fontColor,
},
elevation1: {
boxShadow: "none",
border: "#EAEDEE 1px solid",
borderRadius: 3,
},
},
},
MuiListItem: {
styleOverrides: {
root: {
"&.MuiListItem-root.Mui-selected": {
background: "inherit",
"& .MuiTypography-root": {
fontWeight: "bold",
},
},
},
},
},
MuiTab: {
styleOverrides: {
root: {
textTransform: "none",
},
},
},
},
colors: {
link: "#2781B0",
},
});
return theme;
};

View File

@@ -11038,6 +11038,11 @@ tiny-warning@^1.0.2, tiny-warning@^1.0.3:
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
tinycolor2@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==
tmp-promise@^1.0.5:
version "1.1.0"
resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-1.1.0.tgz#bb924d239029157b9bc1d506a6aa341f8b13e64c"

View File

@@ -95,6 +95,7 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
AccountAccessKey: claims.AccountAccessKey,
Hm: claims.HideMenu,
Ob: claims.ObjectBrowser,
CustomStyleOb: claims.CustomStyleOB,
}, nil
}
@@ -355,6 +356,8 @@ func handleSPA(w http.ResponseWriter, r *http.Request) {
sts := r.URL.Query().Get("sts")
stsAccessKey := r.URL.Query().Get("sts_a")
stsSecretKey := r.URL.Query().Get("sts_s")
overridenStyles := r.URL.Query().Get("ov_st")
// if these three parameters are present we are being asked to issue a session with these values
if sts != "" && stsAccessKey != "" && stsSecretKey != "" {
creds := credentials.NewStaticV4(stsAccessKey, stsSecretKey, sts)
@@ -366,6 +369,14 @@ func handleSPA(w http.ResponseWriter, r *http.Request) {
sf.HideMenu = true
sf.ObjectBrowser = true
err := ValidateEncodedStyles(overridenStyles)
if err != nil {
log.Println(err)
} else {
sf.CustomStyleOB = overridenStyles
}
sessionID, err := login(consoleCreds, sf)
if err != nil {
log.Println(err)

View File

@@ -51,7 +51,6 @@ const (
PrometheusExtraLabels = "CONSOLE_PROMETHEUS_EXTRA_LABELS"
ConsoleLogQueryURL = "CONSOLE_LOG_QUERY_URL"
ConsoleLogQueryAuthToken = "CONSOLE_LOG_QUERY_AUTH_TOKEN"
ConsoleObjectBrowserOnly = "CONSOLE_OBJECT_BROWSER_ONLY"
LogSearchQueryAuthToken = "LOGSEARCH_QUERY_AUTH_TOKEN"
SlashSeparator = "/"
)

View File

@@ -6006,6 +6006,9 @@ func init() {
"accountAccessKey": {
"type": "string"
},
"customStyleOb": {
"type": "string"
},
"hm": {
"type": "boolean"
},
@@ -6318,6 +6321,9 @@ func init() {
"$ref": "#/definitions/permissionResource"
}
},
"customStyles": {
"type": "string"
},
"distributedMode": {
"type": "boolean"
},
@@ -13266,6 +13272,9 @@ func init() {
"accountAccessKey": {
"type": "string"
},
"customStyleOb": {
"type": "string"
},
"hm": {
"type": "boolean"
},
@@ -13578,6 +13587,9 @@ func init() {
"$ref": "#/definitions/permissionResource"
}
},
"customStyles": {
"type": "string"
},
"distributedMode": {
"type": "boolean"
},

View File

@@ -103,6 +103,7 @@ func getSessionResponse(ctx context.Context, session *models.Principal) (*models
}
currTime := time.Now().UTC()
customStyles := session.CustomStyleOb
// This actions will be global, meaning has to be attached to all resources
conditionValues := map[string][]string{
condition.AWSUsername.Name(): {session.AccountAccessKey},
@@ -244,6 +245,7 @@ func getSessionResponse(ctx context.Context, session *models.Principal) (*models
DistributedMode: erasure,
Permissions: resourcePermissions,
AllowResources: allowResources,
CustomStyles: customStyles,
}
return sessionResp, nil
}

View File

@@ -18,6 +18,9 @@ package restapi
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"errors"
"io"
"net/http"
"strings"
@@ -40,6 +43,21 @@ import (
// more likely then others.
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"
type CustomButtonStyle struct {
BackgroundColor *string `json:"backgroundColor"`
TextColor *string `json:"textColor"`
HoverColor *string `json:"hoverColor"`
HoverText *string `json:"hoverText"`
ActiveColor *string `json:"activeColor"`
ActiveText *string `json:"activeText"`
}
type CustomStyles struct {
BackgroundColor *string `json:"backgroundColor"`
FontColor *string `json:"fontColor"`
ButtonStyles *CustomButtonStyle `json:"buttonStyles"`
}
func RandomCharStringWithAlphabet(n int, alphabet string) string {
random := make([]byte, n)
if _, err := io.ReadFull(rand.Reader, random); err != nil {
@@ -130,6 +148,28 @@ func ExpireSessionCookie() http.Cookie {
}
}
func ValidateEncodedStyles(encodedStyles string) error {
// encodedStyle JSON validation
str, err := base64.StdEncoding.DecodeString(encodedStyles)
if err != nil {
return err
}
var styleElements *CustomStyles
err = json.Unmarshal(str, &styleElements)
if err != nil {
return err
}
if styleElements.BackgroundColor == nil || styleElements.FontColor == nil || styleElements.ButtonStyles == nil {
return errors.New("specified style is not in the correct format")
}
return nil
}
// SanitizeEncodedPrefix replaces spaces for + since those are lost when you do GET parameters
func SanitizeEncodedPrefix(rawPrefix string) string {
return strings.ReplaceAll(rawPrefix, " ", "+")

View File

@@ -3729,6 +3729,8 @@ definitions:
type: boolean
ob:
type: boolean
customStyleOb:
type: string
startProfilingItem:
type: object
properties:
@@ -3776,6 +3778,8 @@ definitions:
type: array
items:
type: string
customStyles:
type: string
allowResources:
type: array
items: