mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-16 06:45:28 +00:00
chore(frontend): fixed all eslint errors
This commit is contained in:
@@ -1,32 +1,27 @@
|
||||
import token from '~/pages/api/auth/Token';
|
||||
import token from '@app/pages/api/auth/Token';
|
||||
|
||||
export default class SecurityClient {
|
||||
static #token = '';
|
||||
|
||||
constructor() {}
|
||||
|
||||
static setToken(token: string) {
|
||||
this.#token = token;
|
||||
static setToken(tokenStr: string) {
|
||||
this.#token = tokenStr;
|
||||
}
|
||||
|
||||
static async fetchCall(
|
||||
resource: RequestInfo,
|
||||
options?: RequestInit | undefined
|
||||
) {
|
||||
static async fetchCall(resource: RequestInfo, options?: RequestInit | undefined) {
|
||||
const req = new Request(resource, options);
|
||||
|
||||
if (this.#token == '') {
|
||||
if (this.#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");
|
||||
console.error('Unauthorized access');
|
||||
}
|
||||
}
|
||||
|
||||
if (this.#token) {
|
||||
req.headers.set('Authorization', 'Bearer ' + this.#token);
|
||||
req.headers.set('Authorization', `Bearer ${this.#token}`);
|
||||
}
|
||||
|
||||
return fetch(req);
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { SecretDataProps } from 'public/data/frequentInterfaces';
|
||||
/* eslint-disable prefer-destructuring */
|
||||
import crypto from 'crypto';
|
||||
|
||||
import Aes256Gcm from '~/components/utilities/cryptography/aes-256-gcm';
|
||||
import login1 from '~/pages/api/auth/Login1';
|
||||
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 Aes256Gcm from '@app/components/utilities/cryptography/aes-256-gcm';
|
||||
import login1 from '@app/pages/api/auth/Login1';
|
||||
import login2 from '@app/pages/api/auth/Login2';
|
||||
import addSecrets from '@app/pages/api/files/AddSecrets';
|
||||
import getOrganizations from '@app/pages/api/organization/getOrgs';
|
||||
import getOrganizationUserProjects from '@app/pages/api/organization/GetOrgUserProjects';
|
||||
import getUser from '@app/pages/api/user/getUser';
|
||||
import uploadKeys from '@app/pages/api/workspace/uploadKeys';
|
||||
import jsrp from 'jsrp';
|
||||
import { SecretDataProps } from 'public/data/frequentInterfaces';
|
||||
|
||||
import { encryptAssymmetric } from './cryptography/crypto';
|
||||
import encryptSecrets from './secrets/encryptSecrets';
|
||||
@@ -15,11 +18,7 @@ import Telemetry from './telemetry/Telemetry';
|
||||
import { saveTokenToLocalStorage } from './saveTokenToLocalStorage';
|
||||
import SecurityClient from './SecurityClient';
|
||||
|
||||
|
||||
const crypto = require("crypto");
|
||||
const nacl = require('tweetnacl');
|
||||
nacl.util = require('tweetnacl-util');
|
||||
const jsrp = require('jsrp');
|
||||
// eslint-disable-next-line new-cap
|
||||
const client = new jsrp.client();
|
||||
|
||||
/**
|
||||
@@ -46,7 +45,7 @@ const attemptLogin = async (
|
||||
client.init(
|
||||
{
|
||||
username: email,
|
||||
password: password
|
||||
password
|
||||
},
|
||||
async () => {
|
||||
const clientPublicKey = client.getPublicKey();
|
||||
@@ -59,8 +58,10 @@ const attemptLogin = async (
|
||||
const clientProof = client.getProof(); // called M1
|
||||
|
||||
// if everything works, go the main dashboard page.
|
||||
const { token, publicKey, encryptedPrivateKey, iv, tag } =
|
||||
await login2(email, clientProof);
|
||||
const { token, publicKey, encryptedPrivateKey, iv, tag } = await login2(
|
||||
email,
|
||||
clientProof
|
||||
);
|
||||
|
||||
SecurityClient.setToken(token);
|
||||
|
||||
@@ -70,10 +71,7 @@ const attemptLogin = async (
|
||||
tag,
|
||||
secret: password
|
||||
.slice(0, 32)
|
||||
.padStart(
|
||||
32 + (password.slice(0, 32).length - new Blob([password]).size),
|
||||
'0'
|
||||
)
|
||||
.padStart(32 + (password.slice(0, 32).length - new Blob([password]).size), '0')
|
||||
});
|
||||
|
||||
saveTokenToLocalStorage({
|
||||
@@ -83,9 +81,9 @@ const attemptLogin = async (
|
||||
tag,
|
||||
privateKey
|
||||
});
|
||||
|
||||
|
||||
const userOrgs = await getOrganizations();
|
||||
const userOrgsData = userOrgs.map((org: { _id: string; }) => org._id);
|
||||
const userOrgsData = userOrgs.map((org: { _id: string }) => org._id);
|
||||
|
||||
let orgToLogin;
|
||||
if (userOrgsData.includes(localStorage.getItem('orgData.id'))) {
|
||||
@@ -99,11 +97,9 @@ const attemptLogin = async (
|
||||
orgId: orgToLogin
|
||||
});
|
||||
|
||||
orgUserProjects = orgUserProjects?.map((project: { _id: string; }) => project._id);
|
||||
orgUserProjects = orgUserProjects?.map((project: { _id: string }) => project._id);
|
||||
let projectToLogin;
|
||||
if (
|
||||
orgUserProjects.includes(localStorage.getItem('projectData.id'))
|
||||
) {
|
||||
if (orgUserProjects.includes(localStorage.getItem('projectData.id'))) {
|
||||
projectToLogin = localStorage.getItem('projectData.id');
|
||||
} else {
|
||||
try {
|
||||
@@ -113,90 +109,104 @@ const attemptLogin = async (
|
||||
console.log('ERROR: User likely has no projects. ', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (email) {
|
||||
telemetry.identify(email);
|
||||
telemetry.capture('User Logged In');
|
||||
}
|
||||
|
||||
if (isSignUp) {
|
||||
const randomBytes = crypto.randomBytes(16).toString("hex");
|
||||
const PRIVATE_KEY = String(localStorage.getItem("PRIVATE_KEY"));
|
||||
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,
|
||||
privateKey: PRIVATE_KEY
|
||||
}) as { ciphertext: string; nonce: string };
|
||||
|
||||
await uploadKeys(
|
||||
projectToLogin,
|
||||
myUser._id,
|
||||
ciphertext,
|
||||
nonce
|
||||
);
|
||||
await uploadKeys(projectToLogin, myUser._id, ciphertext, nonce);
|
||||
|
||||
const secretsToBeAdded: SecretDataProps[] = [{
|
||||
pos: 0,
|
||||
key: "DATABASE_URL",
|
||||
value: "mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net",
|
||||
valueOverride: undefined,
|
||||
comment: "This is an example of secret referencing.",
|
||||
id: ''
|
||||
}, {
|
||||
pos: 1,
|
||||
key: "DB_USERNAME",
|
||||
value: "OVERRIDE_THIS",
|
||||
valueOverride: undefined,
|
||||
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: ''
|
||||
}, {
|
||||
pos: 2,
|
||||
key: "DB_PASSWORD",
|
||||
value: "OVERRIDE_THIS",
|
||||
valueOverride: undefined,
|
||||
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: ''
|
||||
}, {
|
||||
pos: 3,
|
||||
key: "DB_USERNAME",
|
||||
value: "user1234",
|
||||
valueOverride: "user1234",
|
||||
comment: "",
|
||||
id: ''
|
||||
}, {
|
||||
pos: 4,
|
||||
key: "DB_PASSWORD",
|
||||
value: "example_password",
|
||||
valueOverride: "example_password",
|
||||
comment: "",
|
||||
id: ''
|
||||
}, {
|
||||
pos: 5,
|
||||
key: "TWILIO_AUTH_TOKEN",
|
||||
value: "example_twillio_token",
|
||||
valueOverride: undefined,
|
||||
comment: "",
|
||||
id: ''
|
||||
}, {
|
||||
pos: 6,
|
||||
key: "WEBSITE_URL",
|
||||
value: "http://localhost:3000",
|
||||
valueOverride: undefined,
|
||||
comment: "",
|
||||
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')) });
|
||||
const secretsToBeAdded: SecretDataProps[] = [
|
||||
{
|
||||
pos: 0,
|
||||
key: 'DATABASE_URL',
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
value: 'mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net',
|
||||
valueOverride: undefined,
|
||||
comment: 'This is an example of secret referencing.',
|
||||
id: ''
|
||||
},
|
||||
{
|
||||
pos: 1,
|
||||
key: 'DB_USERNAME',
|
||||
value: 'OVERRIDE_THIS',
|
||||
valueOverride: undefined,
|
||||
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: ''
|
||||
},
|
||||
{
|
||||
pos: 2,
|
||||
key: 'DB_PASSWORD',
|
||||
value: 'OVERRIDE_THIS',
|
||||
valueOverride: undefined,
|
||||
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: ''
|
||||
},
|
||||
{
|
||||
pos: 3,
|
||||
key: 'DB_USERNAME',
|
||||
value: 'user1234',
|
||||
valueOverride: 'user1234',
|
||||
comment: '',
|
||||
id: ''
|
||||
},
|
||||
{
|
||||
pos: 4,
|
||||
key: 'DB_PASSWORD',
|
||||
value: 'example_password',
|
||||
valueOverride: 'example_password',
|
||||
comment: '',
|
||||
id: ''
|
||||
},
|
||||
{
|
||||
pos: 5,
|
||||
key: 'TWILIO_AUTH_TOKEN',
|
||||
value: 'example_twillio_token',
|
||||
valueOverride: undefined,
|
||||
comment: '',
|
||||
id: ''
|
||||
},
|
||||
{
|
||||
pos: 6,
|
||||
key: 'WEBSITE_URL',
|
||||
value: 'http://localhost:3000',
|
||||
valueOverride: undefined,
|
||||
comment: '',
|
||||
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/' + localStorage.getItem('projectData.id'));
|
||||
router.push(`/dashboard/${localStorage.getItem('projectData.id')}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error);
|
||||
setErrorLogin(true);
|
||||
console.log('Login response not available');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import getOrganizationUsers from '~/pages/api/organization/GetOrgUsers';
|
||||
import checkUserAction from '~/pages/api/userActions/checkUserAction';
|
||||
import getOrganizationUsers from '@app/pages/api/organization/GetOrgUsers';
|
||||
import checkUserAction from '@app/pages/api/userActions/checkUserAction';
|
||||
|
||||
interface OnboardingCheckProps {
|
||||
setTotalOnboardingActionsDone?: (value: number) => void;
|
||||
@@ -26,46 +26,43 @@ const onboardingCheck = async ({
|
||||
action: 'slack_cta_clicked'
|
||||
});
|
||||
if (userActionSlack) {
|
||||
countActions = countActions + 1;
|
||||
countActions += 1;
|
||||
}
|
||||
setHasUserClickedSlack &&
|
||||
setHasUserClickedSlack(userActionSlack ? true : false);
|
||||
if (setHasUserClickedSlack) setHasUserClickedSlack(!!userActionSlack);
|
||||
|
||||
const userActionSecrets = await checkUserAction({
|
||||
action: 'first_time_secrets_pushed'
|
||||
});
|
||||
if (userActionSecrets) {
|
||||
countActions = countActions + 1;
|
||||
countActions += 1;
|
||||
}
|
||||
setHasUserPushedSecrets &&
|
||||
setHasUserPushedSecrets(userActionSecrets ? true : false);
|
||||
if (setHasUserPushedSecrets) setHasUserPushedSecrets(!!userActionSecrets);
|
||||
|
||||
const userActionIntro = await checkUserAction({
|
||||
action: 'intro_cta_clicked'
|
||||
});
|
||||
if (userActionIntro) {
|
||||
countActions = countActions + 1;
|
||||
countActions += 1;
|
||||
}
|
||||
setHasUserClickedIntro &&
|
||||
setHasUserClickedIntro(userActionIntro ? true : false);
|
||||
if (setHasUserClickedIntro) setHasUserClickedIntro(!!userActionIntro);
|
||||
|
||||
const userActionStar = await checkUserAction({
|
||||
action: 'star_cta_clicked'
|
||||
});
|
||||
if (userActionStar) {
|
||||
countActions = countActions + 1;
|
||||
countActions += 1;
|
||||
}
|
||||
setHasUserStarred && setHasUserStarred(userActionStar ? true : false);
|
||||
if (setHasUserStarred) setHasUserStarred(!!userActionStar);
|
||||
|
||||
const orgId = localStorage.getItem('orgData.id');
|
||||
const orgUsers = await getOrganizationUsers({
|
||||
orgId: orgId ? orgId : ''
|
||||
orgId: orgId || ''
|
||||
});
|
||||
if (orgUsers.length > 1) {
|
||||
countActions = countActions + 1;
|
||||
countActions += 1;
|
||||
}
|
||||
setUsersInOrg && setUsersInOrg(orgUsers.length > 1);
|
||||
setTotalOnboardingActionsDone && setTotalOnboardingActionsDone(countActions);
|
||||
if (setUsersInOrg) setUsersInOrg(orgUsers.length > 1);
|
||||
if (setTotalOnboardingActionsDone) setTotalOnboardingActionsDone(countActions);
|
||||
};
|
||||
|
||||
export default onboardingCheck;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
interface PasswordCheckProps {
|
||||
password: string;
|
||||
errorCheck: boolean;
|
||||
@@ -14,7 +15,7 @@ const passwordCheck = ({
|
||||
setPasswordErrorLength,
|
||||
setPasswordErrorNumber,
|
||||
setPasswordErrorLowerCase,
|
||||
errorCheck,
|
||||
errorCheck
|
||||
}: PasswordCheckProps) => {
|
||||
if (!password || password.length < 14) {
|
||||
setPasswordErrorLength(true);
|
||||
|
||||
@@ -32,7 +32,6 @@ class Aes256Gcm {
|
||||
/**
|
||||
* No need to run the constructor. The class only has static methods.
|
||||
*/
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Encrypts text with AES 256 GCM.
|
||||
@@ -65,11 +64,7 @@ class Aes256Gcm {
|
||||
* @returns {string}
|
||||
*/
|
||||
static decrypt({ ciphertext, iv, tag, secret }: DecryptProps): string {
|
||||
const decipher = crypto.createDecipheriv(
|
||||
ALGORITHM,
|
||||
secret,
|
||||
Buffer.from(iv, 'base64')
|
||||
);
|
||||
const decipher = crypto.createDecipheriv(ALGORITHM, secret, Buffer.from(iv, 'base64'));
|
||||
decipher.setAuthTag(Buffer.from(tag, 'base64'));
|
||||
|
||||
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* eslint-disable new-cap */
|
||||
import changePassword2 from '@app/pages/api/auth/ChangePassword2';
|
||||
import SRP1 from '@app/pages/api/auth/SRP1';
|
||||
import jsrp from 'jsrp';
|
||||
|
||||
import changePassword2 from '~/pages/api/auth/ChangePassword2';
|
||||
import SRP1 from '~/pages/api/auth/SRP1';
|
||||
|
||||
import Aes256Gcm from './aes-256-gcm';
|
||||
|
||||
const clientOldPassword = new jsrp.client();
|
||||
@@ -33,15 +33,16 @@ const changePassword = async (
|
||||
clientOldPassword.init(
|
||||
{
|
||||
username: email,
|
||||
password: currentPassword,
|
||||
password: currentPassword
|
||||
},
|
||||
async () => {
|
||||
const clientPublicKey = clientOldPassword.getPublicKey();
|
||||
|
||||
let serverPublicKey, salt;
|
||||
let serverPublicKey;
|
||||
let salt;
|
||||
try {
|
||||
const res = await SRP1({
|
||||
clientPublicKey: clientPublicKey,
|
||||
clientPublicKey
|
||||
});
|
||||
serverPublicKey = res.serverPublicKey;
|
||||
salt = res.salt;
|
||||
@@ -57,7 +58,7 @@ const changePassword = async (
|
||||
clientNewPassword.init(
|
||||
{
|
||||
username: email,
|
||||
password: newPassword,
|
||||
password: newPassword
|
||||
},
|
||||
async () => {
|
||||
clientNewPassword.createVerifier(async (err, result) => {
|
||||
@@ -67,11 +68,9 @@ const changePassword = async (
|
||||
secret: newPassword
|
||||
.slice(0, 32)
|
||||
.padStart(
|
||||
32 +
|
||||
(newPassword.slice(0, 32).length -
|
||||
new Blob([newPassword]).size),
|
||||
32 + (newPassword.slice(0, 32).length - new Blob([newPassword]).size),
|
||||
'0'
|
||||
),
|
||||
)
|
||||
});
|
||||
|
||||
if (ciphertext) {
|
||||
@@ -87,18 +86,18 @@ const changePassword = async (
|
||||
tag,
|
||||
salt: result.salt,
|
||||
verifier: result.verifier,
|
||||
clientProof,
|
||||
clientProof
|
||||
});
|
||||
if (res && res.status == 400) {
|
||||
if (res && res.status === 400) {
|
||||
setCurrentPasswordError(true);
|
||||
} else if (res && res.status == 200) {
|
||||
} else if (res && res.status === 200) {
|
||||
setPasswordChanged(true);
|
||||
setCurrentPassword('');
|
||||
setNewPassword('');
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (error) {
|
||||
setCurrentPasswordError(true);
|
||||
console.log(err);
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
const nacl = require('tweetnacl');
|
||||
nacl.util = require('tweetnacl-util');
|
||||
import aes from './aes-256-gcm';
|
||||
|
||||
type encryptAsymmetricProps = {
|
||||
const nacl = require('tweetnacl');
|
||||
nacl.util = require('tweetnacl-util');
|
||||
|
||||
type EncryptAsymmetricProps = {
|
||||
plaintext: string;
|
||||
publicKey: string;
|
||||
privateKey: string;
|
||||
@@ -22,8 +23,8 @@ type encryptAsymmetricProps = {
|
||||
const encryptAssymmetric = ({
|
||||
plaintext,
|
||||
publicKey,
|
||||
privateKey,
|
||||
}: encryptAsymmetricProps): {
|
||||
privateKey
|
||||
}: EncryptAsymmetricProps): {
|
||||
ciphertext: string;
|
||||
nonce: string;
|
||||
} => {
|
||||
@@ -37,11 +38,11 @@ const encryptAssymmetric = ({
|
||||
|
||||
return {
|
||||
ciphertext: nacl.util.encodeBase64(ciphertext),
|
||||
nonce: nacl.util.encodeBase64(nonce),
|
||||
nonce: nacl.util.encodeBase64(nonce)
|
||||
};
|
||||
};
|
||||
|
||||
type decryptAsymmetricProps = {
|
||||
type DecryptAsymmetricProps = {
|
||||
ciphertext: string;
|
||||
nonce: string;
|
||||
publicKey: string;
|
||||
@@ -61,8 +62,8 @@ const decryptAssymmetric = ({
|
||||
ciphertext,
|
||||
nonce,
|
||||
publicKey,
|
||||
privateKey,
|
||||
}: decryptAsymmetricProps): string => {
|
||||
privateKey
|
||||
}: DecryptAsymmetricProps): string => {
|
||||
const plaintext = nacl.box.open(
|
||||
nacl.util.decodeBase64(ciphertext),
|
||||
nacl.util.decodeBase64(nonce),
|
||||
@@ -73,24 +74,23 @@ const decryptAssymmetric = ({
|
||||
return nacl.util.encodeUTF8(plaintext);
|
||||
};
|
||||
|
||||
type encryptSymmetricProps = {
|
||||
type EncryptSymmetricProps = {
|
||||
plaintext: string;
|
||||
key: string;
|
||||
};
|
||||
|
||||
type encryptSymmetricReturn = {
|
||||
ciphertext:string;
|
||||
iv:string;
|
||||
tag:string;
|
||||
type EncryptSymmetricReturn = {
|
||||
ciphertext: string;
|
||||
iv: string;
|
||||
tag: string;
|
||||
};
|
||||
/**
|
||||
* Return symmetrically encrypted [plaintext] using [key].
|
||||
*/
|
||||
const encryptSymmetric = ({
|
||||
plaintext,
|
||||
key,
|
||||
}: encryptSymmetricProps): encryptSymmetricReturn => {
|
||||
let ciphertext, iv, tag;
|
||||
const encryptSymmetric = ({ plaintext, key }: EncryptSymmetricProps): EncryptSymmetricReturn => {
|
||||
let ciphertext;
|
||||
let iv;
|
||||
let tag;
|
||||
try {
|
||||
const obj = aes.encrypt({ text: plaintext, secret: key });
|
||||
ciphertext = obj.ciphertext;
|
||||
@@ -105,11 +105,11 @@ const encryptSymmetric = ({
|
||||
return {
|
||||
ciphertext,
|
||||
iv,
|
||||
tag,
|
||||
tag
|
||||
};
|
||||
};
|
||||
|
||||
type decryptSymmetricProps = {
|
||||
type DecryptSymmetricProps = {
|
||||
ciphertext: string;
|
||||
iv: string;
|
||||
tag: string;
|
||||
@@ -126,12 +126,7 @@ type decryptSymmetricProps = {
|
||||
* @param {String} obj.key - 32-byte hex key
|
||||
*
|
||||
*/
|
||||
const decryptSymmetric = ({
|
||||
ciphertext,
|
||||
iv,
|
||||
tag,
|
||||
key,
|
||||
}: decryptSymmetricProps): string => {
|
||||
const decryptSymmetric = ({ ciphertext, iv, tag, key }: DecryptSymmetricProps): string => {
|
||||
let plaintext;
|
||||
try {
|
||||
plaintext = aes.decrypt({ ciphertext, iv, tag, secret: key });
|
||||
@@ -143,9 +138,4 @@ const decryptSymmetric = ({
|
||||
return plaintext;
|
||||
};
|
||||
|
||||
export {
|
||||
decryptAssymmetric,
|
||||
decryptSymmetric,
|
||||
encryptAssymmetric,
|
||||
encryptSymmetric,
|
||||
};
|
||||
export { decryptAssymmetric, decryptSymmetric, encryptAssymmetric, encryptSymmetric };
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import issueBackupPrivateKey from '~/pages/api/auth/IssueBackupPrivateKey';
|
||||
import SRP1 from '~/pages/api/auth/SRP1';
|
||||
/* eslint-disable new-cap */
|
||||
import crypto from 'crypto';
|
||||
|
||||
import issueBackupPrivateKey from '@app/pages/api/auth/IssueBackupPrivateKey';
|
||||
import SRP1 from '@app/pages/api/auth/SRP1';
|
||||
import jsrp from 'jsrp';
|
||||
|
||||
import generateBackupPDF from '../generateBackupPDF';
|
||||
import Aes256Gcm from './aes-256-gcm';
|
||||
|
||||
const nacl = require('tweetnacl');
|
||||
nacl.util = require('tweetnacl-util');
|
||||
const jsrp = require('jsrp');
|
||||
const clientPassword = new jsrp.client();
|
||||
const clientKey = new jsrp.client();
|
||||
const crypto = require('crypto');
|
||||
|
||||
interface BackupKeyProps {
|
||||
email: string;
|
||||
@@ -42,15 +42,16 @@ const issueBackupKey = async ({
|
||||
clientPassword.init(
|
||||
{
|
||||
username: email,
|
||||
password: password
|
||||
password
|
||||
},
|
||||
async () => {
|
||||
const clientPublicKey = clientPassword.getPublicKey();
|
||||
|
||||
let serverPublicKey, salt;
|
||||
let serverPublicKey;
|
||||
let salt;
|
||||
try {
|
||||
const res = await SRP1({
|
||||
clientPublicKey: clientPublicKey
|
||||
clientPublicKey
|
||||
});
|
||||
serverPublicKey = res.serverPublicKey;
|
||||
salt = res.salt;
|
||||
@@ -87,9 +88,9 @@ const issueBackupKey = async ({
|
||||
clientProof
|
||||
});
|
||||
|
||||
if (res?.status == 400) {
|
||||
if (res?.status === 400) {
|
||||
setBackupKeyError(true);
|
||||
} else if (res?.status == 200) {
|
||||
} else if (res?.status === 200) {
|
||||
generateBackupPDF({
|
||||
personalName,
|
||||
personalEmail: email,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -29,6 +29,7 @@ export function parseDotEnv(src: ArrayBuffer) {
|
||||
let match;
|
||||
let item: [string, string, string[]] | [] = [];
|
||||
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while ((match = LINE.exec(line)) !== null) {
|
||||
const key = match[1];
|
||||
|
||||
@@ -58,8 +59,8 @@ export function parseDotEnv(src: ArrayBuffer) {
|
||||
})
|
||||
.filter((line) => line.length > 1)
|
||||
.forEach((line) => {
|
||||
const [key, value, comments] = line;
|
||||
object[key as string] = { value, comments };
|
||||
const [key, value, cmnts] = line;
|
||||
object[key as string] = { value, comments: cmnts };
|
||||
});
|
||||
|
||||
return object;
|
||||
|
||||
@@ -3,23 +3,11 @@
|
||||
* @returns
|
||||
*/
|
||||
const guidGenerator = () => {
|
||||
const S4 = function () {
|
||||
const S4 = () => {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
|
||||
};
|
||||
return (
|
||||
S4() +
|
||||
S4() +
|
||||
'-' +
|
||||
S4() +
|
||||
'-' +
|
||||
S4() +
|
||||
'-' +
|
||||
S4() +
|
||||
'-' +
|
||||
S4() +
|
||||
S4() +
|
||||
S4()
|
||||
);
|
||||
return `${S4() + S4()}-${S4()}-${S4()}-${S4()}-${S4()}${S4()}${S4()}`;
|
||||
};
|
||||
|
||||
export default guidGenerator;
|
||||
|
||||
@@ -22,7 +22,7 @@ export const saveTokenToLocalStorage = ({
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
throw new Error(
|
||||
"Unable to send the tokens in local storage:" + err.message
|
||||
`Unable to send the tokens in local storage:${ err.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,32 @@
|
||||
import { SecretDataProps } from "public/data/frequentInterfaces";
|
||||
import { SecretDataProps } from 'public/data/frequentInterfaces';
|
||||
|
||||
/**
|
||||
* This function downloads the secrets as a .env file
|
||||
* @param {object} obj
|
||||
* @param {object} obj
|
||||
* @param {SecretDataProps[]} obj.data - secrets that we want to check for overrides
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
const checkOverrides = async ({ data }: { data: SecretDataProps[]; }) => {
|
||||
let secrets : SecretDataProps[] = data!.map((secret) => Object.create(secret));
|
||||
const overridenSecrets = data!.filter(
|
||||
(secret) => (secret.valueOverride == undefined || secret?.value != secret?.valueOverride) ? 'shared' : 'personal'
|
||||
const checkOverrides = async ({ data }: { data: SecretDataProps[] }) => {
|
||||
let secrets: SecretDataProps[] = data!.map((secret) => Object.create(secret));
|
||||
const overridenSecrets = data!.filter((secret) =>
|
||||
secret.valueOverride === undefined || secret?.value !== secret?.valueOverride
|
||||
? 'shared'
|
||||
: 'personal'
|
||||
);
|
||||
if (overridenSecrets.length) {
|
||||
overridenSecrets.forEach((secret) => {
|
||||
const index = secrets!.findIndex(
|
||||
(_secret) => _secret.key === secret.key && (secret.valueOverride == undefined || secret?.value != secret?.valueOverride)
|
||||
(_secret) =>
|
||||
_secret.key === secret.key &&
|
||||
(secret.valueOverride === undefined || secret?.value !== secret?.valueOverride)
|
||||
);
|
||||
secrets![index].value = secret.value;
|
||||
});
|
||||
secrets = secrets!.filter((secret) => (secret.valueOverride == undefined || secret?.value != secret?.valueOverride));
|
||||
secrets = secrets!.filter(
|
||||
(secret) => secret.valueOverride === undefined || secret?.value !== secret?.valueOverride
|
||||
);
|
||||
}
|
||||
return secrets;
|
||||
}
|
||||
};
|
||||
|
||||
export default checkOverrides;
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { envMapping } from "public/data/frequentConstants";
|
||||
import { SecretDataProps } from "public/data/frequentInterfaces";
|
||||
import { SecretDataProps } from 'public/data/frequentInterfaces';
|
||||
|
||||
import checkOverrides from './checkOverrides';
|
||||
|
||||
|
||||
/**
|
||||
* This function downloads the secrets as a .env file
|
||||
* @param {object} obj
|
||||
* @param {object} obj
|
||||
* @param {SecretDataProps[]} obj.data - secrets that we want to download
|
||||
* @param {string} obj.env - the environment which we're downloading (used for naming the file)
|
||||
*/
|
||||
const downloadDotEnv = async ({ data, env }: { data: SecretDataProps[]; env: string; }) => {
|
||||
const downloadDotEnv = async ({ data, env }: { data: SecretDataProps[]; env: string }) => {
|
||||
if (!data) return;
|
||||
const secrets = await checkOverrides({ data });
|
||||
|
||||
@@ -19,21 +17,21 @@ const downloadDotEnv = async ({ data, env }: { data: SecretDataProps[]; env: str
|
||||
(item: SecretDataProps) =>
|
||||
`${
|
||||
item.comment
|
||||
? item.comment
|
||||
? `${item.comment
|
||||
.split('\n')
|
||||
.map((comment) => '# '.concat(comment))
|
||||
.join('\n') + '\n'
|
||||
.join('\n')}\n`
|
||||
: ''
|
||||
}` + [item.key, item.value].join('=')
|
||||
}${[item.key, item.value].join('=')}`
|
||||
)
|
||||
.join('\n');
|
||||
|
||||
const blob = new Blob([file]);
|
||||
const fileDownloadUrl = URL.createObjectURL(blob);
|
||||
const alink = document.createElement('a');
|
||||
alink.href = fileDownloadUrl;
|
||||
alink.download = env + '.env';
|
||||
alink.click();
|
||||
}
|
||||
const blob = new Blob([file]);
|
||||
const fileDownloadUrl = URL.createObjectURL(blob);
|
||||
const alink = document.createElement('a');
|
||||
alink.href = fileDownloadUrl;
|
||||
alink.download = `${env}.env`;
|
||||
alink.click();
|
||||
};
|
||||
|
||||
export default downloadDotEnv;
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
// import YAML from 'yaml';
|
||||
// import { YAMLSeq } from 'yaml/types';
|
||||
|
||||
import { SecretDataProps } from "public/data/frequentInterfaces";
|
||||
import { SecretDataProps } from 'public/data/frequentInterfaces';
|
||||
|
||||
// import { envMapping } from "../../../public/data/frequentConstants";
|
||||
// import checkOverrides from './checkOverrides';
|
||||
|
||||
|
||||
/**
|
||||
* This function downloads the secrets as a .yml file
|
||||
* @param {object} obj
|
||||
* @param {object} obj
|
||||
* @param {SecretDataProps[]} obj.data - secrets that we want to download
|
||||
* @param {string} obj.env - used for naming the file
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
const downloadYaml = async ({ data, env }: { data: SecretDataProps[]; env: string; }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const downloadYaml = async ({ data, env }: { data: SecretDataProps[]; env: string }) => {
|
||||
// if (!data) return;
|
||||
// const doc = new YAML.Document();
|
||||
// doc.contents = new YAMLSeq();
|
||||
@@ -27,20 +27,17 @@ const downloadYaml = async ({ data, env }: { data: SecretDataProps[]; env: strin
|
||||
// .join('\n');
|
||||
// doc.add(pair);
|
||||
// });
|
||||
|
||||
// const file = doc
|
||||
// .toString()
|
||||
// .split('\n')
|
||||
// .map((line) => (line.startsWith('-') ? line.replace('- ', '') : line))
|
||||
// .join('\n');
|
||||
|
||||
// const blob = new Blob([file]);
|
||||
// const fileDownloadUrl = URL.createObjectURL(blob);
|
||||
// const alink = document.createElement('a');
|
||||
// alink.href = fileDownloadUrl;
|
||||
// alink.download = envMapping[env] + '.yml';
|
||||
// alink.click();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
export default downloadYaml;
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import { SecretDataProps } from "public/data/frequentInterfaces";
|
||||
import crypto from 'crypto';
|
||||
|
||||
import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";
|
||||
|
||||
const crypto = require("crypto");
|
||||
const {
|
||||
decryptAssymmetric,
|
||||
encryptSymmetric,
|
||||
} = require("../cryptography/crypto");
|
||||
const nacl = require("tweetnacl");
|
||||
nacl.util = require("tweetnacl-util");
|
||||
import getLatestFileKey from '@app/pages/api/workspace/getLatestFileKey';
|
||||
import { SecretDataProps } from 'public/data/frequentInterfaces';
|
||||
|
||||
import { decryptAssymmetric, encryptSymmetric } from '../cryptography/crypto';
|
||||
|
||||
interface EncryptedSecretProps {
|
||||
id: string;
|
||||
@@ -24,22 +18,30 @@ interface EncryptedSecretProps {
|
||||
secretValueCiphertext: string;
|
||||
secretValueIV: string;
|
||||
secretValueTag: string;
|
||||
type: "personal" | "shared";
|
||||
type: 'personal' | 'shared';
|
||||
}
|
||||
|
||||
/**
|
||||
* Encypt secrets before pushing the to the DB
|
||||
* @param {object} obj
|
||||
* @param {object} obj
|
||||
* @param {object} obj.secretsToEncrypt - secrets that we want to encrypt
|
||||
* @param {object} obj.workspaceId - the id of a project in which we are encrypting secrets
|
||||
* @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;
|
||||
}) => {
|
||||
let secrets;
|
||||
try {
|
||||
const sharedKey = await getLatestFileKey({ workspaceId });
|
||||
|
||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY");
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
|
||||
|
||||
let randomBytes: string;
|
||||
if (Object.keys(sharedKey).length > 0) {
|
||||
@@ -48,42 +50,42 @@ const encryptSecrets = async ({ secretsToEncrypt, workspaceId, env }: { secretsT
|
||||
ciphertext: sharedKey.latestKey.encryptedKey,
|
||||
nonce: sharedKey.latestKey.nonce,
|
||||
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");
|
||||
randomBytes = crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
|
||||
|
||||
secrets = secretsToEncrypt.map((secret) => {
|
||||
// encrypt key
|
||||
const {
|
||||
ciphertext: secretKeyCiphertext,
|
||||
iv: secretKeyIV,
|
||||
tag: secretKeyTag,
|
||||
tag: secretKeyTag
|
||||
} = encryptSymmetric({
|
||||
plaintext: secret.key,
|
||||
key: randomBytes,
|
||||
key: randomBytes
|
||||
});
|
||||
|
||||
// encrypt value
|
||||
const {
|
||||
ciphertext: secretValueCiphertext,
|
||||
iv: secretValueIV,
|
||||
tag: secretValueTag,
|
||||
tag: secretValueTag
|
||||
} = encryptSymmetric({
|
||||
plaintext: secret.value,
|
||||
key: randomBytes,
|
||||
key: randomBytes
|
||||
});
|
||||
|
||||
// encrypt comment
|
||||
const {
|
||||
ciphertext: secretCommentCiphertext,
|
||||
iv: secretCommentIV,
|
||||
tag: secretCommentTag,
|
||||
tag: secretCommentTag
|
||||
} = encryptSymmetric({
|
||||
plaintext: secret.comment ?? '',
|
||||
key: randomBytes,
|
||||
key: randomBytes
|
||||
});
|
||||
|
||||
const result: EncryptedSecretProps = {
|
||||
@@ -99,17 +101,19 @@ const encryptSecrets = async ({ secretsToEncrypt, workspaceId, env }: { secretsT
|
||||
secretCommentCiphertext,
|
||||
secretCommentIV,
|
||||
secretCommentTag,
|
||||
type: (secret.valueOverride == undefined || secret?.value != secret?.valueOverride) ? 'shared' : 'personal',
|
||||
type:
|
||||
secret.valueOverride === undefined || secret?.value !== secret?.valueOverride
|
||||
? 'shared'
|
||||
: 'personal'
|
||||
};
|
||||
|
||||
return result;
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error while encrypting secrets");
|
||||
console.log('Error while encrypting secrets');
|
||||
}
|
||||
|
||||
return secrets;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
export default encryptSecrets;
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import getSecrets from '~/pages/api/files/GetSecrets';
|
||||
import getLatestFileKey from '~/pages/api/workspace/getLatestFileKey';
|
||||
import getSecrets from '@app/pages/api/files/GetSecrets';
|
||||
import getLatestFileKey from '@app/pages/api/workspace/getLatestFileKey';
|
||||
|
||||
const {
|
||||
decryptAssymmetric,
|
||||
decryptSymmetric
|
||||
} = require('../cryptography/crypto');
|
||||
const nacl = require('tweetnacl');
|
||||
nacl.util = require('tweetnacl-util');
|
||||
import { decryptAssymmetric, decryptSymmetric } from '../cryptography/crypto';
|
||||
|
||||
interface EncryptedSecretProps {
|
||||
_id: string;
|
||||
@@ -21,14 +16,14 @@ interface EncryptedSecretProps {
|
||||
secretValueCiphertext: string;
|
||||
secretValueIV: string;
|
||||
secretValueTag: string;
|
||||
type: "personal" | "shared";
|
||||
type: 'personal' | 'shared';
|
||||
}
|
||||
|
||||
interface SecretProps {
|
||||
key: string;
|
||||
value: string;
|
||||
type: 'personal' | 'shared';
|
||||
comment: string;
|
||||
interface SecretProps {
|
||||
key: string;
|
||||
value: string;
|
||||
type: 'personal' | 'shared';
|
||||
comment: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
@@ -61,11 +56,11 @@ const getSecretsForProject = async ({
|
||||
console.log('ERROR: Not able to access the latest version of secrets');
|
||||
}
|
||||
|
||||
const latestKey = await getLatestFileKey({ workspaceId })
|
||||
const latestKey = await getLatestFileKey({ workspaceId });
|
||||
// This is called isKeyAvailable but what it really means is if a person is able to create new key pairs
|
||||
setIsKeyAvailable(!latestKey ? encryptedSecrets.length == 0 : true);
|
||||
setIsKeyAvailable(!latestKey ? encryptedSecrets.length === 0 : true);
|
||||
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
|
||||
|
||||
const tempDecryptedSecrets: SecretProps[] = [];
|
||||
if (latestKey) {
|
||||
@@ -78,7 +73,7 @@ const getSecretsForProject = async ({
|
||||
});
|
||||
|
||||
// decrypt secret keys, values, and comments
|
||||
encryptedSecrets.map((secret: EncryptedSecretProps) => {
|
||||
encryptedSecrets.forEach((secret: EncryptedSecretProps) => {
|
||||
const plainTextKey = decryptSymmetric({
|
||||
ciphertext: secret.secretKeyCiphertext,
|
||||
iv: secret.secretKeyIV,
|
||||
@@ -102,7 +97,7 @@ const getSecretsForProject = async ({
|
||||
key
|
||||
});
|
||||
} else {
|
||||
plainTextComment = "";
|
||||
plainTextComment = '';
|
||||
}
|
||||
|
||||
tempDecryptedSecrets.push({
|
||||
@@ -115,20 +110,26 @@ const getSecretsForProject = async ({
|
||||
});
|
||||
}
|
||||
|
||||
const secretKeys = [...new Set(tempDecryptedSecrets.map(secret => secret.key))];
|
||||
const secretKeys = [...new Set(tempDecryptedSecrets.map((secret) => secret.key))];
|
||||
|
||||
|
||||
const result = secretKeys.map((key, index) => {
|
||||
return {
|
||||
id: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'shared')[0]?.id,
|
||||
idOverride: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'personal')[0]?.id,
|
||||
pos: index,
|
||||
key: key,
|
||||
value: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'shared')[0]?.value,
|
||||
valueOverride: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'personal')[0]?.value,
|
||||
comment: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'shared')[0]?.comment,
|
||||
}
|
||||
});
|
||||
const result = secretKeys.map((key, index) => ({
|
||||
id: tempDecryptedSecrets.filter((secret) => secret.key === key && secret.type === 'shared')[0]
|
||||
?.id,
|
||||
idOverride: tempDecryptedSecrets.filter(
|
||||
(secret) => secret.key === key && secret.type === 'personal'
|
||||
)[0]?.id,
|
||||
pos: index,
|
||||
key,
|
||||
value: tempDecryptedSecrets.filter(
|
||||
(secret) => secret.key === key && secret.type === 'shared'
|
||||
)[0]?.value,
|
||||
valueOverride: tempDecryptedSecrets.filter(
|
||||
(secret) => secret.key === key && secret.type === 'personal'
|
||||
)[0]?.value,
|
||||
comment: tempDecryptedSecrets.filter(
|
||||
(secret) => secret.key === key && secret.type === 'shared'
|
||||
)[0]?.comment
|
||||
}));
|
||||
|
||||
setData(result);
|
||||
return result;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable */
|
||||
import { PostHog } from 'posthog-js';
|
||||
import { initPostHog } from '~/components/analytics/posthog';
|
||||
import { ENV } from '~/components/utilities/config';
|
||||
import { initPostHog } from '@app/components/analytics/posthog';
|
||||
import { ENV } from '@app/components/utilities/config';
|
||||
|
||||
declare let TELEMETRY_CAPTURING_ENABLED: any;
|
||||
|
||||
@@ -13,7 +13,7 @@ class Capturer {
|
||||
}
|
||||
|
||||
capture(item: string) {
|
||||
if (ENV == 'production' && TELEMETRY_CAPTURING_ENABLED) {
|
||||
if (ENV === 'production' && TELEMETRY_CAPTURING_ENABLED) {
|
||||
try {
|
||||
this.api.capture(item);
|
||||
} catch (error) {
|
||||
@@ -23,7 +23,7 @@ class Capturer {
|
||||
}
|
||||
|
||||
identify(id: string) {
|
||||
if (ENV == 'production' && TELEMETRY_CAPTURING_ENABLED) {
|
||||
if (ENV === 'production' && TELEMETRY_CAPTURING_ENABLED) {
|
||||
try {
|
||||
this.api.identify(id);
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user