Begin refactoring backfilling and preparation operations into setup and start adding encryption metadata to models

This commit is contained in:
Tuan Dang
2023-05-03 14:21:42 +03:00
parent b462ca3e89
commit 099cee7f39
37 changed files with 797 additions and 619 deletions

View File

@@ -1,4 +1,5 @@
import InfisicalClient from 'infisical-node';
import { validateEncryptionKey } from '../validation';
const client = new InfisicalClient({
token: process.env.INFISICAL_TOKEN!
@@ -6,7 +7,18 @@ const client = new InfisicalClient({
export const getPort = async () => (await client.getSecret('PORT')).secretValue || 4000;
export const getInviteOnlySignup = async () => (await client.getSecret('INVITE_ONLY_SIGNUP')).secretValue == undefined ? false : (await client.getSecret('INVITE_ONLY_SIGNUP')).secretValue;
export const getEncryptionKey = async () => (await client.getSecret('ENCRYPTION_KEY')).secretValue;
export const getEncryptionKey = async () => (await client.getSecret('ENCRYPTION_KEY')).secretValue; // TODO: deprecate in favor of INFISICAL_ENCRYPTION_KEY
export const getRootEncryptionKey = async (): Promise<string | undefined> => {
const encryptionKey = (await client.getSecret('ROOT_ENCRYPTION_KEY')).secretValue;
if (encryptionKey) {
// validate [encryptionKey] to make sure it is in base64 format and 256-bit
validateEncryptionKey(encryptionKey);
return encryptionKey;
}
return encryptionKey;
}
export const getSaltRounds = async () => parseInt((await client.getSecret('SALT_ROUNDS')).secretValue) || 10;
export const getJwtAuthLifetime = async () => (await client.getSecret('JWT_AUTH_LIFETIME')).secretValue || '10d';
export const getJwtAuthSecret = async () => (await client.getSecret('JWT_AUTH_SECRET')).secretValue;

View File

@@ -93,52 +93,8 @@ const markDeletedSecretVersionsHelper = async ({
);
};
/**
* Initialize secret versioning by setting previously unversioned
* secrets to version 1 and begin populating secret versions.
*/
const initSecretVersioningHelper = async () => {
await Secret.updateMany(
{ version: { $exists: false } },
{ $set: { version: 1 } }
);
const unversionedSecrets: ISecret[] = await Secret.aggregate([
{
$lookup: {
from: "secretversions",
localField: "_id",
foreignField: "secret",
as: "versions",
},
},
{
$match: {
versions: { $size: 0 },
},
},
]);
if (unversionedSecrets.length > 0) {
await addSecretVersionsHelper({
secretVersions: unversionedSecrets.map(
(s, idx) =>
new SecretVersion({
...s,
secret: s._id,
version: s.version ? s.version : 1,
isDeleted: false,
workspace: s.workspace,
environment: s.environment,
})
),
});
}
};
export {
takeSecretSnapshotHelper,
addSecretVersionsHelper,
markDeletedSecretVersionsHelper,
initSecretVersioningHelper,
markDeletedSecretVersionsHelper
};

View File

@@ -3,8 +3,7 @@ import { ISecretVersion } from '../models';
import {
takeSecretSnapshotHelper,
addSecretVersionsHelper,
markDeletedSecretVersionsHelper,
initSecretVersioningHelper
markDeletedSecretVersionsHelper
} from '../helpers/secret';
import EELicenseService from './EELicenseService';
@@ -64,15 +63,6 @@ class EESecretService {
secretIds
});
}
/**
* Initialize secret versioning by setting previously unversioned
* secrets to version 1 and begin populating secret versions.
*/
static async initSecretVersioning() {
if (!EELicenseService.isLicenseValid) return;
await initSecretVersioningHelper();
}
}
export default EESecretService;

View File

@@ -13,10 +13,10 @@ import {
} from "../models";
import {
generateKeyPair,
encryptSymmetric,
decryptSymmetric,
decryptAsymmetric,
} from "../utils/crypto";
encryptSymmetric128BitHexKeyUTF8,
decryptSymmetric128BitHexKeyUTF8,
decryptAsymmetric
} from '../utils/crypto';
import {
SECRET_SHARED,
AUTH_MODE_JWT,
@@ -26,7 +26,6 @@ import {
} from "../variables";
import { getEncryptionKey } from "../config";
import { BotNotFoundError, UnauthorizedRequestError } from "../utils/errors";
import { validateMembership } from "../helpers/membership";
import { validateUserClientForWorkspace } from "../helpers/user";
import { validateServiceAccountClientForWorkspace } from "../helpers/serviceAccount";
@@ -120,7 +119,7 @@ const createBot = async ({
workspaceId: Types.ObjectId;
}) => {
const { publicKey, privateKey } = generateKeyPair();
const { ciphertext, iv, tag } = encryptSymmetric({
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({
plaintext: privateKey,
key: await getEncryptionKey(),
});
@@ -161,14 +160,14 @@ const getSecretsHelper = async ({
});
secrets.forEach((secret: ISecret) => {
const secretKey = decryptSymmetric({
const secretKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key,
});
const secretValue = decryptSymmetric({
const secretValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
@@ -202,7 +201,7 @@ const getKey = async ({ workspaceId }: { workspaceId: string }) => {
if (!bot) throw new Error("Failed to find bot");
if (!bot.isActive) throw new Error("Bot is not active");
const privateKeyBot = decryptSymmetric({
const privateKeyBot = decryptSymmetric128BitHexKeyUTF8({
ciphertext: bot.encryptedPrivateKey,
iv: bot.iv,
tag: bot.tag,
@@ -234,7 +233,7 @@ const encryptSymmetricHelper = async ({
plaintext: string;
}) => {
const key = await getKey({ workspaceId: workspaceId.toString() });
const { ciphertext, iv, tag } = encryptSymmetric({
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({
plaintext,
key,
});
@@ -266,7 +265,7 @@ const decryptSymmetricHelper = async ({
tag: string;
}) => {
const key = await getKey({ workspaceId: workspaceId.toString() });
const plaintext = decryptSymmetric({
const plaintext = decryptSymmetric128BitHexKeyUTF8({
ciphertext,
iv,
tag,
@@ -281,5 +280,5 @@ export {
createBot,
getSecretsHelper,
encryptSymmetricHelper,
decryptSymmetricHelper,
decryptSymmetricHelper
};

View File

@@ -1,6 +1,4 @@
import mongoose from 'mongoose';
import { EESecretService } from '../ee/services';
import { SecretService } from '../services';
import { getLogger } from '../utils/logger';
/**
@@ -21,9 +19,7 @@ const initDatabaseHelper = async ({
mongoose.Schema.Types.String.checkRequired(v => typeof v === 'string');
(await getLogger("database")).info("Database connection established");
await EESecretService.initSecretVersioning();
await SecretService.initSecretBlindIndexDataHelper();
} catch (err) {
(await getLogger("database")).error(`Unable to establish Database connection due to the error.\n${err}`);
}

View File

@@ -54,9 +54,9 @@ import {
} from '../variables';
import crypto from 'crypto';
import * as argon2 from 'argon2';
import {
encryptSymmetric,
decryptSymmetric
import {
encryptSymmetric128BitHexKeyUTF8,
decryptSymmetric128BitHexKeyUTF8
} from '../utils/crypto';
import { getEncryptionKey } from '../config';
import { TelemetryService } from '../services';
@@ -220,48 +220,6 @@ const validateClientForSecrets = async ({
});
}
/**
* Initialize secret blind index data by setting previously
* un-initialized projects to have secret blind index data
* (Ensures that all projects have associated blind index data)
*/
const initSecretBlindIndexDataHelper = async () => {
const workspaceIdsBlindIndexed = await SecretBlindIndexData.distinct('workspace');
const workspaceIdsToBlindIndex = await Workspace.distinct('_id', {
_id: {
$nin: workspaceIdsBlindIndexed
}
});
const secretBlindIndexDataToInsert = await Promise.all(
workspaceIdsToBlindIndex.map(async (workspaceToBlindIndex) => {
const salt = crypto.randomBytes(16).toString('base64');
const {
ciphertext: encryptedSaltCiphertext,
iv: saltIV,
tag: saltTag
} = encryptSymmetric({
plaintext: salt,
key: await getEncryptionKey()
});
const secretBlindIndexData = new SecretBlindIndexData({
workspace: workspaceToBlindIndex,
encryptedSaltCiphertext,
saltIV,
saltTag
})
return secretBlindIndexData;
})
);
if (secretBlindIndexDataToInsert.length > 0) {
await SecretBlindIndexData.insertMany(secretBlindIndexDataToInsert);
}
}
/**
* Create secret blind index data containing encrypted blind index [salt]
* for workspace with id [workspaceId]
@@ -280,7 +238,7 @@ const createSecretBlindIndexDataHelper = async ({
ciphertext: encryptedSaltCiphertext,
iv: saltIV,
tag: saltTag
} = encryptSymmetric({
} = encryptSymmetric128BitHexKeyUTF8({
plaintext: salt,
key: await getEncryptionKey()
});
@@ -314,7 +272,7 @@ const getSecretBlindIndexSaltHelper = async ({
if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError();
// decrypt workspace salt
const salt = decryptSymmetric({
const salt = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
iv: secretBlindIndexData.saltIV,
tag: secretBlindIndexData.saltTag,
@@ -376,7 +334,7 @@ const generateSecretBlindIndexHelper = async ({
if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError();
// decrypt workspace salt
const salt = decryptSymmetric({
const salt = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
iv: secretBlindIndexData.saltIV,
tag: secretBlindIndexData.saltTag,
@@ -934,7 +892,6 @@ const deleteSecretHelper = async ({
export {
validateClientForSecret,
validateClientForSecrets,
initSecretBlindIndexDataHelper,
createSecretBlindIndexDataHelper,
getSecretBlindIndexSaltHelper,
generateSecretBlindIndexWithSaltHelper,

View File

@@ -28,7 +28,6 @@ import {
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_API_KEY
} from '../variables';
import { encryptSymmetric } from '../utils/crypto';
import { SecretService } from '../services';
/**

View File

@@ -4,15 +4,9 @@ dotenv.config();
import express from 'express';
import helmet from 'helmet';
import cors from 'cors';
import * as Sentry from '@sentry/node';
import { DatabaseService } from './services';
import { setUpHealthEndpoint } from './services/health';
import { initSmtp } from './services/smtp';
import { TelemetryService } from './services';
import { setTransporter } from './helpers/nodemailer';
import { createTestUserForDevelopment } from './utils/addDevelopmentUser';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { patchRouterParam } = require('./utils/patchAsyncRoutes');
import cookieParser from 'cookie-parser';
import swaggerUi = require('swagger-ui-express');
@@ -77,22 +71,13 @@ import {
getSiteURL,
getSmtpHost
} from './config';
import { setup } from './utils/setup';
const main = async () => {
TelemetryService.logTelemetryMessage();
setTransporter(await initSmtp());
await DatabaseService.initDatabase(await getMongoURL());
if ((await getNodeEnv()) !== 'test') {
Sentry.init({
dsn: await getSentryDSN(),
tracesSampleRate: 1.0,
debug: await getNodeEnv() === 'production' ? false : true,
environment: await getNodeEnv()
});
}
await setup();
patchRouterParam();
const app = express();
app.enable('trust proxy');
app.use(express.json());
@@ -176,7 +161,7 @@ const main = async () => {
(await getLogger("backend-main")).info(`Server started listening at port ${await getPort()}`)
});
await createTestUserForDevelopment();
// await createTestUserForDevelopment();
setUpHealthEndpoint(server);
server.on('close', async () => {

View File

@@ -0,0 +1,41 @@
export interface IGenerateKeyPairOutput {
publicKey: string;
privateKey: string
}
export interface IEncryptAsymmetricInput {
plaintext: string;
publicKey: string;
privateKey: string;
}
export interface IEncryptAsymmetricOutput {
ciphertext: string;
nonce: string;
}
export interface IDecryptAsymmetricInput {
ciphertext: string;
nonce: string;
publicKey: string;
privateKey: string;
}
export interface IEncryptSymmetricInput {
plaintext: string;
key: string;
}
export interface IEncryptSymmetricOutput {
ciphertext: string;
iv: string;
tag: string;
}
export interface IDecryptSymmetricInput {
ciphertext: string;
iv: string;
tag: string;
key: string;
}

View File

@@ -0,0 +1 @@
export * from './crypto';

View File

@@ -1,4 +1,9 @@
import { Schema, model, Types } from 'mongoose';
import {
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
ENCODING_SCHEME_BASE64
} from '../variables';
export interface IBackupPrivateKey {
_id: Types.ObjectId;
@@ -7,6 +12,9 @@ export interface IBackupPrivateKey {
iv: string;
tag: string;
salt: string;
algorithm: string;
keySize: number;
keyEncoding: 'base64' | 'utf8';
verifier: string;
}
@@ -32,6 +40,24 @@ const backupPrivateKeySchema = new Schema<IBackupPrivateKey>(
select: false,
required: true
},
algorithm: { // the encryption algorithm used
type: String,
enum: [ALGORITHM_AES_256_GCM],
required: true
},
keySize: { // the size of the key used in the algorithm
type: Number,
enum: [256],
required: true
},
keyEncoding: {
type: String,
enum: [
ENCODING_SCHEME_UTF8,
ENCODING_SCHEME_BASE64
],
required: true
},
salt: {
type: String,
select: false,

View File

@@ -1,4 +1,10 @@
import { Schema, model, Types } from 'mongoose';
import {
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
ENCODING_SCHEME_HEX,
ENCODING_SCHEME_BASE64
} from '../variables';
export interface IBot {
_id: Types.ObjectId;
@@ -9,6 +15,9 @@ export interface IBot {
encryptedPrivateKey: string;
iv: string;
tag: string;
algorithm: 'aes-256-gcm';
keySize: 256;
keyEncoding: 'base64' | 'utf8';
}
const botSchema = new Schema<IBot>(
@@ -45,6 +54,24 @@ const botSchema = new Schema<IBot>(
type: String,
required: true,
select: false
},
algorithm: { // the encryption algorithm used
type: String,
enum: [ALGORITHM_AES_256_GCM],
required: true
},
keySize: { // the size of the key used in the algorithm
type: Number,
enum: [256],
required: true
},
keyEncoding: {
type: String,
enum: [
ENCODING_SCHEME_UTF8,
ENCODING_SCHEME_BASE64
],
required: true
}
},
{

View File

@@ -14,6 +14,9 @@ import {
INTEGRATION_CIRCLECI,
INTEGRATION_TRAVISCI,
INTEGRATION_SUPABASE,
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
ENCODING_SCHEME_BASE64
} from "../variables";
export interface IIntegrationAuth extends Document {
@@ -31,6 +34,9 @@ export interface IIntegrationAuth extends Document {
accessCiphertext?: string;
accessIV?: string;
accessTag?: string;
algorithm?: 'aes-256-gcm';
keySize?: 256;
keyEncoding: 'utf8' | 'base64';
accessExpiresAt?: Date;
}
@@ -109,6 +115,24 @@ const integrationAuthSchema = new Schema<IIntegrationAuth>(
type: Date,
select: false,
},
algorithm: { // the encryption algorithm used
type: String,
enum: [ALGORITHM_AES_256_GCM],
required: true
},
keySize: { // the size of the key used in the algorithm
type: Number,
enum: [256],
required: true
},
keyEncoding: {
type: String,
enum: [
ENCODING_SCHEME_UTF8,
ENCODING_SCHEME_BASE64
],
required: true
}
},
{
timestamps: true,

View File

@@ -1,4 +1,9 @@
import { Schema, model, Types, Document } from 'mongoose';
import {
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
ENCODING_SCHEME_BASE64
} from '../variables';
export interface ISecretBlindIndexData extends Document {
_id: Types.ObjectId;
@@ -6,6 +11,9 @@ export interface ISecretBlindIndexData extends Document {
encryptedSaltCiphertext: string;
saltIV: string;
saltTag: string;
algorithm: 'aes-256-gcm';
keySize: 256;
keyEncoding: 'base64' | 'utf8'
}
const secretBlindIndexDataSchema = new Schema<ISecretBlindIndexData>(
@@ -26,7 +34,26 @@ const secretBlindIndexDataSchema = new Schema<ISecretBlindIndexData>(
saltTag: {
type: String,
required: true
},
algorithm: {
type: String,
enum: [ALGORITHM_AES_256_GCM],
required: true
},
keySize: {
type: Number,
enum: [256],
required: true
},
keyEncoding: {
type: String,
enum: [
ENCODING_SCHEME_UTF8,
ENCODING_SCHEME_BASE64
],
required: true
}
}
);

View File

@@ -1,4 +1,3 @@
// WIP
import { Types } from 'mongoose';
import {
ISecret
@@ -11,7 +10,6 @@ import {
DeleteSecretParams
} from '../interfaces/services/SecretService';
import {
initSecretBlindIndexDataHelper,
createSecretBlindIndexDataHelper,
getSecretBlindIndexSaltHelper,
generateSecretBlindIndexWithSaltHelper,
@@ -24,16 +22,6 @@ import {
} from '../helpers/secrets';
class SecretService {
/**
*
* @param param0 h
* @returns
*/
static async initSecretBlindIndexDataHelper() {
return await initSecretBlindIndexDataHelper();
}
/**
* Create secret blind index data containing encrypted blind index salt

View File

@@ -1,139 +0,0 @@
import nacl from 'tweetnacl';
import util from 'tweetnacl-util';
import AesGCM from './aes-gcm';
/**
* Return new base64, NaCl, public-private key pair.
* @returns {Object} obj
* @returns {String} obj.publicKey - base64, NaCl, public key
* @returns {String} obj.privateKey - base64, NaCl, private key
*/
const generateKeyPair = () => {
const pair = nacl.box.keyPair();
return ({
publicKey: util.encodeBase64(pair.publicKey),
privateKey: util.encodeBase64(pair.secretKey)
});
}
/**
* Return assymmetrically encrypted [plaintext] using [publicKey] where
* [publicKey] likely belongs to the recipient.
* @param {Object} obj
* @param {String} obj.plaintext - plaintext to encrypt
* @param {String} obj.publicKey - public key of the recipient
* @param {String} obj.privateKey - private key of the sender (current user)
* @returns {Object} obj
* @returns {String} ciphertext - base64-encoded ciphertext
* @returns {String} nonce - base64-encoded nonce
*/
const encryptAsymmetric = ({
plaintext,
publicKey,
privateKey
}: {
plaintext: string;
publicKey: string;
privateKey: string;
}) => {
const nonce = nacl.randomBytes(24);
const ciphertext = nacl.box(
util.decodeUTF8(plaintext),
nonce,
util.decodeBase64(publicKey),
util.decodeBase64(privateKey)
);
return {
ciphertext: util.encodeBase64(ciphertext),
nonce: util.encodeBase64(nonce)
};
};
/**
* Return assymmetrically decrypted [ciphertext] using [privateKey] where
* [privateKey] likely belongs to the recipient.
* @param {Object} obj
* @param {String} obj.ciphertext - ciphertext to decrypt
* @param {String} obj.nonce - nonce
* @param {String} obj.publicKey - public key of the sender
* @param {String} obj.privateKey - private key of the receiver (current user)
* @param {String} plaintext - UTF8 plaintext
*/
const decryptAsymmetric = ({
ciphertext,
nonce,
publicKey,
privateKey
}: {
ciphertext: string;
nonce: string;
publicKey: string;
privateKey: string;
}): string => {
const plaintext: any = nacl.box.open(
util.decodeBase64(ciphertext),
util.decodeBase64(nonce),
util.decodeBase64(publicKey),
util.decodeBase64(privateKey)
);
return util.encodeUTF8(plaintext);
};
/**
* Return symmetrically encrypted [plaintext] using [key].
* @param {Object} obj
* @param {String} obj.plaintext - plaintext to encrypt
* @param {String} obj.key - hex key
*/
const encryptSymmetric = ({
plaintext,
key
}: {
plaintext: string;
key: string;
}) => {
const obj = AesGCM.encrypt(plaintext, key);
const { ciphertext, iv, tag } = obj;
return {
ciphertext,
iv,
tag
};
};
/**
* Return symmetrically decypted [ciphertext] using [iv], [tag],
* and [key].
* @param {Object} obj
* @param {String} obj.ciphertext - ciphertext to decrypt
* @param {String} obj.iv - iv
* @param {String} obj.tag - tag
* @param {String} obj.key - hex key
*
*/
const decryptSymmetric = ({
ciphertext,
iv,
tag,
key
}: {
ciphertext: string;
iv: string;
tag: string;
key: string;
}): string => {
const plaintext = AesGCM.decrypt(ciphertext, iv, tag, key);
return plaintext;
};
export {
generateKeyPair,
encryptAsymmetric,
decryptAsymmetric,
encryptSymmetric,
decryptSymmetric
};

View File

@@ -0,0 +1,237 @@
import crypto from 'crypto';
import nacl from 'tweetnacl';
import util from 'tweetnacl-util';
import {
IGenerateKeyPairOutput,
IEncryptAsymmetricInput,
IEncryptAsymmetricOutput,
IDecryptAsymmetricInput,
IEncryptSymmetricInput,
IEncryptSymmetricOutput,
IDecryptSymmetricInput
} from '../../interfaces/utils';
import {
BadRequestError,
InternalServerError
} from '../errors';
import {
ALGORITHM_AES_256_GCM,
BLOCK_SIZE_BYTES_32,
BLOCK_SIZE_BYTES_16
} from '../../variables';
import { validateEncryptionKey } from '../../validation';
/**
* Return new base64, NaCl, public-private key pair.
* @returns {Object} obj
* @returns {String} obj.publicKey - (base64) NaCl, public key
* @returns {String} obj.privateKey - (base64), NaCl, private key
*/
const generateKeyPair = (): IGenerateKeyPairOutput => {
const pair = nacl.box.keyPair();
return ({
publicKey: util.encodeBase64(pair.publicKey),
privateKey: util.encodeBase64(pair.secretKey)
});
}
/**
* Return assymmetrically encrypted [plaintext] using [publicKey] where
* [publicKey] likely belongs to the recipient.
* @param {Object} obj
* @param {String} obj.plaintext - plaintext to encrypt
* @param {String} obj.publicKey - (base64) Nacl public key of the recipient
* @param {String} obj.privateKey - (base64) Nacl private key of the sender (current user)
* @returns {Object} obj
* @returns {String} obj.ciphertext - (base64) ciphertext
* @returns {String} obj.nonce - (base64) nonce
*/
const encryptAsymmetric = ({
plaintext,
publicKey,
privateKey
}: IEncryptAsymmetricInput): IEncryptAsymmetricOutput => {
const nonce = nacl.randomBytes(24);
const ciphertext = nacl.box(
util.decodeUTF8(plaintext),
nonce,
util.decodeBase64(publicKey),
util.decodeBase64(privateKey)
);
return {
ciphertext: util.encodeBase64(ciphertext),
nonce: util.encodeBase64(nonce)
};
};
/**
* Return assymmetrically decrypted [ciphertext] using [privateKey] where
* [privateKey] likely belongs to the recipient.
* @param {Object} obj
* @param {String} obj.ciphertext - ciphertext to decrypt
* @param {String} obj.nonce - (base64) nonce
* @param {String} obj.publicKey - (base64) public key of the sender
* @param {String} obj.privateKey - (base64) private key of the receiver (current user)
* @returns {String} plaintext - (utf8) plaintext
*/
const decryptAsymmetric = ({
ciphertext,
nonce,
publicKey,
privateKey
}: IDecryptAsymmetricInput): string => {
const plaintext: Uint8Array | null = nacl.box.open(
util.decodeBase64(ciphertext),
util.decodeBase64(nonce),
util.decodeBase64(publicKey),
util.decodeBase64(privateKey)
);
if (plaintext == null) throw BadRequestError({
message: 'Invalid ciphertext or keys'
});
return util.encodeUTF8(plaintext);
};
/**
* Return symmetrically encrypted [plaintext] using [key].
* @param {Object} obj
* @param {String} obj.plaintext - (utf8) plaintext to encrypt
* @param {String} obj.key - (base64) 256-bit key
* @returns {Object} obj
* @returns {String} obj.ciphertext (base64) ciphertext
* @returns {String} obj.iv (base64) iv
* @returns {String} obj.tag (base64) tag
*/
const encryptSymmetric = ({
plaintext,
key
}: IEncryptSymmetricInput): IEncryptSymmetricOutput => {
validateEncryptionKey(key);
const iv = crypto.randomBytes(BLOCK_SIZE_BYTES_32);
const secretKey = crypto.createSecretKey(key, 'base64');
const cipher = crypto.createCipheriv(ALGORITHM_AES_256_GCM, secretKey, iv);
let ciphertext = cipher.update(plaintext, 'utf8', 'base64');
ciphertext += cipher.final('base64');
return {
ciphertext,
iv: iv.toString('base64'),
tag: cipher.getAuthTag().toString('base64')
};
};
/**
* Return symmetrically decrypted [ciphertext] using [iv], [tag],
* and [key].
* @param {Object} obj
* @param {String} obj.ciphertext - ciphertext to decrypt
* @param {String} obj.iv - (base64) 256-bit iv
* @param {String} obj.tag - (base64) tag
* @param {String} obj.key - (base64) 256-bit key
* @returns {String} cleartext - the deciphered ciphertext
*/
const decryptSymmetric = ({
ciphertext,
iv,
tag,
key
}: IDecryptSymmetricInput): string => {
validateEncryptionKey(key);
const secretKey = crypto.createSecretKey(key, 'base64');
const decipher = crypto.createDecipheriv(
ALGORITHM_AES_256_GCM,
secretKey,
Buffer.from(iv, 'base64')
);
decipher.setAuthTag(Buffer.from(tag, 'base64'));
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
cleartext += decipher.final('utf8');
return cleartext;
};
/**
* Return symmetrically encrypted [plaintext] using [key].
*
* NOTE: THIS FUNCTION SHOULD NOT BE USED FOR ALL FUTURE
* ENCRYPTION OPERATIONS UNLESS IT TOUCHES OLD FUNCTIONALITY
* THAT USES IT. USE encryptSymmetric() instead
*
* @param {Object} obj
* @param {String} obj.plaintext - (utf8) plaintext to encrypt
* @param {String} obj.key - (base64) 256-bit key
* @returns {Object} obj
* @returns {String} obj.ciphertext (base64) ciphertext
* @returns {String} obj.iv (base64) iv
* @returns {String} obj.tag (base64) tag
*/
const encryptSymmetric128BitHexKeyUTF8 = ({
plaintext,
key
}: IEncryptSymmetricInput) => {
const iv = crypto.randomBytes(BLOCK_SIZE_BYTES_16);
const cipher = crypto.createCipheriv(ALGORITHM_AES_256_GCM, key, iv);
let ciphertext = cipher.update(plaintext, 'utf8', 'base64');
ciphertext += cipher.final('base64');
return {
ciphertext,
iv: iv.toString('base64'),
tag: cipher.getAuthTag().toString('base64')
};
}
/**
* Return symmetrically decrypted [ciphertext] using [iv], [tag],
* and [key].
*
* NOTE: THIS FUNCTION SHOULD NOT BE USED FOR ALL FUTURE
* DECRYPTION OPERATIONS UNLESS IT TOUCHES OLD FUNCTIONALITY
* THAT USES IT. USE decryptSymmetric() instead
*
* @param {Object} obj
* @param {String} obj.ciphertext - ciphertext to decrypt
* @param {String} obj.iv - (base64) 256-bit iv
* @param {String} obj.tag - (base64) tag
* @param {String} obj.key - (hex) 128-bit key
* @returns {String} cleartext - the deciphered ciphertext
*/
const decryptSymmetric128BitHexKeyUTF8 = ({
ciphertext,
iv,
tag,
key
}: IDecryptSymmetricInput) => {
const decipher = crypto.createDecipheriv(
ALGORITHM_AES_256_GCM,
key,
Buffer.from(iv, 'base64')
);
decipher.setAuthTag(Buffer.from(tag, 'base64'));
let cleartext = decipher.update(ciphertext, 'base64', 'utf8');
cleartext += decipher.final('utf8');
return cleartext;
}
export {
generateKeyPair,
encryptAsymmetric,
decryptAsymmetric,
encryptSymmetric,
decryptSymmetric,
encryptSymmetric128BitHexKeyUTF8,
decryptSymmetric128BitHexKeyUTF8
};

View File

@@ -0,0 +1,210 @@
import crypto from 'crypto';
import { encryptSymmetric128BitHexKeyUTF8 } from '../crypto';
import { EESecretService } from '../../ee/services';
import { SecretVersion } from '../../ee/models';
import {
Secret,
ISecret,
SecretBlindIndexData,
Workspace,
Bot,
BackupPrivateKey,
IntegrationAuth
} from '../../models';
import { getEncryptionKey, getRootEncryptionKey } from '../../config';
import {
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8
} from '../../variables';
/**
*
*/
export const backfillSecretVersions = async () => {
await Secret.updateMany(
{ version: { $exists: false } },
{ $set: { version: 1 } }
);
const unversionedSecrets: ISecret[] = await Secret.aggregate([
{
$lookup: {
from: "secretversions",
localField: "_id",
foreignField: "secret",
as: "versions",
},
},
{
$match: {
versions: { $size: 0 },
},
},
]);
if (unversionedSecrets.length > 0) {
await EESecretService.addSecretVersions({
secretVersions: unversionedSecrets.map(
(s, idx) =>
new SecretVersion({
...s,
secret: s._id,
version: s.version ? s.version : 1,
isDeleted: false,
workspace: s.workspace,
environment: s.environment,
})
),
});
}
}
export const backfillSecretBlindIndexData = async () => {
const workspaceIdsBlindIndexed = await SecretBlindIndexData.distinct('workspace');
const workspaceIdsToBlindIndex = await Workspace.distinct('_id', {
_id: {
$nin: workspaceIdsBlindIndexed
}
});
const secretBlindIndexDataToInsert = await Promise.all(
workspaceIdsToBlindIndex.map(async (workspaceToBlindIndex) => {
const salt = crypto.randomBytes(16).toString('base64');
const {
ciphertext: encryptedSaltCiphertext,
iv: saltIV,
tag: saltTag
} = encryptSymmetric128BitHexKeyUTF8({
plaintext: salt,
key: await getEncryptionKey()
});
const secretBlindIndexData = new SecretBlindIndexData({
workspace: workspaceToBlindIndex,
encryptedSaltCiphertext,
saltIV,
saltTag
})
return secretBlindIndexData;
})
);
if (secretBlindIndexDataToInsert.length > 0) {
await SecretBlindIndexData.insertMany(secretBlindIndexDataToInsert);
}
}
export const backfillEncryptionMetadata = async () => {
// backfill bot encryption metadata
await Bot.updateMany(
{
algorithm: {
$exists: false
},
keySize: {
$exists: false
},
keyEncoding: {
$exists: false
}
},
{
$set: {
algorithm: ALGORITHM_AES_256_GCM,
keySize: 256,
keyEncoding: ENCODING_SCHEME_UTF8
}
}
);
// backfill secret blind index encryption metadata
await SecretBlindIndexData.updateMany(
{
algorithm: {
$exists: false
},
keySize: {
$exists: false
},
keyEncoding: {
$exists: false
}
},
{
$set: {
algorithm: ALGORITHM_AES_256_GCM,
keySize: 256,
keyEncoding: ENCODING_SCHEME_UTF8
}
}
);
// backfill backup private key encryption metadata
await BackupPrivateKey.updateMany(
{
algorithm: {
$exists: false
},
keySize: {
$exists: false
},
keyEncoding: {
$exists: false
}
},
{
$set: {
algorithm: ALGORITHM_AES_256_GCM,
keySize: 256,
keyEncoding: ENCODING_SCHEME_UTF8
}
}
);
// backfill integration auth encryption metadata
await IntegrationAuth.updateMany(
{
},
{
$set: {
algorithm: ALGORITHM_AES_256_GCM,
}
}
);
// TODO: blind indices
// TODO: secret versions and snapshots etc.
// TODO: re-encrypt keys logic
// TODO: how do you handle different parts of the software
// encrypting under different schemes?
// const encryptionKey = await getEncryptionKey();
// const rootEncryptionKey = await getRootEncryptionKey();
// console.log('rootEncryptionKey: ', rootEncryptionKey);
// if (encryptionKey && rootEncryptionKey) {
// // case: both the old encryption key and new encryption key are present
// // -> perform migration if needed
// console.log('rootEncryptionKey is defined');
// const bots = await Bot.find({
// algorithm: ALGORITHM_AES_256_GCM,
// keySize: 256,
// keyEncoding: ENCODING_SCHEME_UTF8
// }, 'encryptedPrivateKey iv tag');
// if (bots.length > 0) {
// // TODO: unencrypt and re-encrypt
// // TODO: unencrypt and re-encrypt blind-indices
// // probably then need to move this function
// console.log('bots: ', bots);
// }
// }
}

View File

@@ -0,0 +1,51 @@
import * as Sentry from '@sentry/node';
import { DatabaseService } from '../../services';
import { setTransporter } from '../../helpers/nodemailer';
import { initSmtp } from '../../services/smtp';
import { createTestUserForDevelopment } from '../addDevelopmentUser'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { patchRouterParam } = require('../patchAsyncRoutes');
import {
backfillSecretVersions,
backfillSecretBlindIndexData,
backfillEncryptionMetadata
} from './backfill';
import {
getNodeEnv,
getMongoURL,
getSentryDSN
} from '../../config';
/**
* Prepare Infisical upon startup. This includes tasks like:
* - Initializing SMTP configuration
* - Initializing the database connection
* - Initializing Sentry
* - Backfilling data
*/
export const setup = async () => {
// initializing SMTP configuration
setTransporter(await initSmtp());
// initializing the database connection
await DatabaseService.initDatabase(await getMongoURL());
// backfilling data
await backfillSecretVersions();
await backfillSecretBlindIndexData();
await backfillEncryptionMetadata();
// initializing Sentry
if ((await getNodeEnv()) !== 'development') {
Sentry.init({
dsn: await getSentryDSN(),
tracesSampleRate: 1.0,
debug: await getNodeEnv() === 'production' ? false : true,
environment: await getNodeEnv()
});
}
patchRouterParam();
await createTestUserForDevelopment();
}

View File

@@ -0,0 +1,21 @@
import { InternalServerError } from "../utils/errors";
/**
* Validate that the encryption key [encryptionKey] is in base64 format and 256-bit
* @param {String} encryptionKey - the encryption key to validate
*/
export const validateEncryptionKey = (encryptionKey: string): Buffer => {
const keyBuffer = Buffer.from(encryptionKey, 'base64')
const decoded = keyBuffer.toString('base64');
if (decoded !== encryptionKey) throw InternalServerError({
message: 'Failed to validate the format of the encryption key. Please check that it is in base64 format.'
});
if (keyBuffer.length !== 32) throw InternalServerError({
message: 'Failed to validate that the encryption key is 256-bit. Please check that it is 256-bit.'
});
return keyBuffer;
};

View File

@@ -0,0 +1 @@
export * from './config';

View File

@@ -1,15 +1,6 @@
const ACTION_LOGIN = 'login';
const ACTION_LOGOUT = 'logout';
const ACTION_ADD_SECRETS = 'addSecrets';
const ACTION_DELETE_SECRETS = 'deleteSecrets';
const ACTION_UPDATE_SECRETS = 'updateSecrets';
const ACTION_READ_SECRETS = 'readSecrets';
export {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS
}
export const ACTION_LOGIN = 'login';
export const ACTION_LOGOUT = 'logout';
export const ACTION_ADD_SECRETS = 'addSecrets';
export const ACTION_DELETE_SECRETS = 'deleteSecrets';
export const ACTION_UPDATE_SECRETS = 'updateSecrets';
export const ACTION_READ_SECRETS = 'readSecrets';

View File

@@ -1,11 +1,4 @@
const AUTH_MODE_JWT = 'jwt';
const AUTH_MODE_SERVICE_ACCOUNT = 'serviceAccount';
const AUTH_MODE_SERVICE_TOKEN = 'serviceToken';
const AUTH_MODE_API_KEY = 'apiKey'; // TODO: deprecate
export {
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_API_KEY
}
export const AUTH_MODE_JWT = 'jwt';
export const AUTH_MODE_SERVICE_ACCOUNT = 'serviceAccount';
export const AUTH_MODE_SERVICE_TOKEN = 'serviceToken';
export const AUTH_MODE_API_KEY = 'apiKey'; // TODO: deprecate

View File

@@ -0,0 +1,7 @@
export const ALGORITHM_AES_256_GCM = 'aes-256-gcm';
export const BLOCK_SIZE_BYTES_32 = 32;
export const BLOCK_SIZE_BYTES_16 = 16;
export const ENCODING_SCHEME_UTF8 = 'utf8';
export const ENCODING_SCHEME_HEX = 'hex';
export const ENCODING_SCHEME_BASE64 = 'base64';

View File

@@ -1,14 +1,6 @@
// environments
const ENV_DEV = 'dev';
const ENV_TESTING = 'test';
const ENV_STAGING = 'staging';
const ENV_PROD = 'prod';
const ENV_SET = new Set([ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD]);
export {
ENV_DEV,
ENV_TESTING,
ENV_STAGING,
ENV_PROD,
ENV_SET
}
export const ENV_DEV = 'dev';
export const ENV_TESTING = 'test';
export const ENV_STAGING = 'staging';
export const ENV_PROD = 'prod';
export const ENV_SET = new Set([ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD]);

View File

@@ -1,7 +1,2 @@
const EVENT_PUSH_SECRETS = 'pushSecrets';
const EVENT_PULL_SECRETS = 'pullSecrets';
export {
EVENT_PUSH_SECRETS,
EVENT_PULL_SECRETS
}
export const EVENT_PUSH_SECRETS = 'pushSecrets';
export const EVENT_PULL_SECRETS = 'pullSecrets';

View File

@@ -1,152 +1,13 @@
import {
ENV_DEV,
ENV_TESTING,
ENV_STAGING,
ENV_PROD,
ENV_SET,
} from "./environment";
import {
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_AWS_PARAMETER_STORE,
INTEGRATION_AWS_SECRET_MANAGER,
INTEGRATION_HEROKU,
INTEGRATION_VERCEL,
INTEGRATION_NETLIFY,
INTEGRATION_GITHUB,
INTEGRATION_GITLAB,
INTEGRATION_RENDER,
INTEGRATION_RAILWAY,
INTEGRATION_FLYIO,
INTEGRATION_CIRCLECI,
INTEGRATION_TRAVISCI,
INTEGRATION_SUPABASE,
INTEGRATION_SET,
INTEGRATION_OAUTH2,
INTEGRATION_AZURE_TOKEN_URL,
INTEGRATION_HEROKU_TOKEN_URL,
INTEGRATION_VERCEL_TOKEN_URL,
INTEGRATION_NETLIFY_TOKEN_URL,
INTEGRATION_GITHUB_TOKEN_URL,
INTEGRATION_GITLAB_TOKEN_URL,
INTEGRATION_HEROKU_API_URL,
INTEGRATION_GITLAB_API_URL,
INTEGRATION_VERCEL_API_URL,
INTEGRATION_NETLIFY_API_URL,
INTEGRATION_RENDER_API_URL,
INTEGRATION_RAILWAY_API_URL,
INTEGRATION_FLYIO_API_URL,
INTEGRATION_CIRCLECI_API_URL,
INTEGRATION_TRAVISCI_API_URL,
INTEGRATION_SUPABASE_API_URL,
getIntegrationOptions
} from "./integration";
import { OWNER, ADMIN, MEMBER, INVITED, ACCEPTED } from "./organization";
import { SECRET_SHARED, SECRET_PERSONAL } from "./secret";
import { EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS } from "./event";
import {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_READ_SECRETS
} from './action';
import {
SMTP_HOST_SENDGRID,
SMTP_HOST_MAILGUN,
SMTP_HOST_SOCKETLABS,
SMTP_HOST_ZOHOMAIL
} from './smtp';
import { PLAN_STARTER, PLAN_PRO } from './stripe';
import {
MFA_METHOD_EMAIL
} from './user';
import {
TOKEN_EMAIL_CONFIRMATION,
TOKEN_EMAIL_MFA,
TOKEN_EMAIL_ORG_INVITATION,
TOKEN_EMAIL_PASSWORD_RESET
} from './token';
import {
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS
} from './permission';
import {
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_API_KEY
} from './authentication';
export {
OWNER,
ADMIN,
MEMBER,
INVITED,
ACCEPTED,
SECRET_SHARED,
SECRET_PERSONAL,
ENV_DEV,
ENV_TESTING,
ENV_STAGING,
ENV_PROD,
ENV_SET,
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_AWS_PARAMETER_STORE,
INTEGRATION_AWS_SECRET_MANAGER,
INTEGRATION_HEROKU,
INTEGRATION_VERCEL,
INTEGRATION_NETLIFY,
INTEGRATION_GITHUB,
INTEGRATION_GITLAB,
INTEGRATION_RENDER,
INTEGRATION_RAILWAY,
INTEGRATION_FLYIO,
INTEGRATION_CIRCLECI,
INTEGRATION_TRAVISCI,
INTEGRATION_SUPABASE,
INTEGRATION_SET,
INTEGRATION_OAUTH2,
INTEGRATION_AZURE_TOKEN_URL,
INTEGRATION_HEROKU_TOKEN_URL,
INTEGRATION_VERCEL_TOKEN_URL,
INTEGRATION_NETLIFY_TOKEN_URL,
INTEGRATION_GITHUB_TOKEN_URL,
INTEGRATION_GITLAB_TOKEN_URL,
INTEGRATION_HEROKU_API_URL,
INTEGRATION_GITLAB_API_URL,
INTEGRATION_VERCEL_API_URL,
INTEGRATION_NETLIFY_API_URL,
INTEGRATION_RENDER_API_URL,
INTEGRATION_RAILWAY_API_URL,
INTEGRATION_FLYIO_API_URL,
INTEGRATION_CIRCLECI_API_URL,
INTEGRATION_TRAVISCI_API_URL,
INTEGRATION_SUPABASE_API_URL,
EVENT_PUSH_SECRETS,
EVENT_PULL_SECRETS,
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_READ_SECRETS,
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS,
getIntegrationOptions,
SMTP_HOST_SENDGRID,
SMTP_HOST_MAILGUN,
SMTP_HOST_SOCKETLABS,
SMTP_HOST_ZOHOMAIL,
PLAN_STARTER,
PLAN_PRO,
MFA_METHOD_EMAIL,
TOKEN_EMAIL_CONFIRMATION,
TOKEN_EMAIL_MFA,
TOKEN_EMAIL_ORG_INVITATION,
TOKEN_EMAIL_PASSWORD_RESET,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN,
AUTH_MODE_API_KEY
};
export * from './action';
export * from './authentication';
export * from './crypto';
export * from './environment';
export * from './event';
export * from './integration';
export * from './organization';
export * from './permission';
export * from './secret';
export * from './smtp';
export * from './stripe';
export * from './token';
export * from './user';

View File

@@ -8,21 +8,21 @@ import {
} from '../config';
// integrations
const INTEGRATION_AZURE_KEY_VAULT = 'azure-key-vault';
const INTEGRATION_AWS_PARAMETER_STORE = 'aws-parameter-store';
const INTEGRATION_AWS_SECRET_MANAGER = 'aws-secret-manager';
const INTEGRATION_HEROKU = "heroku";
const INTEGRATION_VERCEL = "vercel";
const INTEGRATION_NETLIFY = "netlify";
const INTEGRATION_GITHUB = "github";
const INTEGRATION_GITLAB = "gitlab";
const INTEGRATION_RENDER = "render";
const INTEGRATION_RAILWAY = "railway";
const INTEGRATION_FLYIO = "flyio";
const INTEGRATION_CIRCLECI = "circleci";
const INTEGRATION_TRAVISCI = "travisci";
const INTEGRATION_SUPABASE = 'supabase';
const INTEGRATION_SET = new Set([
export const INTEGRATION_AZURE_KEY_VAULT = 'azure-key-vault';
export const INTEGRATION_AWS_PARAMETER_STORE = 'aws-parameter-store';
export const INTEGRATION_AWS_SECRET_MANAGER = 'aws-secret-manager';
export const INTEGRATION_HEROKU = "heroku";
export const INTEGRATION_VERCEL = "vercel";
export const INTEGRATION_NETLIFY = "netlify";
export const INTEGRATION_GITHUB = "github";
export const INTEGRATION_GITLAB = "gitlab";
export const INTEGRATION_RENDER = "render";
export const INTEGRATION_RAILWAY = "railway";
export const INTEGRATION_FLYIO = "flyio";
export const INTEGRATION_CIRCLECI = "circleci";
export const INTEGRATION_TRAVISCI = "travisci";
export const INTEGRATION_SUPABASE = 'supabase';
export const INTEGRATION_SET = new Set([
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_HEROKU,
INTEGRATION_VERCEL,
@@ -37,31 +37,31 @@ const INTEGRATION_SET = new Set([
]);
// integration types
const INTEGRATION_OAUTH2 = "oauth2";
export const INTEGRATION_OAUTH2 = "oauth2";
// integration oauth endpoints
const INTEGRATION_AZURE_TOKEN_URL = `https://login.microsoftonline.com/common/oauth2/v2.0/token`;
const INTEGRATION_HEROKU_TOKEN_URL = 'https://id.heroku.com/oauth/token';
const INTEGRATION_VERCEL_TOKEN_URL =
export const INTEGRATION_AZURE_TOKEN_URL = `https://login.microsoftonline.com/common/oauth2/v2.0/token`;
export const INTEGRATION_HEROKU_TOKEN_URL = 'https://id.heroku.com/oauth/token';
export const INTEGRATION_VERCEL_TOKEN_URL =
"https://api.vercel.com/v2/oauth/access_token";
const INTEGRATION_NETLIFY_TOKEN_URL = "https://api.netlify.com/oauth/token";
const INTEGRATION_GITHUB_TOKEN_URL =
export const INTEGRATION_NETLIFY_TOKEN_URL = "https://api.netlify.com/oauth/token";
export const INTEGRATION_GITHUB_TOKEN_URL =
"https://github.com/login/oauth/access_token";
const INTEGRATION_GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token";
export const INTEGRATION_GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token";
// integration apps endpoints
const INTEGRATION_HEROKU_API_URL = "https://api.heroku.com";
const INTEGRATION_GITLAB_API_URL = "https://gitlab.com/api";
const INTEGRATION_VERCEL_API_URL = "https://api.vercel.com";
const INTEGRATION_NETLIFY_API_URL = "https://api.netlify.com";
const INTEGRATION_RENDER_API_URL = "https://api.render.com";
const INTEGRATION_RAILWAY_API_URL = "https://backboard.railway.app/graphql/v2";
const INTEGRATION_FLYIO_API_URL = "https://api.fly.io/graphql";
const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api";
const INTEGRATION_TRAVISCI_API_URL = "https://api.travis-ci.com";
const INTEGRATION_SUPABASE_API_URL = 'https://api.supabase.com';
export const INTEGRATION_HEROKU_API_URL = "https://api.heroku.com";
export const INTEGRATION_GITLAB_API_URL = "https://gitlab.com/api";
export const INTEGRATION_VERCEL_API_URL = "https://api.vercel.com";
export const INTEGRATION_NETLIFY_API_URL = "https://api.netlify.com";
export const INTEGRATION_RENDER_API_URL = "https://api.render.com";
export const INTEGRATION_RAILWAY_API_URL = "https://backboard.railway.app/graphql/v2";
export const INTEGRATION_FLYIO_API_URL = "https://api.fly.io/graphql";
export const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api";
export const INTEGRATION_TRAVISCI_API_URL = "https://api.travis-ci.com";
export const INTEGRATION_SUPABASE_API_URL = 'https://api.supabase.com';
const getIntegrationOptions = async () => {
export const getIntegrationOptions = async () => {
const INTEGRATION_OPTIONS = [
{
name: 'Heroku',
@@ -202,41 +202,4 @@ const getIntegrationOptions = async () => {
]
return INTEGRATION_OPTIONS;
}
export {
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_AWS_PARAMETER_STORE,
INTEGRATION_AWS_SECRET_MANAGER,
INTEGRATION_HEROKU,
INTEGRATION_VERCEL,
INTEGRATION_NETLIFY,
INTEGRATION_GITHUB,
INTEGRATION_GITLAB,
INTEGRATION_RENDER,
INTEGRATION_RAILWAY,
INTEGRATION_FLYIO,
INTEGRATION_CIRCLECI,
INTEGRATION_TRAVISCI,
INTEGRATION_SUPABASE,
INTEGRATION_SET,
INTEGRATION_OAUTH2,
INTEGRATION_AZURE_TOKEN_URL,
INTEGRATION_HEROKU_TOKEN_URL,
INTEGRATION_VERCEL_TOKEN_URL,
INTEGRATION_NETLIFY_TOKEN_URL,
INTEGRATION_GITHUB_TOKEN_URL,
INTEGRATION_GITLAB_API_URL,
INTEGRATION_HEROKU_API_URL,
INTEGRATION_GITLAB_TOKEN_URL,
INTEGRATION_VERCEL_API_URL,
INTEGRATION_NETLIFY_API_URL,
INTEGRATION_RENDER_API_URL,
INTEGRATION_RAILWAY_API_URL,
INTEGRATION_FLYIO_API_URL,
INTEGRATION_CIRCLECI_API_URL,
INTEGRATION_TRAVISCI_API_URL,
INTEGRATION_SUPABASE_API_URL,
getIntegrationOptions
};
}

View File

@@ -1,12 +1,10 @@
// membership roles
const OWNER = "owner";
const ADMIN = "admin";
const MEMBER = "member";
export const OWNER = "owner";
export const ADMIN = "admin";
export const MEMBER = "member";
// membership statuses
const INVITED = "invited";
export const INVITED = "invited";
// -- organization
const ACCEPTED = "accepted";
export { OWNER, ADMIN, MEMBER, INVITED, ACCEPTED };
export const ACCEPTED = "accepted";

View File

@@ -1,7 +1,2 @@
const PERMISSION_READ_SECRETS = 'read';
const PERMISSION_WRITE_SECRETS = 'write';
export {
PERMISSION_READ_SECRETS,
PERMISSION_WRITE_SECRETS
}
export const PERMISSION_READ_SECRETS = 'read';
export const PERMISSION_WRITE_SECRETS = 'write';

View File

@@ -1,8 +1,3 @@
// secrets
const SECRET_SHARED = 'shared';
const SECRET_PERSONAL = 'personal';
export {
SECRET_SHARED,
SECRET_PERSONAL
}
export const SECRET_SHARED = 'shared';
export const SECRET_PERSONAL = 'personal';

View File

@@ -1,11 +1,4 @@
const SMTP_HOST_SENDGRID = 'smtp.sendgrid.net';
const SMTP_HOST_MAILGUN = 'smtp.mailgun.org';
const SMTP_HOST_SOCKETLABS = 'smtp.socketlabs.com';
const SMTP_HOST_ZOHOMAIL = 'smtp.zoho.com';
export {
SMTP_HOST_SENDGRID,
SMTP_HOST_MAILGUN,
SMTP_HOST_SOCKETLABS,
SMTP_HOST_ZOHOMAIL
}
export const SMTP_HOST_SENDGRID = 'smtp.sendgrid.net';
export const SMTP_HOST_MAILGUN = 'smtp.mailgun.org';
export const SMTP_HOST_SOCKETLABS = 'smtp.socketlabs.com';
export const SMTP_HOST_ZOHOMAIL = 'smtp.zoho.com';

View File

@@ -1,7 +1,2 @@
const PLAN_STARTER = 'starter';
const PLAN_PRO = 'pro';
export {
PLAN_STARTER,
PLAN_PRO
}
export const PLAN_STARTER = 'starter';
export const PLAN_PRO = 'pro';

View File

@@ -1,11 +1,4 @@
const TOKEN_EMAIL_CONFIRMATION = 'emailConfirmation';
const TOKEN_EMAIL_MFA = 'emailMfa';
const TOKEN_EMAIL_ORG_INVITATION = 'organizationInvitation';
const TOKEN_EMAIL_PASSWORD_RESET = 'passwordReset';
export {
TOKEN_EMAIL_CONFIRMATION,
TOKEN_EMAIL_MFA,
TOKEN_EMAIL_ORG_INVITATION,
TOKEN_EMAIL_PASSWORD_RESET
}
export const TOKEN_EMAIL_CONFIRMATION = 'emailConfirmation';
export const TOKEN_EMAIL_MFA = 'emailMfa';
export const TOKEN_EMAIL_ORG_INVITATION = 'organizationInvitation';
export const TOKEN_EMAIL_PASSWORD_RESET = 'passwordReset';

View File

@@ -1,5 +1 @@
const MFA_METHOD_EMAIL = 'email';
export {
MFA_METHOD_EMAIL
}
export const MFA_METHOD_EMAIL = 'email';

View File

@@ -10,7 +10,9 @@ const jsrp = require('jsrp');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const axios = require('axios');
import { plainTextWorkspaceKey, testWorkspaceId } from "../../src/utils/addDevelopmentUser";
import { encryptSymmetric } from "../../src/utils/crypto";
import {
encryptSymmetric128BitHexKeyUTF8
} from '../../src/utils/crypto';
interface TokenData {
token: string;
@@ -64,7 +66,7 @@ export const getJWTFromTestUser = (): Promise<TokenData> => {
export const getServiceTokenFromTestUser = async () => {
const loggedInUserDetails = await getJWTFromTestUser()
const randomBytes = crypto.randomBytes(16).toString('hex');
const { ciphertext, iv, tag } = encryptSymmetric({
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({
plaintext: plainTextWorkspaceKey,
key: randomBytes,
});