mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-25 14:05:03 +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 {
|
||||
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">
|
||||
|
@ -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">
|
||||
|
@ -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';
|
||||
@ -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');
|
||||
@ -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)
|
||||
|
@ -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,
|
||||
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 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 {
|
||||
// case: a (shared) key does not exist for the workspace
|
||||
randomBytes = crypto.randomBytes(16).toString("hex");
|
||||
} catch (error) {
|
||||
console.log("Error while encrypting secrets");
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
export default encryptSecrets;
|
||||
|
@ -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)}>
|
||||
@ -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>}
|
||||
</>
|
||||
|
@ -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';
|
||||
@ -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 })
|
||||
|
Reference in New Issue
Block a user