api keys
parent
cc59493b04
commit
16f3cbf1d2
|
@ -140,6 +140,10 @@
|
||||||
"userProfile.column.expiresAt": "Läuft ab am",
|
"userProfile.column.expiresAt": "Läuft ab am",
|
||||||
"userProfile.column.action": "Maßnahme",
|
"userProfile.column.action": "Maßnahme",
|
||||||
"userProfile.column.action.signOut": "Abmelden",
|
"userProfile.column.action.signOut": "Abmelden",
|
||||||
|
"userProfile.column.createdAt": "Erstellt am",
|
||||||
|
"userProfile.column.usageCount": "Anzahl der Nutzungen",
|
||||||
|
"userProfile.column.name": "Name",
|
||||||
|
"userProfile.column.token": "Token",
|
||||||
"userProfile.changeAvatarError.notification.message": "Ihr Avatar konnte nicht geändert werden",
|
"userProfile.changeAvatarError.notification.message": "Ihr Avatar konnte nicht geändert werden",
|
||||||
"userProfile.changeAvatarError.notification.description": "Avatar muss kleiner sein als {{MAX_AVATAR_SIZE}} MB sein",
|
"userProfile.changeAvatarError.notification.description": "Avatar muss kleiner sein als {{MAX_AVATAR_SIZE}} MB sein",
|
||||||
"userProfile.form.username": "Benutzername",
|
"userProfile.form.username": "Benutzername",
|
||||||
|
@ -149,6 +153,11 @@
|
||||||
"userProfile.form.repeatNewPassword": "Neues Passwort wiederholen",
|
"userProfile.form.repeatNewPassword": "Neues Passwort wiederholen",
|
||||||
"userProfile.form.language": "Sprache",
|
"userProfile.form.language": "Sprache",
|
||||||
"userProfile.header.yourSessions": "Ihre Sitzungen",
|
"userProfile.header.yourSessions": "Ihre Sitzungen",
|
||||||
|
"userProfile.header.yourApiKeys": "Ihre API Schlüssel",
|
||||||
|
"userProfile.button.createApiKey": "Neuen API Schlüssel erstellen",
|
||||||
|
"userProfile.button.createApiKey.popconfirm.title": "Name für den neuen API Schlüssel",
|
||||||
|
"userProfile.button.createApiKey.popconfirm.okText": "Erstellen",
|
||||||
|
"userProfile.button.copyToClipboard.notification": "API Token in die Zwischenablage kopiert",
|
||||||
"scanners.column.name": "Name",
|
"scanners.column.name": "Name",
|
||||||
"scanners.column.usedBy": "Verwendet von",
|
"scanners.column.usedBy": "Verwendet von",
|
||||||
"scanners.column.lastUsed": "Zuletzt verwendet",
|
"scanners.column.lastUsed": "Zuletzt verwendet",
|
||||||
|
|
|
@ -140,6 +140,10 @@
|
||||||
"userProfile.column.expiresAt": "Expires at",
|
"userProfile.column.expiresAt": "Expires at",
|
||||||
"userProfile.column.action": "Action",
|
"userProfile.column.action": "Action",
|
||||||
"userProfile.column.action.signOut": "Sign out",
|
"userProfile.column.action.signOut": "Sign out",
|
||||||
|
"userProfile.column.createdAt": "Created at",
|
||||||
|
"userProfile.column.usageCount": "Usage count",
|
||||||
|
"userProfile.column.name": "Name",
|
||||||
|
"userProfile.column.token": "Token",
|
||||||
"userProfile.changeAvatarError.notification.message": "Your avatar could not be changed",
|
"userProfile.changeAvatarError.notification.message": "Your avatar could not be changed",
|
||||||
"userProfile.changeAvatarError.notification.description": "Avatar must be smaller than {{MAX_AVATAR_SIZE}} MB",
|
"userProfile.changeAvatarError.notification.description": "Avatar must be smaller than {{MAX_AVATAR_SIZE}} MB",
|
||||||
"userProfile.form.username": "Username",
|
"userProfile.form.username": "Username",
|
||||||
|
@ -149,6 +153,11 @@
|
||||||
"userProfile.form.repeatNewPassword": "Repeat new password",
|
"userProfile.form.repeatNewPassword": "Repeat new password",
|
||||||
"userProfile.form.language": "Language",
|
"userProfile.form.language": "Language",
|
||||||
"userProfile.header.yourSessions": "Your sessions",
|
"userProfile.header.yourSessions": "Your sessions",
|
||||||
|
"userProfile.header.yourApiKeys": "Your API keys",
|
||||||
|
"userProfile.button.createApiKey": "Create new API key",
|
||||||
|
"userProfile.button.createApiKey.popconfirm.title": "Name for the new API key",
|
||||||
|
"userProfile.button.createApiKey.popconfirm.okText": "Create",
|
||||||
|
"userProfile.button.copyToClipboard.notification": "API token copied to clipboard",
|
||||||
"scanners.column.name": "Name",
|
"scanners.column.name": "Name",
|
||||||
"scanners.column.usedBy": "Used by",
|
"scanners.column.usedBy": "Used by",
|
||||||
"scanners.column.lastUsed": "Last used",
|
"scanners.column.lastUsed": "Last used",
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
Col,
|
Col,
|
||||||
Form,
|
Form,
|
||||||
Input,
|
Input,
|
||||||
|
Popconfirm,
|
||||||
Row,
|
Row,
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
|
@ -26,6 +27,12 @@ import {
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import {
|
||||||
|
CopyOutlined,
|
||||||
|
EyeInvisibleOutlined,
|
||||||
|
EyeOutlined,
|
||||||
|
KeyOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
|
||||||
export default function UserProfile() {
|
export default function UserProfile() {
|
||||||
const webSocketContext = useContext(WebSocketContext);
|
const webSocketContext = useContext(WebSocketContext);
|
||||||
|
@ -36,8 +43,10 @@ export default function UserProfile() {
|
||||||
const [oldPassword, setOldPassword] = useState("");
|
const [oldPassword, setOldPassword] = useState("");
|
||||||
const [newPassword, setNewPassword] = useState("");
|
const [newPassword, setNewPassword] = useState("");
|
||||||
const [repeatedNewPassword, setRepeatedNewPassword] = useState("");
|
const [repeatedNewPassword, setRepeatedNewPassword] = useState("");
|
||||||
|
const [newApiKeyName, setNewApikeyName] = useState("");
|
||||||
|
const [showApiKeyPassword, setShowApiKeyPassword] = useState(false);
|
||||||
|
|
||||||
const getTableColumns = () => {
|
const getSessionTableColumns = () => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
title: t("userProfile.column.userAgent"),
|
title: t("userProfile.column.userAgent"),
|
||||||
|
@ -91,7 +100,81 @@ export default function UserProfile() {
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTableItems = () => {
|
const getApiKeyTableColumns = () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: t("userProfile.column.name"),
|
||||||
|
dataIndex: "name",
|
||||||
|
key: "name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("userProfile.column.token"),
|
||||||
|
dataIndex: "token",
|
||||||
|
key: "token",
|
||||||
|
render: (text) => (
|
||||||
|
<Space>
|
||||||
|
{showApiKeyPassword ? text : "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"}
|
||||||
|
{showApiKeyPassword ? (
|
||||||
|
<EyeInvisibleOutlined
|
||||||
|
onClick={() => setShowApiKeyPassword(false)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<EyeOutlined onClick={() => setShowApiKeyPassword(true)} />
|
||||||
|
)}
|
||||||
|
{showApiKeyPassword && (
|
||||||
|
<CopyOutlined
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
setShowApiKeyPassword(false);
|
||||||
|
|
||||||
|
notificationApi["info"]({
|
||||||
|
message: t(
|
||||||
|
"userProfile.button.copyToClipboard.notification"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("userProfile.column.usageCount"),
|
||||||
|
dataIndex: "usageCount",
|
||||||
|
key: "usageCount",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: t("userProfile.column.lastUsed"),
|
||||||
|
dataIndex: "lastUsed",
|
||||||
|
key: "lastUsed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("userProfile.column.createdAt"),
|
||||||
|
dataIndex: "createdAt",
|
||||||
|
key: "createdAt",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getApiKeyTableItems = () => {
|
||||||
|
let items = [];
|
||||||
|
|
||||||
|
webSocketContext.User.ApiKeys.forEach((apiKey) => {
|
||||||
|
items.push({
|
||||||
|
key: apiKey.Id,
|
||||||
|
name: apiKey.Name,
|
||||||
|
token: apiKey.Token,
|
||||||
|
usageCount: apiKey.UsageCount,
|
||||||
|
createdAt: FormatDatetime(apiKey.CreatedAt),
|
||||||
|
lastUsed: FormatDatetime(apiKey.LastUsed),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSessionTableItems = () => {
|
||||||
let items = [];
|
let items = [];
|
||||||
|
|
||||||
webSocketContext.User.Sessions.sort(
|
webSocketContext.User.Sessions.sort(
|
||||||
|
@ -178,6 +261,15 @@ export default function UserProfile() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onCreateNewApiKeyConfirm = () => {
|
||||||
|
webSocketContext.SendSocketMessage(
|
||||||
|
SentMessagesCommands.CreateNewUserApiKey,
|
||||||
|
{ Name: newApiKeyName }
|
||||||
|
);
|
||||||
|
|
||||||
|
setNewApikeyName("");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{notificationContextHolder}
|
{notificationContextHolder}
|
||||||
|
@ -325,7 +417,46 @@ export default function UserProfile() {
|
||||||
{webSocketContext.User.Sessions.length})
|
{webSocketContext.User.Sessions.length})
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<Table columns={getTableColumns()} dataSource={getTableItems()} />
|
<Table
|
||||||
|
columns={getSessionTableColumns()}
|
||||||
|
dataSource={getSessionTableItems()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h1 style={{ fontWeight: "bold" }}>
|
||||||
|
{t("userProfile.header.yourApiKeys")} (
|
||||||
|
{webSocketContext.User.ApiKeys.length})
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<Popconfirm
|
||||||
|
placement="topRight"
|
||||||
|
title={t("userProfile.button.createApiKey.popconfirm.title")}
|
||||||
|
description={
|
||||||
|
<Input
|
||||||
|
placeholder="Name"
|
||||||
|
value={newApiKeyName}
|
||||||
|
onChange={(e) => setNewApikeyName(e.target.value)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
okText={t("userProfile.button.createApiKey.popconfirm.okText")}
|
||||||
|
cancelText={t("buttonCancel")}
|
||||||
|
onConfirm={() => onCreateNewApiKeyConfirm()}
|
||||||
|
>
|
||||||
|
<Button icon={<KeyOutlined />}>
|
||||||
|
{t("userProfile.button.createApiKey")}
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table
|
||||||
|
columns={getApiKeyTableColumns()}
|
||||||
|
dataSource={getApiKeyTableItems()}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
11
src/utils.js
11
src/utils.js
|
@ -144,6 +144,7 @@ let webSocketContextPreview = {
|
||||||
Sessions: [],
|
Sessions: [],
|
||||||
Permissions: [],
|
Permissions: [],
|
||||||
RoleId: "",
|
RoleId: "",
|
||||||
|
ApiKeys: [],
|
||||||
},
|
},
|
||||||
AllUsers: [],
|
AllUsers: [],
|
||||||
AllRoles: [],
|
AllRoles: [],
|
||||||
|
@ -191,6 +192,7 @@ const ReceivedMessagesCommands = {
|
||||||
AllUsersUserDeleted: 27,
|
AllUsersUserDeleted: 27,
|
||||||
AllUsersUserDeactivation: 28,
|
AllUsersUserDeactivation: 28,
|
||||||
GroupTasksCategoryGroupChanges: 29,
|
GroupTasksCategoryGroupChanges: 29,
|
||||||
|
NewUserApiKeyCreated: 30,
|
||||||
};
|
};
|
||||||
|
|
||||||
// commands sent to the backend server
|
// commands sent to the backend server
|
||||||
|
@ -213,6 +215,7 @@ export const SentMessagesCommands = {
|
||||||
ScannersDisconnectScanner: 16,
|
ScannersDisconnectScanner: 16,
|
||||||
GroupTasksCheckingForCategoryGroupChanges: 17,
|
GroupTasksCheckingForCategoryGroupChanges: 17,
|
||||||
HandleUserActionTaskStep: 18,
|
HandleUserActionTaskStep: 18,
|
||||||
|
CreateNewUserApiKey: 19,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function WebSocketProvider({
|
export function WebSocketProvider({
|
||||||
|
@ -951,7 +954,15 @@ export function WebSocketProvider({
|
||||||
return newArr;
|
return newArr;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case ReceivedMessagesCommands.NewUserApiKeyCreated:
|
||||||
|
setUser((user) => {
|
||||||
|
const updatedUser = { ...user };
|
||||||
|
|
||||||
|
updatedUser.ApiKeys.push(body);
|
||||||
|
|
||||||
|
return updatedUser;
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in New Issue