Skip to content

Commit 1b2b0fe

Browse files
authored
[*] mask password in the source connection string, fixes #610
1 parent 4834b0f commit 1b2b0fe

File tree

2 files changed

+41
-0
lines changed

2 files changed

+41
-0
lines changed

internal/webui/src/pages/SourcesPage/components/SourcesGrid/SourcesGrid.consts.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Source } from "types/Source/Source";
55
import { CustomTagsPopUp } from "./components/CustomTagsPopUp/CustomTagsPopUp";
66
import { EnabledSourceSwitch } from "./components/EnabledSourceSwitch";
77
import { HostConfigPopUp } from "./components/HostConfigPopUp/HostConfigPopUp";
8+
import { MaskConnectionString } from "./components/MaskConnectionString";
89
import { SourcesGridActions } from "./components/SourcesGridActions";
910

1011
const getIcon = (value: boolean) => {
@@ -33,6 +34,7 @@ export const useSourcesGridColumns = (): GridColDef<Source>[] => ([
3334
minWidth: 300,
3435
align: "center",
3536
headerAlign: "center",
37+
renderCell: ({ row }) => <MaskConnectionString source={row} />,
3638
},
3739
{
3840
field: "Metrics",
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useState } from "react";
2+
import Visibility from "@mui/icons-material/Visibility";
3+
import VisibilityOff from "@mui/icons-material/VisibilityOff";
4+
import { IconButton } from "@mui/material";
5+
import { Source } from "types/Source/Source";
6+
7+
type MaskedTextProps = {
8+
source: Source;
9+
};
10+
11+
const mask = (connStr: string) => {
12+
if (connStr.includes("://")) {
13+
return connStr.replace(
14+
/(postgresql:\/\/[^:]+:)([^@]+)(@.*)/,
15+
(_, start, pass, end) => `${start}${"•".repeat(8)}${end}`
16+
);
17+
} else {
18+
return connStr.replace(
19+
/(Password=)[^;]+(;|$)/i, (_, start, end) => `${start}${"•".repeat(8)}${end}`
20+
);
21+
}
22+
};
23+
24+
export const MaskConnectionString = ({ source }: MaskedTextProps) => {
25+
const [unmask, setUnmask] = useState(false);
26+
27+
const handleUnmask = () => setUnmask(!unmask);
28+
29+
return (
30+
<>
31+
<span>{unmask ? source.ConnStr : mask(source.ConnStr)}</span>
32+
<IconButton title={unmask ? "Hide password" : "Show password"} onClick={handleUnmask}>
33+
{
34+
unmask ? <VisibilityOff /> : <Visibility />
35+
}
36+
</IconButton>
37+
</>
38+
);
39+
};

0 commit comments

Comments
 (0)