Skip to content

Commit

Permalink
Added sharing keys with a user while creating a new project
Browse files Browse the repository at this point in the history
  • Loading branch information
vmatsiiako committed Jan 10, 2023
1 parent ce7d411 commit 556a646
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 81 deletions.
2 changes: 1 addition & 1 deletion frontend/components/basic/popups/BottomRightPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function BottonRightPopup({
}: PopupProps): JSX.Element {
return (
<div
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"
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-md 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">
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/navigation/NavBarDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ export default function Navbar() {
href="https://infisical.com/docs/getting-started/introduction"
target="_blank"
rel="noopener noreferrer"
className="text-gray-200 hover:text-primary duration-200">
className="text-gray-200 hover:bg-white/10 px-3 rounded-md duration-200 text-sm mr-4 py-2 flex items-center">
<FontAwesomeIcon icon={faBook} className="text-xl mr-2" />
Docs
<FontAwesomeIcon icon={faUpRightFromSquare} className="text-xs mb-[0.1rem] mr-5 ml-1.5" />
</a>
<Menu as="div" className="relative inline-block text-left">
<div className="mr-4">
Expand Down
79 changes: 78 additions & 1 deletion frontend/components/utilities/attemptLogin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import login2 from '~/pages/api/auth/Login2';
import addSecrets from '~/pages/api/files/AddSecrets';
import getOrganizations from '~/pages/api/organization/getOrgs';
import getOrganizationUserProjects from '~/pages/api/organization/GetOrgUserProjects';
import getUser from '~/pages/api/user/getUser';
import uploadKeys from '~/pages/api/workspace/uploadKeys';

import { encryptAssymmetric } from './cryptography/crypto';
import encryptSecrets from './secrets/encryptSecrets';
import Telemetry from './telemetry/Telemetry';
import { saveTokenToLocalStorage } from './saveTokenToLocalStorage';
Expand All @@ -19,6 +22,7 @@ interface SecretDataProps {
comment: string;
}

const crypto = require("crypto");
const nacl = require('tweetnacl');
nacl.util = require('tweetnacl-util');
const jsrp = require('jsrp');
Expand Down Expand Up @@ -121,8 +125,81 @@ const attemptLogin = async (
telemetry.capture('User Logged In');
}

if (isSignUp) {
const randomBytes = crypto.randomBytes(16).toString("hex");
const PRIVATE_KEY = String(localStorage.getItem("PRIVATE_KEY"));

const myUser = await getUser();

const { ciphertext, nonce } = encryptAssymmetric({
plaintext: randomBytes,
publicKey: myUser.publicKey,
privateKey: PRIVATE_KEY,
}) as { ciphertext: string; nonce: string };

await uploadKeys(
projectToLogin,
myUser._id,
ciphertext,
nonce
);

const secretsToBeAdded: SecretDataProps[] = [{
type: "shared",
pos: 0,
key: "DATABASE_URL",
value: "mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net",
comment: "This is an example of secret referencing.",
id: ''
}, {
type: "shared",
pos: 1,
key: "DB_USERNAME",
value: "OVERRIDE_THIS",
comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need",
id: ''
}, {
type: "personal",
pos: 2,
key: "DB_USERNAME",
value: "user1234",
comment: "",
id: ''
}, {
type: "shared",
pos: 3,
key: "DB_PASSWORD",
value: "OVERRIDE_THIS",
comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need",
id: ''
}, {
type: "personal",
pos: 4,
key: "DB_PASSWORD",
value: "example_password",
comment: "",
id: ''
}, {
type: "shared",
pos: 5,
key: "TWILIO_AUTH_TOKEN",
value: "example_twillio_token",
comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need",
id: ''
}, {
type: "shared",
pos: 6,
key: "WEBSITE_URL",
value: "http://localhost:3000",
comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need",
id: ''
}]
const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeAdded, workspaceId: String(localStorage.getItem('projectData.id')), env: 'dev' })
await addSecrets({ secrets: secrets ?? [], env: "dev", workspaceId: String(localStorage.getItem('projectData.id')) });
}

if (isLogin) {
router.push('/dashboard/');
router.push('/dashboard/' + localStorage.getItem('projectData.id'));
}
} catch (error) {
console.log(error)
Expand Down
128 changes: 67 additions & 61 deletions frontend/components/utilities/secrets/encryptSecrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,75 +42,81 @@ interface EncryptedSecretProps {
* @returns
*/
const encryptSecrets = async ({ secretsToEncrypt, workspaceId, env }: { secretsToEncrypt: SecretDataProps[]; workspaceId: string; env: string; }) => {
const sharedKey = await getLatestFileKey({ workspaceId });
let secrets;
try {
const sharedKey = await getLatestFileKey({ workspaceId });

const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");

let randomBytes: string;
if (Object.keys(sharedKey).length > 0) {
// case: a (shared) key exists for the workspace
randomBytes = decryptAssymmetric({
ciphertext: sharedKey.latestKey.encryptedKey,
nonce: sharedKey.latestKey.nonce,
publicKey: sharedKey.latestKey.sender.publicKey,
privateKey: PRIVATE_KEY,
});
} else {
// case: a (shared) key does not exist for the workspace
randomBytes = crypto.randomBytes(16).toString("hex");
}

const secrets = secretsToEncrypt.map((secret) => {
// encrypt key
const {
ciphertext: secretKeyCiphertext,
iv: secretKeyIV,
tag: secretKeyTag,
} = encryptSymmetric({
plaintext: secret.key,
key: randomBytes,
});
let randomBytes: string;
if (Object.keys(sharedKey).length > 0) {
// case: a (shared) key exists for the workspace
randomBytes = decryptAssymmetric({
ciphertext: sharedKey.latestKey.encryptedKey,
nonce: sharedKey.latestKey.nonce,
publicKey: sharedKey.latestKey.sender.publicKey,
privateKey: PRIVATE_KEY,
});
} else {
// case: a (shared) key does not exist for the workspace
randomBytes = crypto.randomBytes(16).toString("hex");
}
secrets = secretsToEncrypt.map((secret) => {
// encrypt key
const {
ciphertext: secretKeyCiphertext,
iv: secretKeyIV,
tag: secretKeyTag,
} = encryptSymmetric({
plaintext: secret.key,
key: randomBytes,
});

// encrypt value
const {
ciphertext: secretValueCiphertext,
iv: secretValueIV,
tag: secretValueTag,
} = encryptSymmetric({
plaintext: secret.value,
key: randomBytes,
});
// encrypt value
const {
ciphertext: secretValueCiphertext,
iv: secretValueIV,
tag: secretValueTag,
} = encryptSymmetric({
plaintext: secret.value,
key: randomBytes,
});

// encrypt comment
const {
ciphertext: secretCommentCiphertext,
iv: secretCommentIV,
tag: secretCommentTag,
} = encryptSymmetric({
plaintext: secret.comment ?? '',
key: randomBytes,
});
// encrypt comment
const {
ciphertext: secretCommentCiphertext,
iv: secretCommentIV,
tag: secretCommentTag,
} = encryptSymmetric({
plaintext: secret.comment ?? '',
key: randomBytes,
});

const result: EncryptedSecretProps = {
id: secret.id,
createdAt: '',
environment: env,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag,
type: secret.type,
};
const result: EncryptedSecretProps = {
id: secret.id,
createdAt: '',
environment: env,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag,
type: secret.type,
};

return result;
});
return result;
});
} catch (error) {
console.log("Error while encrypting secrets");
}

return secrets;

}

export default encryptSecrets;
8 changes: 3 additions & 5 deletions frontend/ee/components/ActivityTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,8 @@ const ActivityLogsRow = ({ row, toggleSidebar }: { row: logData, toggleSidebar:
<td>{String(t("common:timestamp"))}</td>
<td>{row.createdAt}</td>
</tr>}
{payloadOpened &&
row.payload?.map((action, index) => {
action.secretVersions.length > 0 &&
<tr key={index} className="h-9 text-bunker-200 border-mineshaft-700 border-t text-sm">
{payloadOpened && row.payload?.map((action, index) => {
return action.secretVersions.length > 0 && <tr key={index} className="h-9 text-bunker-200 border-mineshaft-700 border-t text-sm">
<td></td>
<td className="">{t("activity:event." + action.name)}</td>
<td className="text-primary-300 cursor-pointer hover:text-primary duration-200" onClick={() => toggleSidebar(action._id)}>
Expand All @@ -87,7 +85,7 @@ const ActivityLogsRow = ({ row, toggleSidebar }: { row: logData, toggleSidebar:
{payloadOpened &&
<tr className='h-9 text-bunker-200 border-mineshaft-700 border-t text-sm'>
<td></td>
<td>{String(t("common:ip-address"))}</td>
<td>{String(t("activity:ip-address"))}</td>
<td>{row.ipAddress}</td>
</tr>}
</>
Expand Down
29 changes: 18 additions & 11 deletions frontend/pages/dashboard/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ import {
faPlus,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Menu, Transition } from '@headlessui/react';
import getProjectSercetSnapshotsCount from 'ee/api/secrets/GetProjectSercetSnapshotsCount';
import performSecretRollback from 'ee/api/secrets/PerformSecretRollback';
import PITRecoverySidebar from 'ee/components/PITRecoverySidebar';
import { Document, YAMLSeq } from 'yaml';

import Button from '~/components/basic/buttons/Button';
import ListBox from '~/components/basic/Listbox';
Expand Down Expand Up @@ -542,15 +540,24 @@ export default function Dashboard() {
text={String(t("Rollback to this snapshot"))}
onButtonPressed={async () => {
// Update secrets in the state only for the current environment
setData(
snapshotData.secretVersions
.filter(row => reverseEnvMapping[row.environment] == env)
.map((sv, position) => {
return {
id: sv.id, pos: position, type: sv.type, key: sv.key, value: sv.value, comment: ''
}
})
);
const rolledBackSecrets = snapshotData.secretVersions
.filter(row => reverseEnvMapping[row.environment] == env)
.map((sv, position) => {
return {
id: sv.id, pos: position, type: sv.type, key: sv.key, value: sv.value, comment: ''
}
});
setData(rolledBackSecrets);

setSharedToHide(
rolledBackSecrets?.filter(row => (rolledBackSecrets
?.map((item) => item.key)
.filter(
(item, index) =>
index !==
rolledBackSecrets?.map((item) => item.key).indexOf(item)
).includes(row.key) && row.type == 'shared'))?.map((item) => item.id)
)

// Perform the rollback globally
performSecretRollback({ workspaceId, version: snapshotData.version })
Expand Down

0 comments on commit 556a646

Please sign in to comment.