Remove/deprecate service accounts + old logs/actions

This commit is contained in:
Tuan Dang
2023-11-05 15:49:24 +02:00
parent c29a11866e
commit 50ce977c55
59 changed files with 13 additions and 3560 deletions
@@ -1,76 +0,0 @@
import SecurityClient from "@app/components/utilities/SecurityClient";
interface WorkspaceProps {
workspaceId: string;
offset: number;
limit: number;
userId: string;
actionNames: string;
}
/**
* This function fetches the activity logs for a certain project
* @param {object} obj
* @param {string} obj.workspaceId - workspace id for which we are trying to get project log
* @param {object} obj.offset - teh starting point of logs that we want to pull
* @param {object} obj.limit - how many logs will we output
* @param {object} obj.userId - optional userId filter - will only query logs for that user
* @param {string} obj.actionNames - optional actionNames filter - will only query logs for those actions
* @returns
*/
const getProjectLogs = async ({
workspaceId,
offset,
limit,
userId,
actionNames
}: WorkspaceProps) => {
let payload;
if (userId !== "" && actionNames !== "") {
payload = {
offset: String(offset),
limit: String(limit),
sortBy: "recent",
userId: JSON.stringify(userId),
actionNames
};
} else if (userId !== "") {
payload = {
offset: String(offset),
limit: String(limit),
sortBy: "recent",
userId: JSON.stringify(userId)
};
} else if (actionNames !== "") {
payload = {
offset: String(offset),
limit: String(limit),
sortBy: "recent",
actionNames
};
} else {
payload = {
offset: String(offset),
limit: String(limit),
sortBy: "recent"
};
}
return SecurityClient.fetchCall(
`/api/v1/workspace/${workspaceId}/logs?${new URLSearchParams(payload)}`,
{
method: "GET",
headers: {
"Content-Type": "application/json"
}
}
).then(async (res) => {
if (res && res.status === 200) {
return (await res.json()).logs;
}
console.log("Failed to get project logs");
return undefined;
});
};
export default getProjectLogs;
@@ -1,237 +0,0 @@
// TODO: deprecate in favor of new audit logs
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import Image from "next/image";
import { useRouter } from "next/router";
import { faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import getActionData from "@app/ee/api/secrets/GetActionData";
import patienceDiff from "@app/ee/utilities/findTextDifferences";
import {
useGetUserWsKey
} from "@app/hooks/api";
import {
decryptAssymmetric,
decryptSymmetric
} from "../../components/utilities/cryptography/crypto";
interface SideBarProps {
toggleSidebar: (value: string) => void;
currentAction: string;
}
interface SecretProps {
secret: string;
secretKeyCiphertext: string;
secretKeyHash: string;
secretKeyIV: string;
secretKeyTag: string;
secretValueCiphertext: string;
secretValueHash: string;
secretValueIV: string;
secretValueTag: string;
}
interface DecryptedSecretProps {
newSecretVersion: {
key: string;
value: string;
};
oldSecretVersion: {
key: string;
value: string;
};
}
interface ActionProps {
name: string;
}
/**
* @param {object} obj
* @param {function} obj.toggleSidebar - function that opens or closes the sidebar
* @param {string} obj.currentAction - the action id for which a sidebar is being displayed
* @returns the sidebar with the payload of user activity logs
*/
const ActivitySideBar = ({ toggleSidebar, currentAction }: SideBarProps) => {
const { t } = useTranslation();
const router = useRouter();
const [actionData, setActionData] = useState<DecryptedSecretProps[]>();
const [actionMetaData, setActionMetaData] = useState<ActionProps>();
const [isLoading, setIsLoading] = useState(false);
const { data: wsKey } = useGetUserWsKey(String(router.query.id));
useEffect(() => {
const getLogData = async () => {
setIsLoading(true);
const tempActionData = await getActionData({ actionId: currentAction });
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
// #TODO: make this a separate function and reuse across the app
let decryptedLatestKey: string;
if (wsKey) {
// assymmetrically decrypt symmetric key with local private key
decryptedLatestKey = decryptAssymmetric({
ciphertext: wsKey.encryptedKey,
nonce: wsKey.nonce,
publicKey: wsKey.sender.publicKey,
privateKey: String(PRIVATE_KEY)
});
const decryptedSecretVersions = tempActionData.payload.secretVersions.map(
(encryptedSecretVersion: {
newSecretVersion?: SecretProps;
oldSecretVersion?: SecretProps;
}) => ({
newSecretVersion: {
key: decryptSymmetric({
ciphertext: encryptedSecretVersion.newSecretVersion!.secretKeyCiphertext,
iv: encryptedSecretVersion.newSecretVersion!.secretKeyIV,
tag: encryptedSecretVersion.newSecretVersion!.secretKeyTag,
key: decryptedLatestKey
}),
value: decryptSymmetric({
ciphertext: encryptedSecretVersion.newSecretVersion!.secretValueCiphertext,
iv: encryptedSecretVersion.newSecretVersion!.secretValueIV,
tag: encryptedSecretVersion.newSecretVersion!.secretValueTag,
key: decryptedLatestKey
})
},
oldSecretVersion: {
key: encryptedSecretVersion.oldSecretVersion?.secretKeyCiphertext
? decryptSymmetric({
ciphertext: encryptedSecretVersion.oldSecretVersion?.secretKeyCiphertext,
iv: encryptedSecretVersion.oldSecretVersion?.secretKeyIV,
tag: encryptedSecretVersion.oldSecretVersion?.secretKeyTag,
key: decryptedLatestKey
})
: undefined,
value: encryptedSecretVersion.oldSecretVersion?.secretValueCiphertext
? decryptSymmetric({
ciphertext: encryptedSecretVersion.oldSecretVersion?.secretValueCiphertext,
iv: encryptedSecretVersion.oldSecretVersion?.secretValueIV,
tag: encryptedSecretVersion.oldSecretVersion?.secretValueTag,
key: decryptedLatestKey
})
: undefined
}
})
);
setActionData(decryptedSecretVersions);
setActionMetaData({ name: tempActionData.name });
setIsLoading(false);
}
};
getLogData();
}, [currentAction, wsKey]);
return (
<div
className={`absolute border-l border-mineshaft-500 ${
isLoading ? "bg-bunker-800" : "bg-bunker"
} fixed right-0 z-40 flex h-[calc(100vh)] w-96 flex-col justify-between shadow-xl`}
>
{isLoading ? (
<div className="mb-8 flex h-full items-center justify-center">
<Image
src="/images/loading/loading.gif"
height={60}
width={100}
alt="infisical loading indicator"
/>
</div>
) : (
<div className="h-min">
<div className="flex flex-row items-center justify-between border-b border-mineshaft-500 px-4 py-3">
<p className="text-lg font-semibold text-bunker-200">
{t(`activity.event.${actionMetaData?.name}`)}
</p>
<div
className="p-1"
onKeyDown={() => null}
role="button"
tabIndex={0}
onClick={() => toggleSidebar("")}
>
<FontAwesomeIcon icon={faXmark} className="h-4 w-4 cursor-pointer text-bunker-300" />
</div>
</div>
<div className="overflow-y-autp flex h-[calc(100vh-120px)] flex-col overflow-y-auto px-4">
{(actionMetaData?.name === "readSecrets" ||
actionMetaData?.name === "addSecrets" ||
actionMetaData?.name === "deleteSecrets") &&
actionData?.map((item, id) => (
<div key={`secret.${id + 1}`}>
<div className="ph-no-capture mt-4 pl-1 text-xs text-bunker-200">
{item.newSecretVersion.key}
</div>
<div className="w-full break-all rounded-md border border-mineshaft-500 bg-mineshaft-600 px-2 py-0.5 font-mono text-sm text-bunker-200">
{item.newSecretVersion.value ? (
<span> {item.newSecretVersion.value} </span>
) : (
<span className="text-bunker-400"> EMPTY </span>
)}
</div>
</div>
))}
{actionMetaData?.name === "updateSecrets" &&
actionData?.map((item, id) => (
<>
<div className="mt-4 pl-1 text-xs text-bunker-200">
{item.newSecretVersion.key}
</div>
<div className="overflow-hidden break-all rounded-md border border-mineshaft-500 font-mono text-bunker-200">
<div className="ph-no-capture bg-red/40 px-2">
-{" "}
{patienceDiff(
item.oldSecretVersion.value.split(""),
item.newSecretVersion.value.split(""),
false
).lines.map(
(character, lineId) =>
character.bIndex !== -1 && (
<span
key={`actionData.${id + 1}.line.${lineId + 1}`}
className={`${
character.aIndex === -1 && "bg-red-700/80 text-bunker-100"
}`}
>
{character.line}
</span>
)
)}
</div>
<div className="ph-no-capture break-all bg-green-500/40 px-2">
+{" "}
{patienceDiff(
item.oldSecretVersion.value.split(""),
item.newSecretVersion.value.split(""),
false
).lines.map(
(character, lineId) =>
character.aIndex !== -1 && (
<span
key={`actionData.${id + 1}.linev2.${lineId + 1}`}
className={`${
character.bIndex === -1 && "bg-green-700/80 text-bunker-100"
}`}
>
{character.line}
</span>
)
)}
</div>
</div>
</>
))}
</div>
</div>
)}
</div>
);
};
export default ActivitySideBar;
@@ -1,205 +0,0 @@
// TODO: deprecate in favor of new audit logs
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { faAngleDown, faAngleRight, faUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import timeSince from "@app/ee/utilities/timeSince";
import guidGenerator from "../../components/utilities/randomId";
interface PayloadProps {
_id: string;
name: string;
secretVersions: string[];
}
interface LogData {
_id: string;
channel: string;
createdAt: string;
ipAddress: string;
user: string;
serviceAccount: {
name: string;
};
serviceTokenData: {
name: string;
};
payload: PayloadProps[];
}
/**
* This is a single row of the activity table
* @param obj
* @param {LogData} obj.row - data for a certain event
* @param {function} obj.toggleSidebar - open and close sidebar that displays data for a specific event
* @returns
*/
const ActivityLogsRow = ({
row,
toggleSidebar
}: {
row: LogData;
toggleSidebar: (value: string) => void;
}) => {
const [payloadOpened, setPayloadOpened] = useState(false);
const { t } = useTranslation();
const renderUser = () => {
if (row?.user) return `${row.user}`;
if (row?.serviceAccount) return `Service Account: ${row.serviceAccount.name}`;
if (row?.serviceTokenData?.name) return `Service Token: ${row.serviceTokenData.name}`;
return "";
};
return (
<>
<div key={guidGenerator()} className="w-full bg-mineshaft-800 text-sm text-mineshaft-200 duration-100 flex flex-row items-center">
<button
type="button"
onClick={() => setPayloadOpened(!payloadOpened)}
className="border-t border-mineshaft-700 pt-[0.58rem]"
>
<FontAwesomeIcon
icon={payloadOpened ? faAngleDown : faAngleRight}
className={`ml-6 mb-2 text-mineshaft-300 cursor-pointer ${
payloadOpened ? "bg-mineshaft-500 hover:bg-mineshaft-500" : "hover:bg-mineshaft-700"
} h-4 w-4 rounded-md p-1 duration-100`}
/>
</button>
<div className="border-t border-mineshaft-700 py-3 w-1/4 pl-6">
{row.payload
?.map(
(action) =>
`${String(action.secretVersions.length)} ${t(`activity.event.${action.name}`)}`
)
.join(" and ")}
</div>
<div className="border-t border-mineshaft-700 py-3 pl-6 w-1/4">{renderUser()}</div>
<div className="border-t border-mineshaft-700 py-3 pl-6 w-1/4">{row.channel}</div>
<div className="border-t border-mineshaft-700 py-3 pl-6 w-1/4">
{timeSince(new Date(row.createdAt))}
</div>
</div>
{payloadOpened && (
<div className="h-9 border-t border-mineshaft-700 text-sm text-bunker-200 bg-mineshaft-900/50 w-full flex flex-row items-center">
<div className='max-w-xl w-full flex flex-row items-center'>
<div className='w-24' />
<div className='w-1/2'>{String(t("common.timestamp"))}</div>
<div className='w-1/2'>{row.createdAt}</div>
</div>
</div>
)}
{payloadOpened &&
row.payload?.map(
(action) =>
action.secretVersions.length > 0 && (
<div
key={action.name}
className="h-9 border-t border-mineshaft-700 text-sm text-bunker-200 bg-mineshaft-900/50 w-full flex flex-row items-center"
>
<div className='max-w-xl w-full flex flex-row items-center'>
<div className='w-24' />
<div className='w-1/2'>{t(`activity.event.${action.name}`)}</div>
<button
type="button"
onClick={() => toggleSidebar(action._id)}
className='w-1/2 text-primary-300 hover:text-primary-500 flex flex-row justify-left items-center duration-100'
>
{action.secretVersions.length +
(action.secretVersions.length !== 1 ? " secrets" : " secret")}
<FontAwesomeIcon
icon={faUpRightFromSquare}
className="ml-2 mb-0.5 h-3 w-3 font-light"
/>
</button>
</div>
</div>
)
)}
{payloadOpened && (
<div className="h-9 border-t border-mineshaft-700 text-sm text-bunker-200 bg-mineshaft-900/50 w-full flex flex-row items-center">
<div className='max-w-xl w-full flex flex-row items-center'>
<div className='w-24' />
<div className='w-1/2'>{String(t("activity.ip-address"))}</div>
<div className='w-1/2'>{row.ipAddress}</div>
</div>
</div>
)}
</>
);
};
/**
* This is the table for activity logs (one of the tabs)
* @param {object} obj
* @param {logData} obj.data - data for user activity logs
* @param {function} obj.toggleSidebar - function that opens or closes the sidebar
* @param {boolean} obj.isLoading - whether the log data has been loaded yet or not
* @returns
*/
const ActivityTable = ({
data,
toggleSidebar,
isLoading
}: {
data: LogData[];
toggleSidebar: (value: string) => void;
isLoading: boolean;
}) => {
const { t } = useTranslation();
return (
<div className="mt-8 w-full px-6">
<div className="table-container relative mb-6 w-full rounded-md border border-mineshaft-700 bg-mineshaft-800">
{/* <div className="absolute h-[3rem] w-full rounded-t-md bg-white/5" /> */}
<div className="my-1 w-full">
<div className="text-bunker-300 border-b border-mineshaft-600">
<div className="text-sm flex flex-row w-full">
<button
type="button"
onClick={() => {}}
className="opacity-0"
>
<FontAwesomeIcon
icon={faAngleRight}
className="ml-6 mb-2 text-bunker-100 hover:bg-mineshaft-700 cursor-pointer h-4 w-4 rounded-md p-1 duration-100"
/>
</button>
<div className="flex flex-row justify-between w-full">
<div className="pt-2.5 pb-3 text-left font-semibold w-1/4 pl-6">
{String(t("common.event")).toUpperCase()}
</div>
<div className="pl-6 pt-2.5 pb-3 text-left font-semibold w-1/4 pl-6">
{String(t("common.user")).toUpperCase()}
</div>
<div className="pl-6 pt-2.5 pb-3 text-left font-semibold w-1/4 pl-6">
{String(t("common.source")).toUpperCase()}
</div>
<div className="pl-6 pt-2.5 pb-3 text-left font-semibold w-1/4 pl-6">
{String(t("common.time")).toUpperCase()}
</div>
</div>
</div>
</div>
{data?.map((row, index) => (
<ActivityLogsRow
key={`activity.${index + 1}.${row._id}`}
row={row}
toggleSidebar={toggleSidebar}
/>
))}
</div>
</div>
{isLoading && (
<div className="mb-8 mt-4 bg-mineshaft-800 rounded-md h-60 flex w-full justify-center animate-pulse" />
)}
</div>
);
};
export default ActivityTable;