Fixing merge conflicts for pages/users

This commit is contained in:
Vladyslav Matsiiako
2022-12-27 18:19:56 -05:00
120 changed files with 3842 additions and 399 deletions
+1
View File
@@ -48,6 +48,7 @@ export default function RouteGuard({ children }) {
// Check if the user is authenticated
const response = await checkAuth();
// #TODO: figure our why sometimes it doesn't output a response
// ANS(akhilmhdh): Because inside the security client the await token() doesn't have try/catch
if (!publicPaths.includes(path)) {
try {
if (response.status !== 200) {
+2 -2
View File
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import { memo,useState } from 'react';
import { faCircle, faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -140,4 +140,4 @@ const InputField = (
}
};
export default React.memo(InputField);
export default memo(InputField);
+83
View File
@@ -0,0 +1,83 @@
import React from "react";
import { Switch } from "@headlessui/react";
interface OverrideProps {
id: string;
keyName: string;
value: string;
pos: number;
comment: string;
}
interface ToggleProps {
enabled: boolean;
setEnabled: (value: boolean) => void;
addOverride: (value: OverrideProps) => void;
keyName: string;
value: string;
pos: number;
id: string;
comment: string;
deleteOverride: (id: string) => void;
sharedToHide: string[];
setSharedToHide: (values: string[]) => void;
}
/**
* This is a typical 'iPhone' toggle (e.g., user for overriding secrets with personal values)
* @param obj
* @param {boolean} obj.enabled - whether the toggle is turned on or off
* @param {function} obj.setEnabled - change the state of the toggle
* @param {function} obj.addOverride - a function that adds an override to a certain secret
* @param {string} obj.keyName - key of a certain secret
* @param {string} obj.value - value of a certain secret
* @param {number} obj.pos - position of a certain secret
#TODO: make the secret id persistent?
* @param {string} obj.id - id of a certain secret
* @param {function} obj.deleteOverride - a function that deleted an override for a certain secret
* @param {string[]} obj.sharedToHide - an array of shared secrets that we want to hide visually because they are overriden.
* @param {function} obj.setSharedToHide - a function that updates the array of secrets that we want to hide visually
* @returns
*/
export default function Toggle ({
enabled,
setEnabled,
addOverride,
keyName,
value,
pos,
id,
comment,
deleteOverride,
sharedToHide,
setSharedToHide
}: ToggleProps): JSX.Element {
return (
<Switch
checked={enabled}
onChange={() => {
if (enabled == false) {
addOverride({ id, keyName, value, pos, comment });
setSharedToHide([
...sharedToHide!,
id
])
} else {
deleteOverride(id);
}
setEnabled(!enabled);
}}
className={`${
enabled ? 'bg-primary' : 'bg-bunker-400'
} relative inline-flex h-5 w-9 items-center rounded-full`}
>
<span className="sr-only">Enable notifications</span>
<span
className={`${
enabled ? 'translate-x-[1.26rem]' : 'translate-x-0.5'
} inline-block h-3.5 w-3.5 transform rounded-full bg-bunker-800 transition`}
/>
</Switch>
)
}
+5 -4
View File
@@ -16,7 +16,7 @@ type ButtonProps = {
size: string;
icon?: IconProp;
active?: boolean;
iconDisabled?: string;
iconDisabled?: IconProp;
textDisabled?: string;
type?: ButtonHTMLAttributes<any>['type'];
};
@@ -73,15 +73,16 @@ export default function Button(props: ButtonProps): JSX.Element {
// Setting the text color for the text and icon
props.color == "mineshaft" && "text-gray-400",
props.color != "mineshaft" && props.color != "red" && "text-black",
props.color != "mineshaft" && props.color != "red" && props.color != "none" && "text-black",
props.color == "red" && "text-gray-200",
activityStatus && props.color != "red" ? "group-hover:text-black" : "",
props.color == "none" && "text-gray-200 text-xl",
activityStatus && props.color != "red" && props.color != "none" ? "group-hover:text-black" : "",
props.size == "icon" && "flex items-center justify-center"
);
const textStyle = classNames(
"relative duration-200",
"relative duration-200 text-center w-full",
// Show the loading sign if the loading indicator is on
props.loading ? "opacity-0" : "opacity-100",
@@ -3,6 +3,16 @@ import { faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
interface PopupProps {
buttonText: string;
buttonLink: string;
titleText: string;
emoji: string;
textLine1: string;
textLine2: string;
setCheckDocsPopUpVisible: (value: boolean) => void;
}
/**
* This is the notification that pops up at the bottom right when a user performs a certain action
* @param {object} org
@@ -23,16 +33,16 @@ export default function BottonRightPopup({
textLine1,
textLine2,
setCheckDocsPopUpVisible,
}) {
}: PopupProps): JSX.Element {
return (
<div
class="z-50 drop-shadow-xl border-gray-600/50 border flex flex-col items-start bg-bunker max-w-xl text-gray-200 pt-3 pb-4 rounded-xl absolute bottom-0 right-0 mr-6 mb-6"
className="z-50 drop-shadow-xl border-gray-600/50 border flex flex-col items-start bg-bunker max-w-xl text-gray-200 pt-3 pb-4 rounded-xl absolute bottom-0 right-0 mr-6 mb-6"
role="alert"
>
<div className="flex flex-row items-center justify-between w-full border-b border-gray-600/70 pb-3 px-6">
<div className="font-bold text-xl mr-2 mt-0.5 flex flex-row">
<div>{titleText}</div>
<div class="ml-2.5">{emoji}</div>
<div className="ml-2.5">{emoji}</div>
</div>
<button
className="mt-1"
@@ -44,14 +54,14 @@ export default function BottonRightPopup({
/>
</button>
</div>
<div class="block sm:inline px-6 mt-4 mb-0.5 text-gray-300">
<div className="block sm:inline px-6 mt-4 mb-0.5 text-gray-300">
{textLine1}
</div>
<div class="block sm:inline mb-4 px-6">{textLine2}</div>
<div className="block sm:inline mb-4 px-6">{textLine2}</div>
<div className="flex flex-row px-6 w-full">
{/*eslint-disable-next-line react/jsx-no-target-blank */}
<a
class="font-bold p-2 bg-white/10 rounded-md w-full hover:bg-primary duration-200 hover:text-black flex justify-center"
className="font-bold p-2 bg-white/10 rounded-md w-full hover:bg-primary duration-200 hover:text-black flex justify-center"
href={buttonLink}
target="_blank"
rel="noopener"
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import { faX } from '@fortawesome/free-solid-svg-icons';
+1 -1
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useRouter } from 'next/router';
import { faX } from '@fortawesome/free-solid-svg-icons';
@@ -0,0 +1,16 @@
/**
* This is the text field where people can add comments to particular secrets.
*/
const CommentField = ({ comment, modifyComment, position }: { comment: string; modifyComment: (value: string, posistion: number) => void; position: number;}) => {
return <div className={`relative mt-4 px-4 pt-4`}>
<p className='text-sm text-bunker-300'>Comments & notes</p>
<textarea
className="bg-bunker-800 h-32 w-full bg-bunker-800 p-2 rounded-md border border-mineshaft-500 text-sm text-bunker-300 outline-none focus:ring-2 ring-primary-800 ring-opacity-70"
value={comment}
onChange={(e) => modifyComment(e.target.value, position)}
placeholder="Leave any comments here..."
/>
</div>
}
export default CommentField;
@@ -1,4 +1,4 @@
import React, { SyntheticEvent, useRef } from 'react';
import { memo, SyntheticEvent, useRef } from 'react';
import { faCircle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -11,19 +11,21 @@ interface DashboardInputFieldProps {
onChangeHandler: (value: string, position: number) => void;
value: string;
type: 'varName' | 'value';
blurred: boolean;
duplicates: string[];
blurred?: boolean;
isDuplicate?: boolean;
override?: boolean;
}
/**
* This component renders the input fields on the dashboard
* @param {object} obj - the order number of a keyPair
* @param {number} obj.pos - the order number of a keyPair
* @param {number} obj.position - the order number of a keyPair
* @param {function} obj.onChangeHandler - what happens when the input is modified
* @param {string} obj.type - whether the input field is for a Key Name or for a Key Value
* @param {string} obj.value - value of the InputField
* @param {boolean} obj.blurred - whether the input field should be blurred (behind the gray dots) or not; this can be turned on/off in the dashboard
* @param {string[]} obj.duplicates - list of all the duplicated key names on the dashboard
* @param {boolean} obj.isDuplicate - if the key name is duplicated
* @param {boolean} obj.override - whether a secret/row should be displalyed as overriden
* @returns
*/
@@ -33,7 +35,8 @@ const DashboardInputField = ({
type,
value,
blurred,
duplicates
isDuplicate,
override
}: DashboardInputFieldProps) => {
const ref = useRef<HTMLDivElement | null>(null);
const syncScroll = (e: SyntheticEvent<HTMLDivElement>) => {
@@ -45,8 +48,7 @@ const DashboardInputField = ({
if (type === 'varName') {
const startsWithNumber = !isNaN(Number(value.charAt(0))) && value != '';
const hasDuplicates = duplicates?.includes(value);
const error = startsWithNumber || hasDuplicates;
const error = startsWithNumber || isDuplicate;
return (
<div className="flex-col w-full">
@@ -72,7 +74,7 @@ const DashboardInputField = ({
Should not start with a number
</p>
)}
{hasDuplicates && !startsWithNumber && (
{isDuplicate && !startsWithNumber && (
<p className="text-red text-xs mt-0.5 mx-1 mb-2 max-w-xs">
Secret names should be unique
</p>
@@ -85,6 +87,7 @@ const DashboardInputField = ({
<div
className={`group relative whitespace-pre flex flex-col justify-center w-full max-w-2xl border border-mineshaft-500 rounded-md`}
>
{override == true && <div className='bg-primary-300 absolute top-[0.1rem] right-[0.1rem] z-10 w-min text-xxs px-1 text-black opacity-80 rounded-md'>Override enabled</div>}
<input
value={value}
onChange={(e) => onChangeHandler(e.target.value, position)}
@@ -99,10 +102,13 @@ const DashboardInputField = ({
<div
ref={ref}
className={`${
blurred
blurred && !override
? 'text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-400 peer-active:text-gray-400'
: ''
} absolute flex flex-row whitespace-pre font-mono z-0 ph-no-capture max-w-2xl overflow-x-scroll bg-bunker-800 h-9 rounded-md text-gray-400 text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 focus:ring-primary/50 duration-100 no-scrollbar no-scrollbar::-webkit-scrollbar`}
} ${
override ? 'text-primary-300' : 'text-gray-400'
}
absolute flex flex-row whitespace-pre font-mono z-0 ph-no-capture max-w-2xl overflow-x-scroll bg-bunker-800 h-9 rounded-md text-md px-2 py-1.5 w-full min-w-16 outline-none focus:ring-2 focus:ring-primary/50 duration-100 no-scrollbar no-scrollbar::-webkit-scrollbar`}
>
{value.split(REGEX).map((word, id) => {
if (word.match(REGEX) !== null) {
@@ -153,4 +159,8 @@ const DashboardInputField = ({
return <>Something Wrong</>;
};
export default React.memo(DashboardInputField);
function inputPropsAreEqual(prev: DashboardInputFieldProps, next: DashboardInputFieldProps) {
return prev.value === next.value && prev.type === next.type && prev.position === next.position && prev.blurred === next.blurred && prev.override === next.override && prev.isDuplicate === next.isDuplicate;
}
export default memo(DashboardInputField, inputPropsAreEqual);
@@ -152,7 +152,13 @@ const DropZone = ({
icon={faUpload}
className="text-bunker-300 text-3xl mr-6"
/>
<<<<<<< HEAD
<p className="text-bunker-300 mt-1">{t("common:drop-zone-keys")}</p>
=======
<p className="text-bunker-300 mt-1">
Drag and drop your .env file here to add more keys.
</p>
>>>>>>> 876c5f51c2caabf92b47d5a18a9b98e601806bf4
</div>
{errorDragAndDrop ? (
<div className="mt-8 max-w-xl opacity-80">
@@ -0,0 +1,93 @@
import { Fragment,useState } from 'react';
import { faShuffle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Menu, Transition } from '@headlessui/react';
/**
* This is the menu that is used to (re)generate secrets (currently we only have ranom hex, in future we will have more options)
* @returns the popup-menu for randomly generating secrets
*/
const GenerateSecretMenu = ({ modifyValue, position }: { modifyValue: (value: string, position: number) => void; position: number; }) => {
const [randomStringLength, setRandomStringLength] = useState(32);
return <Menu as="div" className="relative inline-block text-left">
<div>
<Menu.Button className="inline-flex w-full justify-center rounded-md text-sm font-medium text-gray-200 rounded-md hover:bg-white/10 duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75">
<div className='py-1 px-2 rounded-md bg-bunker-800 hover:bg-bunker-500'>
<FontAwesomeIcon icon={faShuffle} className='text-bunker-300'/>
</div>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute z-50 drop-shadow-xl right-0 mt-0.5 w-[20rem] origin-top-right rounded-md bg-bunker border border-mineshaft-500 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none px-1 py-1">
<div
onClick={() => {
if (randomStringLength > 32) {
setRandomStringLength(32);
} else if (randomStringLength < 2) {
setRandomStringLength(2);
} else {
modifyValue(
[...Array(randomStringLength)]
.map(() => Math.floor(Math.random() * 16).toString(16))
.join(''),
position
);
}
}}
className="relative flex flex-row justify-start items-center cursor-pointer select-none py-2 px-2 rounded-md text-gray-400 hover:bg-white/10 duration-200 hover:text-gray-200 w-full"
>
<FontAwesomeIcon
className="text-lg pl-1.5 pr-3"
icon={faShuffle}
/>
<div className="text-sm justify-between flex flex-row w-full">
<p>Generate Random Hex</p>
<p>digits</p>
</div>
</div>
<div className="flex flex-row absolute bottom-[0.4rem] right-[3.3rem] w-16 bg-bunker-800 border border-chicago-700 rounded-md text-bunker-200 ">
<div
className="m-0.5 px-1 cursor-pointer rounded-md hover:bg-chicago-700"
onClick={() => {
if (randomStringLength > 1) {
setRandomStringLength(randomStringLength - 1);
}
}}
>
-
</div>
<input
onChange={(e) =>
setRandomStringLength(parseInt(e.target.value))
}
value={randomStringLength}
className="text-center z-20 peer text-sm bg-transparent w-full outline-none"
spellCheck="false"
/>
<div
className="m-0.5 px-1 pb-0.5 cursor-pointer rounded-md hover:bg-chicago-700"
onClick={() => {
if (randomStringLength < 32) {
setRandomStringLength(randomStringLength + 1);
}
}}
>
+
</div>
</div>
</Menu.Items>
</Transition>
</Menu>
}
export default GenerateSecretMenu;
+102
View File
@@ -0,0 +1,102 @@
import React from 'react';
import { faEllipsis, faShuffle, faX } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import Button from '../basic/buttons/Button';
import DashboardInputField from './DashboardInputField';
interface SecretDataProps {
type: 'personal' | 'shared';
pos: number;
key: string;
value: string;
id: string;
}
interface KeyPairProps {
keyPair: SecretDataProps;
deleteRow: (id: string) => void;
modifyKey: (value: string, position: number) => void;
modifyValue: (value: string, position: number) => void;
isBlurred: boolean;
isDuplicate: boolean;
toggleSidebar: (id: string) => void;
sidebarSecretId: string;
}
/**
* This component represent a single row for an environemnt variable on the dashboard
* @param {object} obj
* @param {String[]} obj.keyPair - data related to the environment variable (id, pos, key, value, public/private)
* @param {function} obj.deleteRow - a function to delete a certain keyPair
* @param {function} obj.modifyKey - modify the key of a certain environment variable
* @param {function} obj.modifyValue - modify the value of a certain environment variable
* @param {boolean} obj.isBlurred - if the blurring setting is turned on
* @param {boolean} obj.isDuplicate - list of all the duplicates secret names on the dashboard
* @param {function} obj.toggleSidebar - open/close/switch sidebar
* @param {string} obj.sidebarSecretId - the id of a secret for the side bar is displayed
* @returns
*/
const KeyPair = ({
keyPair,
deleteRow,
modifyKey,
modifyValue,
isBlurred,
isDuplicate,
toggleSidebar,
sidebarSecretId
}: KeyPairProps) => {
return (
<div className={`mx-1 flex flex-col items-center ml-1 ${keyPair.id == sidebarSecretId && "bg-mineshaft-500 duration-200"} rounded-md`}>
<div className="relative flex flex-row justify-between w-full max-w-5xl mr-auto max-h-14 my-1 items-start px-1">
{keyPair.type == "personal" && <div className="group font-normal group absolute top-[1rem] left-[0.2rem] z-40 inline-block text-gray-300 underline hover:text-primary duration-200">
<div className='w-1 h-1 rounded-full bg-primary z-40'></div>
<span className="absolute z-50 hidden group-hover:flex group-hover:animate-popdown duration-200 w-[10.5rem] -left-[0.4rem] -top-[1.7rem] translate-y-full px-2 py-2 bg-mineshaft-500 rounded-b-md rounded-r-md text-center text-gray-100 text-sm after:content-[''] after:absolute after:left-0 after:bottom-[100%] after:-translate-x-0 after:border-8 after:border-x-transparent after:border-t-transparent after:border-b-mineshaft-500">
This secret is overriden
</span>
</div>}
<div className="min-w-xl w-96">
<div className="flex pr-1 items-center rounded-lg mt-4 md:mt-0 max-h-16">
<DashboardInputField
onChangeHandler={modifyKey}
type="varName"
position={keyPair.pos}
value={keyPair.key}
isDuplicate={isDuplicate}
/>
</div>
</div>
<div className="w-full min-w-5xl">
<div className="flex min-w-7xl items-center pl-1 pr-1.5 rounded-lg mt-4 md:mt-0 max-h-10 ">
<DashboardInputField
onChangeHandler={modifyValue}
type="value"
position={keyPair.pos}
value={keyPair.value}
blurred={isBlurred}
override={keyPair.type == "personal"}
/>
</div>
</div>
<div onClick={() => toggleSidebar(keyPair.id)} className="cursor-pointer w-9 h-9 bg-mineshaft-700 hover:bg-chicago-700 rounded-md flex flex-row justify-center items-center duration-200">
<FontAwesomeIcon
className="text-gray-300 px-2.5 text-lg mt-0.5"
icon={faEllipsis}
/>
</div>
<div className="w-2"></div>
<div className="bg-[#9B3535] hover:bg-red rounded-md duration-200">
<Button
onButtonPressed={() => deleteRow(keyPair.id)}
color="none"
size="icon-sm"
icon={faX}
/>
</div>
</div>
</div>
);
};
export default React.memo(KeyPair);
+168
View File
@@ -0,0 +1,168 @@
import { useState } from 'react';
import { faX } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import SecretVersionList from 'ee/components/SecretVersionList';
import Button from '../basic/buttons/Button';
import Toggle from '../basic/Toggle';
import CommentField from './CommentField';
import DashboardInputField from './DashboardInputField';
import GenerateSecretMenu from './GenerateSecretMenu';
interface SecretProps {
key: string;
value: string;
pos: number;
type: string;
id: string;
comment: string;
}
interface OverrideProps {
id: string;
keyName: string;
value: string;
pos: number;
comment: string;
}
interface SideBarProps {
toggleSidebar: (value: string) => void;
data: SecretProps[];
modifyKey: (value: string, position: number) => void;
modifyValue: (value: string, position: number) => void;
modifyComment: (value: string, position: number) => void;
addOverride: (value: OverrideProps) => void;
deleteOverride: (id: string) => void;
buttonReady: boolean;
savePush: () => void;
sharedToHide: string[];
setSharedToHide: (values: string[]) => void;
}
/**
* @param {object} obj
* @param {function} obj.toggleSidebar - function that opens or closes the sidebar
* @param {SecretProps[]} obj.data - data of a certain key valeu pair
* @param {function} obj.modifyKey - function that modifies the secret key
* @param {function} obj.modifyValue - function that modifies the secret value
* @param {function} obj.addOverride - override a certain secret
* @param {function} obj.deleteOverride - delete the personal override for a certain secret
* @param {boolean} obj.buttonReady - is the button for saving chagnes active
* @param {function} obj.savePush - save changes andp ush secrets
* @param {string[]} obj.sharedToHide - an array of shared secrets that we want to hide visually because they are overriden.
* @param {function} obj.setSharedToHide - a function that updates the array of secrets that we want to hide visually
* @returns the sidebar with 'secret's settings'
*/
const SideBar = ({
toggleSidebar,
data,
modifyKey,
modifyValue,
modifyComment,
addOverride,
deleteOverride,
buttonReady,
savePush,
sharedToHide,
setSharedToHide
}: SideBarProps) => {
const [overrideEnabled, setOverrideEnabled] = useState(data.map(secret => secret.type).includes("personal"));
return <div className='absolute border-l border-mineshaft-500 bg-bunker fixed h-full w-96 top-14 right-0 z-50 shadow-xl flex flex-col justify-between'>
<div className='h-min overflow-y-auto'>
<div className="flex flex-row px-4 py-3 border-b border-mineshaft-500 justify-between items-center">
<p className="font-semibold text-lg text-bunker-200">Secret</p>
<div className='p-1' onClick={() => toggleSidebar("None")}>
<FontAwesomeIcon icon={faX} className='w-4 h-4 text-bunker-300 cursor-pointer'/>
</div>
</div>
<div className='mt-4 px-4 pointer-events-none'>
<p className='text-sm text-bunker-300'>Key</p>
<DashboardInputField
onChangeHandler={modifyKey}
type="varName"
position={data[0]?.pos}
value={data[0]?.key}
isDuplicate={false}
blurred={false}
/>
</div>
{data.filter(secret => secret.type == "shared")[0]?.value
? <div className={`relative mt-2 px-4 ${overrideEnabled && "opacity-40 pointer-events-none"} duration-200`}>
<p className='text-sm text-bunker-300'>Value</p>
<DashboardInputField
onChangeHandler={modifyValue}
type="value"
position={data.filter(secret => secret.type == "shared")[0]?.pos}
value={data.filter(secret => secret.type == "shared")[0]?.value}
isDuplicate={false}
blurred={true}
/>
<div className='absolute bg-bunker-800 right-[1.07rem] top-[1.6rem] z-50'>
<GenerateSecretMenu modifyValue={modifyValue} position={data.filter(secret => secret.type == "shared")[0]?.pos} />
</div>
</div>
: <div className='px-4 text-sm text-bunker-300 pt-4'>
<span className='py-0.5 px-1 rounded-md bg-primary-200/10 mr-1'>Note:</span>
This secret is personal. It is not shared with any of your teammates.
</div>}
<div className='mt-4 px-4'>
{data.filter(secret => secret.type == "shared")[0]?.value &&
<div className='flex flex-row items-center justify-between my-2 pl-1 pr-2'>
<p className='text-sm text-bunker-300'>Override value with a personal value</p>
<Toggle
enabled={overrideEnabled}
setEnabled={setOverrideEnabled}
addOverride={addOverride}
keyName={data[0]?.key}
value={data[0]?.value}
pos={data[0]?.pos}
id={data[0]?.id}
comment={data[0]?.comment}
deleteOverride={deleteOverride}
sharedToHide={sharedToHide}
setSharedToHide={setSharedToHide}
/>
</div>}
<div className={`relative ${!overrideEnabled && "opacity-40 pointer-events-none"} duration-200`}>
<DashboardInputField
onChangeHandler={modifyValue}
type="value"
position={overrideEnabled ? data.filter(secret => secret.type == "personal")[0]?.pos : data[0]?.pos}
value={overrideEnabled ? data.filter(secret => secret.type == "personal")[0]?.value : data[0]?.value}
isDuplicate={false}
blurred={true}
/>
<div className='absolute right-[0.57rem] top-[0.3rem] z-50'>
<GenerateSecretMenu modifyValue={modifyValue} position={overrideEnabled ? data.filter(secret => secret.type == "personal")[0]?.pos : data[0]?.pos} />
</div>
</div>
</div>
{/* <div className={`relative mt-4 px-4 opacity-80 duration-200`}>
<p className='text-sm text-bunker-200'>Group</p>
<ListBox
selected={"Database Secrets"}
onChange={() => {}}
data={["Group1"]}
isFull={true}
/>
</div> */}
<SecretVersionList secretId={data[0]?.id} />
<CommentField comment={data.filter(secret => secret.type == "shared")[0]?.comment} modifyComment={modifyComment} position={data[0]?.pos} />
</div>
<div className={`flex justify-start max-w-sm mt-4 px-4 mt-full mb-[4.7rem]`}>
<Button
text="Save Changes"
onButtonPressed={savePush}
color="primary"
size="md"
active={buttonReady}
textDisabled="Saved"
/>
</div>
</div>
};
export default SideBar;
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import {
faArrowRight,
@@ -1,10 +1,18 @@
/* eslint-disable react/jsx-key */
<<<<<<< HEAD
import React, { Fragment, useEffect, useMemo, useState } from "react";
import Image from "next/image";
import { useRouter } from "next/router";
import { TFunction, useTranslation } from "next-i18next";
import { faGithub, faSlack } from "@fortawesome/free-brands-svg-icons";
import { faCircleQuestion } from "@fortawesome/free-regular-svg-icons";
=======
import { Fragment, useEffect, useState } from 'react';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { faGithub, faSlack } from '@fortawesome/free-brands-svg-icons';
import { faCircleQuestion } from '@fortawesome/free-regular-svg-icons';
>>>>>>> 876c5f51c2caabf92b47d5a18a9b98e601806bf4
import {
faAngleDown,
faBook,
+1 -1
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import {
faAngleRight,
@@ -16,12 +16,19 @@ export default class SecurityClient {
const req = new Request(resource, options);
if (this.#token == '') {
this.setToken(await token());
try {
// TODO: This should be moved to a context to do it only once when app loads
// this try catch saves route guard from a stuck state
this.setToken(await token());
} catch (error) {
console.error("Unauthorized access");
}
}
if (this.#token) {
req.headers.set('Authorization', 'Bearer ' + this.#token);
return fetch(req);
}
return fetch(req);
}
}
@@ -10,6 +10,14 @@ const {
const nacl = require('tweetnacl');
nacl.util = require('tweetnacl-util');
interface SecretProps {
key: string;
value: string;
type: 'personal' | 'shared';
comment: string;
id: string;
}
interface Props {
env: keyof typeof envMapping;
setFileState: any;
@@ -34,12 +42,12 @@ const getSecretsForProject = async ({
} catch (error) {
console.log('ERROR: Not able to access the latest file');
}
// This is called isKeyAvilable but what it really means is if a person is able to create new key pairs
// This is called isKeyAvailable but what it really means is if a person is able to create new key pairs
setIsKeyAvailable(!file.key ? file.secrets.length == 0 : true);
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
const tempFileState: { key: string; value: string; type: string }[] = [];
const tempFileState: SecretProps[] = [];
if (file.key) {
// assymmetrically decrypt symmetric key with local private key
const key = decryptAssymmetric({
@@ -64,10 +72,25 @@ const getSecretsForProject = async ({
tag: secretPair.secretValue.tag,
key
});
let plainTextComment;
if (secretPair.secretComment.ciphertext) {
plainTextComment = decryptSymmetric({
ciphertext: secretPair.secretComment.ciphertext,
iv: secretPair.secretComment.iv,
tag: secretPair.secretComment.tag,
key
});
} else {
plainTextComment = "";
}
tempFileState.push({
id: secretPair._id,
key: plainTextKey,
value: plainTextValue,
type: secretPair.type
type: secretPair.type,
comment: plainTextComment
});
});
}
@@ -76,28 +99,30 @@ const getSecretsForProject = async ({
setData(
tempFileState.map((line, index) => {
return {
id: guidGenerator(),
id: line['id'],
pos: index,
key: line['key'],
value: line['value'],
type: line['type']
type: line['type'],
comment: line['comment']
};
})
);
return tempFileState.map((line, index) => {
return {
id: guidGenerator(),
id: line['id'],
pos: index,
key: line['key'],
value: line['value'],
type: line['type']
type: line['type'],
comment: line['comment']
};
});
} catch (error) {
console.log('Something went wrong during accessing or decripting secrets.');
}
return true;
return [];
};
export default getSecretsForProject;
@@ -47,35 +47,49 @@ const pushKeys = async({ obj, workspaceId, env }: { obj: object; workspaceId: st
const secrets = Object.keys(obj).map((key) => {
// encrypt key
const {
ciphertext: ciphertextKey,
iv: ivKey,
tag: tagKey,
ciphertext: secretKeyCiphertext,
iv: secretKeyIV,
tag: secretKeyTag,
} = encryptSymmetric({
plaintext: key,
plaintext: key.slice(1),
key: randomBytes,
});
// encrypt value
const {
ciphertext: ciphertextValue,
iv: ivValue,
tag: tagValue,
ciphertext: secretValueCiphertext,
iv: secretValueIV,
tag: secretValueTag,
} = encryptSymmetric({
plaintext: obj[key as keyof typeof obj][0],
key: randomBytes,
});
const visibility = obj[key as keyof typeof obj][1] != null ? obj[key as keyof typeof obj][1] : "personal";
// encrypt comment
const {
ciphertext: secretCommentCiphertext,
iv: secretCommentIV,
tag: secretCommentTag,
} = encryptSymmetric({
plaintext: obj[key as keyof typeof obj][1],
key: randomBytes,
});
const visibility = key.charAt(0) == "p" ? "personal" : "shared";
return {
ciphertextKey,
ivKey,
tagKey,
hashKey: crypto.createHash("sha256").update(key).digest("hex"),
ciphertextValue,
ivValue,
tagValue,
hashValue: crypto.createHash("sha256").update(obj[key as keyof typeof obj][0]).digest("hex"),
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretKeyHash: crypto.createHash("sha256").update(key.slice(1)).digest("hex"),
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretValueHash: crypto.createHash("sha256").update(obj[key as keyof typeof obj][0]).digest("hex"),
secretCommentCiphertext,
secretCommentIV,
secretCommentTag,
secretCommentHash: crypto.createHash("sha256").update(obj[key as keyof typeof obj][1]).digest("hex"),
type: visibility,
};
});