mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 07:27:43 +00:00
Added sharing keys with a user while creating a new project
This commit is contained in:
@@ -36,7 +36,7 @@ export default function BottonRightPopup({
|
|||||||
}: PopupProps): JSX.Element {
|
}: PopupProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div
|
<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"
|
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="flex flex-row items-center justify-between w-full border-b border-gray-600/70 pb-3 px-6">
|
||||||
|
|||||||
@@ -112,9 +112,9 @@ export default function Navbar() {
|
|||||||
href="https://infisical.com/docs/getting-started/introduction"
|
href="https://infisical.com/docs/getting-started/introduction"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
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
|
Docs
|
||||||
<FontAwesomeIcon icon={faUpRightFromSquare} className="text-xs mb-[0.1rem] mr-5 ml-1.5" />
|
|
||||||
</a>
|
</a>
|
||||||
<Menu as="div" className="relative inline-block text-left">
|
<Menu as="div" className="relative inline-block text-left">
|
||||||
<div className="mr-4">
|
<div className="mr-4">
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import login2 from '~/pages/api/auth/Login2';
|
|||||||
import addSecrets from '~/pages/api/files/AddSecrets';
|
import addSecrets from '~/pages/api/files/AddSecrets';
|
||||||
import getOrganizations from '~/pages/api/organization/getOrgs';
|
import getOrganizations from '~/pages/api/organization/getOrgs';
|
||||||
import getOrganizationUserProjects from '~/pages/api/organization/GetOrgUserProjects';
|
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 encryptSecrets from './secrets/encryptSecrets';
|
||||||
import Telemetry from './telemetry/Telemetry';
|
import Telemetry from './telemetry/Telemetry';
|
||||||
import { saveTokenToLocalStorage } from './saveTokenToLocalStorage';
|
import { saveTokenToLocalStorage } from './saveTokenToLocalStorage';
|
||||||
@@ -19,6 +22,7 @@ interface SecretDataProps {
|
|||||||
comment: string;
|
comment: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const crypto = require("crypto");
|
||||||
const nacl = require('tweetnacl');
|
const nacl = require('tweetnacl');
|
||||||
nacl.util = require('tweetnacl-util');
|
nacl.util = require('tweetnacl-util');
|
||||||
const jsrp = require('jsrp');
|
const jsrp = require('jsrp');
|
||||||
@@ -121,8 +125,81 @@ const attemptLogin = async (
|
|||||||
telemetry.capture('User Logged In');
|
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) {
|
if (isLogin) {
|
||||||
router.push('/dashboard/');
|
router.push('/dashboard/' + localStorage.getItem('projectData.id'));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
|||||||
@@ -42,75 +42,81 @@ interface EncryptedSecretProps {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const encryptSecrets = async ({ secretsToEncrypt, workspaceId, env }: { secretsToEncrypt: SecretDataProps[]; workspaceId: string; env: string; }) => {
|
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;
|
let randomBytes: string;
|
||||||
if (Object.keys(sharedKey).length > 0) {
|
if (Object.keys(sharedKey).length > 0) {
|
||||||
// case: a (shared) key exists for the workspace
|
// case: a (shared) key exists for the workspace
|
||||||
randomBytes = decryptAssymmetric({
|
randomBytes = decryptAssymmetric({
|
||||||
ciphertext: sharedKey.latestKey.encryptedKey,
|
ciphertext: sharedKey.latestKey.encryptedKey,
|
||||||
nonce: sharedKey.latestKey.nonce,
|
nonce: sharedKey.latestKey.nonce,
|
||||||
publicKey: sharedKey.latestKey.sender.publicKey,
|
publicKey: sharedKey.latestKey.sender.publicKey,
|
||||||
privateKey: PRIVATE_KEY,
|
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 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
} else {
|
} catch (error) {
|
||||||
// case: a (shared) key does not exist for the workspace
|
console.log("Error while encrypting secrets");
|
||||||
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,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
});
|
|
||||||
|
|
||||||
const result: EncryptedSecretProps = {
|
|
||||||
id: secret.id,
|
|
||||||
createdAt: '',
|
|
||||||
environment: env,
|
|
||||||
secretKeyCiphertext,
|
|
||||||
secretKeyIV,
|
|
||||||
secretKeyTag,
|
|
||||||
secretValueCiphertext,
|
|
||||||
secretValueIV,
|
|
||||||
secretValueTag,
|
|
||||||
secretCommentCiphertext,
|
|
||||||
secretCommentIV,
|
|
||||||
secretCommentTag,
|
|
||||||
type: secret.type,
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
return secrets;
|
return secrets;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default encryptSecrets;
|
export default encryptSecrets;
|
||||||
|
|||||||
@@ -72,10 +72,8 @@ const ActivityLogsRow = ({ row, toggleSidebar }: { row: logData, toggleSidebar:
|
|||||||
<td>{String(t("common:timestamp"))}</td>
|
<td>{String(t("common:timestamp"))}</td>
|
||||||
<td>{row.createdAt}</td>
|
<td>{row.createdAt}</td>
|
||||||
</tr>}
|
</tr>}
|
||||||
{payloadOpened &&
|
{payloadOpened && row.payload?.map((action, index) => {
|
||||||
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">
|
||||||
action.secretVersions.length > 0 &&
|
|
||||||
<tr key={index} className="h-9 text-bunker-200 border-mineshaft-700 border-t text-sm">
|
|
||||||
<td></td>
|
<td></td>
|
||||||
<td className="">{t("activity:event." + action.name)}</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)}>
|
<td className="text-primary-300 cursor-pointer hover:text-primary duration-200" onClick={() => toggleSidebar(action._id)}>
|
||||||
@@ -87,7 +85,7 @@ const ActivityLogsRow = ({ row, toggleSidebar }: { row: logData, toggleSidebar:
|
|||||||
{payloadOpened &&
|
{payloadOpened &&
|
||||||
<tr className='h-9 text-bunker-200 border-mineshaft-700 border-t text-sm'>
|
<tr className='h-9 text-bunker-200 border-mineshaft-700 border-t text-sm'>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>{String(t("common:ip-address"))}</td>
|
<td>{String(t("activity:ip-address"))}</td>
|
||||||
<td>{row.ipAddress}</td>
|
<td>{row.ipAddress}</td>
|
||||||
</tr>}
|
</tr>}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -16,11 +16,9 @@ import {
|
|||||||
faPlus,
|
faPlus,
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { Menu, Transition } from '@headlessui/react';
|
|
||||||
import getProjectSercetSnapshotsCount from 'ee/api/secrets/GetProjectSercetSnapshotsCount';
|
import getProjectSercetSnapshotsCount from 'ee/api/secrets/GetProjectSercetSnapshotsCount';
|
||||||
import performSecretRollback from 'ee/api/secrets/PerformSecretRollback';
|
import performSecretRollback from 'ee/api/secrets/PerformSecretRollback';
|
||||||
import PITRecoverySidebar from 'ee/components/PITRecoverySidebar';
|
import PITRecoverySidebar from 'ee/components/PITRecoverySidebar';
|
||||||
import { Document, YAMLSeq } from 'yaml';
|
|
||||||
|
|
||||||
import Button from '~/components/basic/buttons/Button';
|
import Button from '~/components/basic/buttons/Button';
|
||||||
import ListBox from '~/components/basic/Listbox';
|
import ListBox from '~/components/basic/Listbox';
|
||||||
@@ -542,15 +540,24 @@ export default function Dashboard() {
|
|||||||
text={String(t("Rollback to this snapshot"))}
|
text={String(t("Rollback to this snapshot"))}
|
||||||
onButtonPressed={async () => {
|
onButtonPressed={async () => {
|
||||||
// Update secrets in the state only for the current environment
|
// Update secrets in the state only for the current environment
|
||||||
setData(
|
const rolledBackSecrets = snapshotData.secretVersions
|
||||||
snapshotData.secretVersions
|
.filter(row => reverseEnvMapping[row.environment] == env)
|
||||||
.filter(row => reverseEnvMapping[row.environment] == env)
|
.map((sv, position) => {
|
||||||
.map((sv, position) => {
|
return {
|
||||||
return {
|
id: sv.id, pos: position, type: sv.type, key: sv.key, value: sv.value, comment: ''
|
||||||
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
|
// Perform the rollback globally
|
||||||
performSecretRollback({ workspaceId, version: snapshotData.version })
|
performSecretRollback({ workspaceId, version: snapshotData.version })
|
||||||
|
|||||||
Reference in New Issue
Block a user