mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 12:28:56 +00:00
feat(folder-scoped-st): implemented backend api for folder scoped service tokens
This commit is contained in:
@@ -1,20 +1,16 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import crypto from 'crypto';
|
import crypto from "crypto";
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from "bcrypt";
|
||||||
|
import { User, ServiceAccount, ServiceTokenData } from "../../models";
|
||||||
|
import { userHasWorkspaceAccess } from "../../ee/helpers/checkMembershipPermissions";
|
||||||
import {
|
import {
|
||||||
User,
|
PERMISSION_READ_SECRETS,
|
||||||
ServiceAccount,
|
AUTH_MODE_JWT,
|
||||||
ServiceTokenData
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
} from '../../models';
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions';
|
} from "../../variables";
|
||||||
import {
|
import { getSaltRounds } from "../../config";
|
||||||
PERMISSION_READ_SECRETS,
|
import { BadRequestError } from "../../utils/errors";
|
||||||
AUTH_MODE_JWT,
|
|
||||||
AUTH_MODE_SERVICE_ACCOUNT,
|
|
||||||
AUTH_MODE_SERVICE_TOKEN
|
|
||||||
} from '../../variables';
|
|
||||||
import { getSaltRounds } from '../../config';
|
|
||||||
import { BadRequestError } from '../../utils/errors';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return service token data associated with service token on request
|
* Return service token data associated with service token on request
|
||||||
@@ -23,7 +19,7 @@ import { BadRequestError } from '../../utils/errors';
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getServiceTokenData = async (req: Request, res: Response) => {
|
export const getServiceTokenData = async (req: Request, res: Response) => {
|
||||||
/*
|
/*
|
||||||
#swagger.summary = 'Return Infisical Token data'
|
#swagger.summary = 'Return Infisical Token data'
|
||||||
#swagger.description = 'Return Infisical Token data'
|
#swagger.description = 'Return Infisical Token data'
|
||||||
|
|
||||||
@@ -49,17 +45,19 @@ export const getServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!(req.authData.authPayload instanceof ServiceTokenData)) throw BadRequestError({
|
if (!(req.authData.authPayload instanceof ServiceTokenData))
|
||||||
message: 'Failed accepted client validation for service token data'
|
throw BadRequestError({
|
||||||
|
message: "Failed accepted client validation for service token data",
|
||||||
});
|
});
|
||||||
|
|
||||||
const serviceTokenData = await ServiceTokenData
|
const serviceTokenData = await ServiceTokenData.findById(
|
||||||
.findById(req.authData.authPayload._id)
|
req.authData.authPayload._id
|
||||||
.select('+encryptedKey +iv +tag')
|
)
|
||||||
.populate('user');
|
.select("+encryptedKey +iv +tag")
|
||||||
|
.populate("user");
|
||||||
|
|
||||||
return res.status(200).json(serviceTokenData);
|
return res.status(200).json(serviceTokenData);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new service token data for workspace with id [workspaceId] and
|
* Create new service token data for workspace with id [workspaceId] and
|
||||||
@@ -69,65 +67,73 @@ export const getServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const createServiceTokenData = async (req: Request, res: Response) => {
|
export const createServiceTokenData = async (req: Request, res: Response) => {
|
||||||
let serviceTokenData;
|
let serviceTokenData;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
encryptedKey,
|
encryptedKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
expiresIn,
|
expiresIn,
|
||||||
permissions
|
secretPath,
|
||||||
} = req.body;
|
permissions,
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
const secret = crypto.randomBytes(16).toString('hex');
|
const secret = crypto.randomBytes(16).toString("hex");
|
||||||
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||||
|
|
||||||
let expiresAt;
|
let expiresAt;
|
||||||
if (expiresIn) {
|
if (expiresIn) {
|
||||||
expiresAt = new Date()
|
expiresAt = new Date();
|
||||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
let user, serviceAccount;
|
let user, serviceAccount;
|
||||||
|
|
||||||
if (req.authData.authMode === AUTH_MODE_JWT && req.authData.authPayload instanceof User) {
|
if (
|
||||||
user = req.authData.authPayload._id;
|
req.authData.authMode === AUTH_MODE_JWT &&
|
||||||
}
|
req.authData.authPayload instanceof User
|
||||||
|
) {
|
||||||
|
user = req.authData.authPayload._id;
|
||||||
|
}
|
||||||
|
|
||||||
if (req.authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && req.authData.authPayload instanceof ServiceAccount) {
|
if (
|
||||||
serviceAccount = req.authData.authPayload._id;
|
req.authData.authMode === AUTH_MODE_SERVICE_ACCOUNT &&
|
||||||
}
|
req.authData.authPayload instanceof ServiceAccount
|
||||||
|
) {
|
||||||
|
serviceAccount = req.authData.authPayload._id;
|
||||||
|
}
|
||||||
|
|
||||||
serviceTokenData = await new ServiceTokenData({
|
serviceTokenData = await new ServiceTokenData({
|
||||||
name,
|
name,
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
environment,
|
environment,
|
||||||
user,
|
user,
|
||||||
serviceAccount,
|
serviceAccount,
|
||||||
lastUsed: new Date(),
|
lastUsed: new Date(),
|
||||||
expiresAt,
|
expiresAt,
|
||||||
secretHash,
|
secretHash,
|
||||||
encryptedKey,
|
encryptedKey,
|
||||||
iv,
|
iv,
|
||||||
tag,
|
tag,
|
||||||
permissions
|
secretPath,
|
||||||
}).save();
|
permissions,
|
||||||
|
}).save();
|
||||||
|
|
||||||
// return service token data without sensitive data
|
// return service token data without sensitive data
|
||||||
serviceTokenData = await ServiceTokenData.findById(serviceTokenData._id);
|
serviceTokenData = await ServiceTokenData.findById(serviceTokenData._id);
|
||||||
|
|
||||||
if (!serviceTokenData) throw new Error('Failed to find service token data');
|
if (!serviceTokenData) throw new Error("Failed to find service token data");
|
||||||
|
|
||||||
const serviceToken = `st.${serviceTokenData._id.toString()}.${secret}`;
|
const serviceToken = `st.${serviceTokenData._id.toString()}.${secret}`;
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceToken,
|
serviceToken,
|
||||||
serviceTokenData
|
serviceTokenData,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete service token data with id [serviceTokenDataId].
|
* Delete service token data with id [serviceTokenDataId].
|
||||||
@@ -136,11 +142,13 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
||||||
const { serviceTokenDataId } = req.params;
|
const { serviceTokenDataId } = req.params;
|
||||||
|
|
||||||
const serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId);
|
const serviceTokenData = await ServiceTokenData.findByIdAndDelete(
|
||||||
|
serviceTokenDataId
|
||||||
|
);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
serviceTokenData
|
serviceTokenData,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from "express";
|
||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import {
|
import { SecretService, EventService } from "../../services";
|
||||||
SecretService,
|
import { eventPushSecrets } from "../../events";
|
||||||
TelemetryService,
|
|
||||||
EventService
|
|
||||||
} from '../../services';
|
|
||||||
import { eventPushSecrets } from '../../events';
|
|
||||||
import { getAuthDataPayloadIdObj } from '../../utils/auth';
|
|
||||||
import { BadRequestError } from '../../utils/errors';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get secrets for workspace with id [workspaceId] and environment
|
* Get secrets for workspace with id [workspaceId] and environment
|
||||||
@@ -16,19 +10,21 @@ import { BadRequestError } from '../../utils/errors';
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getSecrets = async (req: Request, res: Response) => {
|
export const getSecrets = async (req: Request, res: Response) => {
|
||||||
const workspaceId = req.query.workspaceId as string;
|
const workspaceId = req.query.workspaceId as string;
|
||||||
const environment = req.query.environment as string;
|
const environment = req.query.environment as string;
|
||||||
|
const secretPath = req.query.secretPath as string;
|
||||||
|
|
||||||
const secrets = await SecretService.getSecrets({
|
const secrets = await SecretService.getSecrets({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
authData: req.authData
|
secretPath,
|
||||||
});
|
authData: req.authData,
|
||||||
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secrets
|
secrets,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get secret with name [secretName]
|
* Get secret with name [secretName]
|
||||||
@@ -36,23 +32,25 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getSecretByName = async (req: Request, res: Response) => {
|
export const getSecretByName = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
const { secretName } = req.params;
|
||||||
const workspaceId = req.query.workspaceId as string;
|
const workspaceId = req.query.workspaceId as string;
|
||||||
const environment = req.query.environment as string;
|
const environment = req.query.environment as string;
|
||||||
const type = req.query.type as 'shared' | 'personal' | undefined;
|
const secretPath = req.query.secretPath as string;
|
||||||
|
const type = req.query.type as "shared" | "personal" | undefined;
|
||||||
|
|
||||||
const secret = await SecretService.getSecret({
|
const secret = await SecretService.getSecret({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData: req.authData
|
secretPath,
|
||||||
});
|
authData: req.authData,
|
||||||
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secret
|
secret,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create secret with name [secretName]
|
* Create secret with name [secretName]
|
||||||
@@ -60,55 +58,59 @@ export const getSecretByName = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const createSecret = async (req: Request, res: Response) => {
|
export const createSecret = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
const { secretName } = req.params;
|
||||||
const {
|
const {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
secretKeyCiphertext,
|
secretKeyCiphertext,
|
||||||
secretKeyIV,
|
secretKeyIV,
|
||||||
secretKeyTag,
|
secretKeyTag,
|
||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
secretCommentCiphertext,
|
secretCommentCiphertext,
|
||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag
|
secretCommentTag,
|
||||||
} = req.body;
|
secretPath = "/",
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
const secret = await SecretService.createSecret({
|
const secret = await SecretService.createSecret({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData: req.authData,
|
authData: req.authData,
|
||||||
secretKeyCiphertext,
|
secretKeyCiphertext,
|
||||||
secretKeyIV,
|
secretKeyIV,
|
||||||
secretKeyTag,
|
secretKeyTag,
|
||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
...((secretCommentCiphertext && secretCommentIV && secretCommentTag) ? {
|
secretPath,
|
||||||
secretCommentCiphertext,
|
...(secretCommentCiphertext && secretCommentIV && secretCommentTag
|
||||||
secretCommentIV,
|
? {
|
||||||
secretCommentTag
|
secretCommentCiphertext,
|
||||||
} : {})
|
secretCommentIV,
|
||||||
});
|
secretCommentTag,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
});
|
||||||
|
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
event: eventPushSecrets({
|
event: eventPushSecrets({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment
|
environment,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretWithoutBlindIndex = secret.toObject();
|
const secretWithoutBlindIndex = secret.toObject();
|
||||||
delete secretWithoutBlindIndex.secretBlindIndex;
|
delete secretWithoutBlindIndex.secretBlindIndex;
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secret: secretWithoutBlindIndex
|
secret: secretWithoutBlindIndex,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update secret with name [secretName]
|
* Update secret with name [secretName]
|
||||||
@@ -116,38 +118,40 @@ export const createSecret = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const updateSecretByName = async (req: Request, res: Response) => {
|
export const updateSecretByName = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
const { secretName } = req.params;
|
||||||
const {
|
const {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag
|
secretValueTag,
|
||||||
} = req.body;
|
secretPath = "/",
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
const secret = await SecretService.updateSecret({
|
const secret = await SecretService.updateSecret({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData: req.authData,
|
authData: req.authData,
|
||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag
|
secretValueTag,
|
||||||
});
|
secretPath,
|
||||||
|
});
|
||||||
|
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
event: eventPushSecrets({
|
event: eventPushSecrets({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment
|
environment,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secret
|
secret,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete secret with name [secretName]
|
* Delete secret with name [secretName]
|
||||||
@@ -155,29 +159,26 @@ export const updateSecretByName = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const deleteSecretByName = async (req: Request, res: Response) => {
|
export const deleteSecretByName = async (req: Request, res: Response) => {
|
||||||
const { secretName } = req.params;
|
const { secretName } = req.params;
|
||||||
const {
|
const { workspaceId, environment, type, secretPath = "/" } = req.body;
|
||||||
workspaceId,
|
|
||||||
environment,
|
|
||||||
type
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
const { secret, secrets } = await SecretService.deleteSecret({
|
const { secret } = await SecretService.deleteSecret({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData: req.authData
|
authData: req.authData,
|
||||||
});
|
secretPath,
|
||||||
|
});
|
||||||
|
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
event: eventPushSecrets({
|
event: eventPushSecrets({
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment
|
environment,
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secret
|
secret,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|||||||
+151
-37
@@ -1,19 +1,24 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
import {
|
import {
|
||||||
CreateSecretParams,
|
CreateSecretParams,
|
||||||
GetSecretsParams,
|
GetSecretsParams,
|
||||||
GetSecretParams,
|
GetSecretParams,
|
||||||
UpdateSecretParams,
|
UpdateSecretParams,
|
||||||
DeleteSecretParams,
|
DeleteSecretParams,
|
||||||
} from '../interfaces/services/SecretService';
|
} from "../interfaces/services/SecretService";
|
||||||
import { Secret, ISecret, SecretBlindIndexData } from '../models';
|
import {
|
||||||
import { SecretVersion } from '../ee/models';
|
Secret,
|
||||||
|
ISecret,
|
||||||
|
SecretBlindIndexData,
|
||||||
|
ServiceTokenData,
|
||||||
|
} from "../models";
|
||||||
|
import { SecretVersion } from "../ee/models";
|
||||||
import {
|
import {
|
||||||
BadRequestError,
|
BadRequestError,
|
||||||
SecretNotFoundError,
|
SecretNotFoundError,
|
||||||
SecretBlindIndexDataNotFoundError,
|
SecretBlindIndexDataNotFoundError,
|
||||||
InternalServerError,
|
InternalServerError,
|
||||||
} from '../utils/errors';
|
} from "../utils/errors";
|
||||||
import {
|
import {
|
||||||
SECRET_PERSONAL,
|
SECRET_PERSONAL,
|
||||||
SECRET_SHARED,
|
SECRET_SHARED,
|
||||||
@@ -24,20 +29,44 @@ import {
|
|||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
ENCODING_SCHEME_UTF8,
|
||||||
ENCODING_SCHEME_BASE64,
|
ENCODING_SCHEME_BASE64,
|
||||||
} from '../variables';
|
} from "../variables";
|
||||||
import crypto from 'crypto';
|
import crypto from "crypto";
|
||||||
import * as argon2 from 'argon2';
|
import * as argon2 from "argon2";
|
||||||
import {
|
import {
|
||||||
encryptSymmetric128BitHexKeyUTF8,
|
encryptSymmetric128BitHexKeyUTF8,
|
||||||
decryptSymmetric128BitHexKeyUTF8,
|
decryptSymmetric128BitHexKeyUTF8,
|
||||||
} from '../utils/crypto';
|
} from "../utils/crypto";
|
||||||
import { getEncryptionKey, client, getRootEncryptionKey } from '../config';
|
import { getEncryptionKey, client, getRootEncryptionKey } from "../config";
|
||||||
import { TelemetryService } from '../services';
|
import { TelemetryService } from "../services";
|
||||||
import { EESecretService, EELogService } from '../ee/services';
|
import { EESecretService, EELogService } from "../ee/services";
|
||||||
import {
|
import {
|
||||||
getAuthDataPayloadIdObj,
|
getAuthDataPayloadIdObj,
|
||||||
getAuthDataPayloadUserObj,
|
getAuthDataPayloadUserObj,
|
||||||
} from '../utils/auth';
|
} from "../utils/auth";
|
||||||
|
import Folder from "../models/folder";
|
||||||
|
import { getFolderByPath } from "../services/FolderService";
|
||||||
|
|
||||||
|
export const getFolderIdFromServiceToken = async (
|
||||||
|
workspaceId: Types.ObjectId | string,
|
||||||
|
environment: string,
|
||||||
|
secretPath: string
|
||||||
|
) => {
|
||||||
|
const folders = await Folder.findOne({
|
||||||
|
workspace: workspaceId,
|
||||||
|
environment,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!folders) {
|
||||||
|
if (secretPath !== "/") throw new Error("Invalid path. Folders not found");
|
||||||
|
} else {
|
||||||
|
const folder = getFolderByPath(folders.nodes, secretPath);
|
||||||
|
if (!folder) {
|
||||||
|
throw new Error("Folder not found");
|
||||||
|
}
|
||||||
|
return folder.id;
|
||||||
|
}
|
||||||
|
return "root";
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create secret blind index data containing encrypted blind index [salt]
|
* Create secret blind index data containing encrypted blind index [salt]
|
||||||
@@ -46,12 +75,12 @@ import {
|
|||||||
* @param {Types.ObjectId} obj.workspaceId
|
* @param {Types.ObjectId} obj.workspaceId
|
||||||
*/
|
*/
|
||||||
export const createSecretBlindIndexDataHelper = async ({
|
export const createSecretBlindIndexDataHelper = async ({
|
||||||
workspaceId
|
workspaceId,
|
||||||
}: {
|
}: {
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
}) => {
|
}) => {
|
||||||
// initialize random blind index salt for workspace
|
// initialize random blind index salt for workspace
|
||||||
const salt = crypto.randomBytes(16).toString('base64');
|
const salt = crypto.randomBytes(16).toString("base64");
|
||||||
|
|
||||||
const encryptionKey = await getEncryptionKey();
|
const encryptionKey = await getEncryptionKey();
|
||||||
const rootEncryptionKey = await getRootEncryptionKey();
|
const rootEncryptionKey = await getRootEncryptionKey();
|
||||||
@@ -99,7 +128,7 @@ export const createSecretBlindIndexDataHelper = async ({
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getSecretBlindIndexSaltHelper = async ({
|
export const getSecretBlindIndexSaltHelper = async ({
|
||||||
workspaceId
|
workspaceId,
|
||||||
}: {
|
}: {
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -108,7 +137,7 @@ export const getSecretBlindIndexSaltHelper = async ({
|
|||||||
|
|
||||||
const secretBlindIndexData = await SecretBlindIndexData.findOne({
|
const secretBlindIndexData = await SecretBlindIndexData.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
}).select('+algorithm +keyEncoding');
|
}).select("+algorithm +keyEncoding");
|
||||||
|
|
||||||
if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError();
|
if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError();
|
||||||
|
|
||||||
@@ -136,7 +165,7 @@ export const getSecretBlindIndexSaltHelper = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
throw InternalServerError({
|
throw InternalServerError({
|
||||||
message: 'Failed to obtain workspace salt needed for secret blind indexing',
|
message: "Failed to obtain workspace salt needed for secret blind indexing",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -148,8 +177,8 @@ export const getSecretBlindIndexSaltHelper = async ({
|
|||||||
* @param {String} obj.salt - base64-salt
|
* @param {String} obj.salt - base64-salt
|
||||||
*/
|
*/
|
||||||
export const generateSecretBlindIndexWithSaltHelper = async ({
|
export const generateSecretBlindIndexWithSaltHelper = async ({
|
||||||
secretName,
|
secretName,
|
||||||
salt
|
salt,
|
||||||
}: {
|
}: {
|
||||||
secretName: string;
|
secretName: string;
|
||||||
salt: string;
|
salt: string;
|
||||||
@@ -158,14 +187,14 @@ export const generateSecretBlindIndexWithSaltHelper = async ({
|
|||||||
const secretBlindIndex = (
|
const secretBlindIndex = (
|
||||||
await argon2.hash(secretName, {
|
await argon2.hash(secretName, {
|
||||||
type: argon2.argon2id,
|
type: argon2.argon2id,
|
||||||
salt: Buffer.from(salt, 'base64'),
|
salt: Buffer.from(salt, "base64"),
|
||||||
saltLength: 16, // default 16 bytes
|
saltLength: 16, // default 16 bytes
|
||||||
memoryCost: 65536, // default pool of 64 MiB per thread.
|
memoryCost: 65536, // default pool of 64 MiB per thread.
|
||||||
hashLength: 32,
|
hashLength: 32,
|
||||||
parallelism: 1,
|
parallelism: 1,
|
||||||
raw: true,
|
raw: true,
|
||||||
})
|
})
|
||||||
).toString('base64');
|
).toString("base64");
|
||||||
|
|
||||||
return secretBlindIndex;
|
return secretBlindIndex;
|
||||||
};
|
};
|
||||||
@@ -178,8 +207,8 @@ export const generateSecretBlindIndexWithSaltHelper = async ({
|
|||||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to
|
* @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to
|
||||||
*/
|
*/
|
||||||
export const generateSecretBlindIndexHelper = async ({
|
export const generateSecretBlindIndexHelper = async ({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId
|
workspaceId,
|
||||||
}: {
|
}: {
|
||||||
secretName: string;
|
secretName: string;
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
@@ -190,7 +219,7 @@ export const generateSecretBlindIndexHelper = async ({
|
|||||||
|
|
||||||
const secretBlindIndexData = await SecretBlindIndexData.findOne({
|
const secretBlindIndexData = await SecretBlindIndexData.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
}).select('+algorithm +keyEncoding');
|
}).select("+algorithm +keyEncoding");
|
||||||
|
|
||||||
if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError();
|
if (!secretBlindIndexData) throw SecretBlindIndexDataNotFoundError();
|
||||||
|
|
||||||
@@ -233,7 +262,7 @@ export const generateSecretBlindIndexHelper = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
throw InternalServerError({
|
throw InternalServerError({
|
||||||
message: 'Failed to generate secret blind index'
|
message: "Failed to generate secret blind index",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -262,23 +291,37 @@ export const createSecretHelper = async ({
|
|||||||
secretCommentCiphertext,
|
secretCommentCiphertext,
|
||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag,
|
secretCommentTag,
|
||||||
folderId,
|
secretPath = "/",
|
||||||
}: CreateSecretParams) => {
|
}: CreateSecretParams) => {
|
||||||
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// if using service token filter towards the folderId by secretpath
|
||||||
|
if (authData.authPayload instanceof ServiceTokenData) {
|
||||||
|
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
||||||
|
if (secretPath !== serviceTkScopedSecretPath) {
|
||||||
|
throw new Error("Folder Permission Denied");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const folderId = await getFolderIdFromServiceToken(
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
secretPath
|
||||||
|
);
|
||||||
|
|
||||||
const exists = await Secret.exists({
|
const exists = await Secret.exists({
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
|
folder: folderId,
|
||||||
type,
|
type,
|
||||||
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (exists)
|
if (exists)
|
||||||
throw BadRequestError({
|
throw BadRequestError({
|
||||||
message: 'Failed to create secret that already exists',
|
message: "Failed to create secret that already exists",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (type === SECRET_PERSONAL) {
|
if (type === SECRET_PERSONAL) {
|
||||||
@@ -287,6 +330,7 @@ export const createSecretHelper = async ({
|
|||||||
|
|
||||||
const exists = await Secret.exists({
|
const exists = await Secret.exists({
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
|
folder: folderId,
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
type: SECRET_SHARED,
|
type: SECRET_SHARED,
|
||||||
});
|
});
|
||||||
@@ -294,7 +338,7 @@ export const createSecretHelper = async ({
|
|||||||
if (!exists)
|
if (!exists)
|
||||||
throw BadRequestError({
|
throw BadRequestError({
|
||||||
message:
|
message:
|
||||||
'Failed to create personal secret override for no corresponding shared secret',
|
"Failed to create personal secret override for no corresponding shared secret",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,6 +369,7 @@ export const createSecretHelper = async ({
|
|||||||
version: secret.version,
|
version: secret.version,
|
||||||
workspace: secret.workspace,
|
workspace: secret.workspace,
|
||||||
type,
|
type,
|
||||||
|
folder: folderId,
|
||||||
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
||||||
environment: secret.environment,
|
environment: secret.environment,
|
||||||
isDeleted: false,
|
isDeleted: false,
|
||||||
@@ -372,7 +417,7 @@ export const createSecretHelper = async ({
|
|||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets added',
|
event: "secrets added",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData,
|
authData,
|
||||||
}),
|
}),
|
||||||
@@ -380,6 +425,7 @@ export const createSecretHelper = async ({
|
|||||||
numberOfSecrets: 1,
|
numberOfSecrets: 1,
|
||||||
environment,
|
environment,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
folderId,
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
userAgent: authData.authUserAgent,
|
userAgent: authData.authUserAgent,
|
||||||
},
|
},
|
||||||
@@ -398,16 +444,30 @@ export const createSecretHelper = async ({
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getSecretsHelper = async ({
|
export const getSecretsHelper = async ({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
environment,
|
environment,
|
||||||
authData
|
authData,
|
||||||
|
secretPath = "/",
|
||||||
}: GetSecretsParams) => {
|
}: GetSecretsParams) => {
|
||||||
let secrets: ISecret[] = [];
|
let secrets: ISecret[] = [];
|
||||||
|
// if using service token filter towards the folderId by secretpath
|
||||||
|
if (authData.authPayload instanceof ServiceTokenData) {
|
||||||
|
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
||||||
|
if (secretPath !== serviceTkScopedSecretPath) {
|
||||||
|
throw new Error("Folder Permission Denied");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const folderId = await getFolderIdFromServiceToken(
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
secretPath
|
||||||
|
);
|
||||||
|
|
||||||
// get personal secrets first
|
// get personal secrets first
|
||||||
secrets = await Secret.find({
|
secrets = await Secret.find({
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
|
folder: folderId,
|
||||||
type: SECRET_PERSONAL,
|
type: SECRET_PERSONAL,
|
||||||
...getAuthDataPayloadUserObj(authData),
|
...getAuthDataPayloadUserObj(authData),
|
||||||
});
|
});
|
||||||
@@ -417,6 +477,7 @@ export const getSecretsHelper = async ({
|
|||||||
await Secret.find({
|
await Secret.find({
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
|
folder: folderId,
|
||||||
type: SECRET_SHARED,
|
type: SECRET_SHARED,
|
||||||
secretBlindIndex: {
|
secretBlindIndex: {
|
||||||
$nin: secrets.map((secret) => secret.secretBlindIndex),
|
$nin: secrets.map((secret) => secret.secretBlindIndex),
|
||||||
@@ -445,7 +506,7 @@ export const getSecretsHelper = async ({
|
|||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets pulled',
|
event: "secrets pulled",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData,
|
authData,
|
||||||
}),
|
}),
|
||||||
@@ -453,6 +514,7 @@ export const getSecretsHelper = async ({
|
|||||||
numberOfSecrets: secrets.length,
|
numberOfSecrets: secrets.length,
|
||||||
environment,
|
environment,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
folderId,
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
userAgent: authData.authUserAgent,
|
userAgent: authData.authUserAgent,
|
||||||
},
|
},
|
||||||
@@ -478,18 +540,32 @@ export const getSecretHelper = async ({
|
|||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData,
|
authData,
|
||||||
|
secretPath = "/",
|
||||||
}: GetSecretParams) => {
|
}: GetSecretParams) => {
|
||||||
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
});
|
});
|
||||||
let secret: ISecret | null = null;
|
let secret: ISecret | null = null;
|
||||||
|
// if using service token filter towards the folderId by secretpath
|
||||||
|
if (authData.authPayload instanceof ServiceTokenData) {
|
||||||
|
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
||||||
|
if (secretPath !== serviceTkScopedSecretPath) {
|
||||||
|
throw new Error("Folder Permission Denied");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const folderId = await getFolderIdFromServiceToken(
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
secretPath
|
||||||
|
);
|
||||||
|
|
||||||
// try getting personal secret first (if exists)
|
// try getting personal secret first (if exists)
|
||||||
secret = await Secret.findOne({
|
secret = await Secret.findOne({
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
|
folder: folderId,
|
||||||
type: type ?? SECRET_PERSONAL,
|
type: type ?? SECRET_PERSONAL,
|
||||||
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
||||||
});
|
});
|
||||||
@@ -501,6 +577,7 @@ export const getSecretHelper = async ({
|
|||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
|
folder: folderId,
|
||||||
type: SECRET_SHARED,
|
type: SECRET_SHARED,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -528,7 +605,7 @@ export const getSecretHelper = async ({
|
|||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets pull',
|
event: "secrets pull",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData,
|
authData,
|
||||||
}),
|
}),
|
||||||
@@ -536,6 +613,7 @@ export const getSecretHelper = async ({
|
|||||||
numberOfSecrets: 1,
|
numberOfSecrets: 1,
|
||||||
environment,
|
environment,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
folderId,
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
userAgent: authData.authUserAgent,
|
userAgent: authData.authUserAgent,
|
||||||
},
|
},
|
||||||
@@ -568,6 +646,7 @@ export const updateSecretHelper = async ({
|
|||||||
secretValueCiphertext,
|
secretValueCiphertext,
|
||||||
secretValueIV,
|
secretValueIV,
|
||||||
secretValueTag,
|
secretValueTag,
|
||||||
|
secretPath,
|
||||||
}: UpdateSecretParams) => {
|
}: UpdateSecretParams) => {
|
||||||
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
||||||
secretName,
|
secretName,
|
||||||
@@ -575,6 +654,18 @@ export const updateSecretHelper = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
let secret: ISecret | null = null;
|
let secret: ISecret | null = null;
|
||||||
|
// if using service token filter towards the folderId by secretpath
|
||||||
|
if (authData.authPayload instanceof ServiceTokenData) {
|
||||||
|
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
||||||
|
if (secretPath !== serviceTkScopedSecretPath) {
|
||||||
|
throw new Error("Folder Permission Denied");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const folderId = await getFolderIdFromServiceToken(
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
secretPath
|
||||||
|
);
|
||||||
|
|
||||||
if (type === SECRET_SHARED) {
|
if (type === SECRET_SHARED) {
|
||||||
// case: update shared secret
|
// case: update shared secret
|
||||||
@@ -583,6 +674,7 @@ export const updateSecretHelper = async ({
|
|||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
|
folder: folderId,
|
||||||
type,
|
type,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -604,6 +696,7 @@ export const updateSecretHelper = async ({
|
|||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
|
folder: folderId,
|
||||||
...getAuthDataPayloadUserObj(authData),
|
...getAuthDataPayloadUserObj(authData),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -624,6 +717,7 @@ export const updateSecretHelper = async ({
|
|||||||
secret: secret._id,
|
secret: secret._id,
|
||||||
version: secret.version,
|
version: secret.version,
|
||||||
workspace: secret.workspace,
|
workspace: secret.workspace,
|
||||||
|
folder: folderId,
|
||||||
type,
|
type,
|
||||||
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
|
||||||
environment: secret.environment,
|
environment: secret.environment,
|
||||||
@@ -672,7 +766,7 @@ export const updateSecretHelper = async ({
|
|||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets modified',
|
event: "secrets modified",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData,
|
authData,
|
||||||
}),
|
}),
|
||||||
@@ -680,6 +774,7 @@ export const updateSecretHelper = async ({
|
|||||||
numberOfSecrets: 1,
|
numberOfSecrets: 1,
|
||||||
environment,
|
environment,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
folderId,
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
userAgent: authData.authUserAgent,
|
userAgent: authData.authUserAgent,
|
||||||
},
|
},
|
||||||
@@ -705,12 +800,26 @@ export const deleteSecretHelper = async ({
|
|||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
authData,
|
authData,
|
||||||
|
secretPath = "/",
|
||||||
}: DeleteSecretParams) => {
|
}: DeleteSecretParams) => {
|
||||||
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
const secretBlindIndex = await generateSecretBlindIndexHelper({
|
||||||
secretName,
|
secretName,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// if using service token filter towards the folderId by secretpath
|
||||||
|
if (authData.authPayload instanceof ServiceTokenData) {
|
||||||
|
const { secretPath: serviceTkScopedSecretPath } = authData.authPayload;
|
||||||
|
if (secretPath !== serviceTkScopedSecretPath) {
|
||||||
|
throw new Error("Folder Permission Denied");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const folderId = await getFolderIdFromServiceToken(
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
secretPath
|
||||||
|
);
|
||||||
|
|
||||||
let secrets: ISecret[] = [];
|
let secrets: ISecret[] = [];
|
||||||
let secret: ISecret | null = null;
|
let secret: ISecret | null = null;
|
||||||
|
|
||||||
@@ -719,6 +828,7 @@ export const deleteSecretHelper = async ({
|
|||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
|
folder: folderId,
|
||||||
});
|
});
|
||||||
|
|
||||||
secret = await Secret.findOneAndDelete({
|
secret = await Secret.findOneAndDelete({
|
||||||
@@ -726,16 +836,19 @@ export const deleteSecretHelper = async ({
|
|||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
|
folder: folderId,
|
||||||
});
|
});
|
||||||
|
|
||||||
await Secret.deleteMany({
|
await Secret.deleteMany({
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
|
folder: folderId,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
secret = await Secret.findOneAndDelete({
|
secret = await Secret.findOneAndDelete({
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
|
folder: folderId,
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
type,
|
type,
|
||||||
@@ -782,7 +895,7 @@ export const deleteSecretHelper = async ({
|
|||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets deleted',
|
event: "secrets deleted",
|
||||||
distinctId: await TelemetryService.getDistinctId({
|
distinctId: await TelemetryService.getDistinctId({
|
||||||
authData,
|
authData,
|
||||||
}),
|
}),
|
||||||
@@ -790,6 +903,7 @@ export const deleteSecretHelper = async ({
|
|||||||
numberOfSecrets: secrets.length,
|
numberOfSecrets: secrets.length,
|
||||||
environment,
|
environment,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
folderId,
|
||||||
channel: authData.authChannel,
|
channel: authData.authChannel,
|
||||||
userAgent: authData.authUserAgent,
|
userAgent: authData.authUserAgent,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export interface CreateSecretParams {
|
|||||||
secretName: string;
|
secretName: string;
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
environment: string;
|
environment: string;
|
||||||
folderId?: string;
|
|
||||||
type: "shared" | "personal";
|
type: "shared" | "personal";
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
secretKeyCiphertext: string;
|
secretKeyCiphertext: string;
|
||||||
@@ -17,17 +16,20 @@ export interface CreateSecretParams {
|
|||||||
secretCommentCiphertext?: string;
|
secretCommentCiphertext?: string;
|
||||||
secretCommentIV?: string;
|
secretCommentIV?: string;
|
||||||
secretCommentTag?: string;
|
secretCommentTag?: string;
|
||||||
|
secretPath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetSecretsParams {
|
export interface GetSecretsParams {
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
environment: string;
|
environment: string;
|
||||||
|
secretPath: string;
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetSecretParams {
|
export interface GetSecretParams {
|
||||||
secretName: string;
|
secretName: string;
|
||||||
workspaceId: Types.ObjectId;
|
workspaceId: Types.ObjectId;
|
||||||
|
secretPath: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
type?: "shared" | "personal";
|
type?: "shared" | "personal";
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
@@ -42,7 +44,7 @@ export interface UpdateSecretParams {
|
|||||||
secretValueCiphertext: string;
|
secretValueCiphertext: string;
|
||||||
secretValueIV: string;
|
secretValueIV: string;
|
||||||
secretValueTag: string;
|
secretValueTag: string;
|
||||||
folderId?: string;
|
secretPath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeleteSecretParams {
|
export interface DeleteSecretParams {
|
||||||
@@ -51,4 +53,5 @@ export interface DeleteSecretParams {
|
|||||||
environment: string;
|
environment: string;
|
||||||
type: "shared" | "personal";
|
type: "shared" | "personal";
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
|
secretPath: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +1,88 @@
|
|||||||
import { Schema, model, Types, Document } from 'mongoose';
|
import { Schema, model, Types, Document } from "mongoose";
|
||||||
|
|
||||||
export interface IServiceTokenData extends Document {
|
export interface IServiceTokenData extends Document {
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
name: string;
|
name: string;
|
||||||
workspace: Types.ObjectId;
|
workspace: Types.ObjectId;
|
||||||
environment: string;
|
environment: string;
|
||||||
user: Types.ObjectId;
|
user: Types.ObjectId;
|
||||||
serviceAccount: Types.ObjectId;
|
serviceAccount: Types.ObjectId;
|
||||||
lastUsed: Date;
|
lastUsed: Date;
|
||||||
expiresAt: Date;
|
expiresAt: Date;
|
||||||
secretHash: string;
|
secretHash: string;
|
||||||
encryptedKey: string;
|
encryptedKey: string;
|
||||||
iv: string;
|
iv: string;
|
||||||
tag: string;
|
tag: string;
|
||||||
permissions: string[];
|
secretPath: string;
|
||||||
|
permissions: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const serviceTokenDataSchema = new Schema<IServiceTokenData>(
|
const serviceTokenDataSchema = new Schema<IServiceTokenData>(
|
||||||
{
|
{
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
},
|
|
||||||
workspace: {
|
|
||||||
type: Schema.Types.ObjectId,
|
|
||||||
ref: 'Workspace',
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
environment: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
type: Schema.Types.ObjectId,
|
|
||||||
ref: 'User',
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
serviceAccount: {
|
|
||||||
type: Schema.Types.ObjectId,
|
|
||||||
ref: 'ServiceAccount'
|
|
||||||
},
|
|
||||||
lastUsed: {
|
|
||||||
type: Date
|
|
||||||
},
|
|
||||||
expiresAt: {
|
|
||||||
type: Date
|
|
||||||
},
|
|
||||||
secretHash: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
select: false
|
|
||||||
},
|
|
||||||
encryptedKey: {
|
|
||||||
type: String,
|
|
||||||
select: false
|
|
||||||
},
|
|
||||||
iv: {
|
|
||||||
type: String,
|
|
||||||
select: false
|
|
||||||
},
|
|
||||||
tag: {
|
|
||||||
type: String,
|
|
||||||
select: false
|
|
||||||
},
|
|
||||||
permissions: {
|
|
||||||
type: [String],
|
|
||||||
enum: ['read', 'write'],
|
|
||||||
default: ['read']
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
workspace: {
|
||||||
timestamps: true
|
type: Schema.Types.ObjectId,
|
||||||
}
|
ref: "Workspace",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
environment: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: "User",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
serviceAccount: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: "ServiceAccount",
|
||||||
|
},
|
||||||
|
lastUsed: {
|
||||||
|
type: Date,
|
||||||
|
},
|
||||||
|
expiresAt: {
|
||||||
|
type: Date,
|
||||||
|
},
|
||||||
|
secretHash: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
select: false,
|
||||||
|
},
|
||||||
|
encryptedKey: {
|
||||||
|
type: String,
|
||||||
|
select: false,
|
||||||
|
},
|
||||||
|
iv: {
|
||||||
|
type: String,
|
||||||
|
select: false,
|
||||||
|
},
|
||||||
|
tag: {
|
||||||
|
type: String,
|
||||||
|
select: false,
|
||||||
|
},
|
||||||
|
permissions: {
|
||||||
|
type: [String],
|
||||||
|
enum: ["read", "write"],
|
||||||
|
default: ["read"],
|
||||||
|
},
|
||||||
|
secretPath: {
|
||||||
|
type: String,
|
||||||
|
default: "/",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const ServiceTokenData = model<IServiceTokenData>('ServiceTokenData', serviceTokenDataSchema);
|
const ServiceTokenData = model<IServiceTokenData>(
|
||||||
|
"ServiceTokenData",
|
||||||
|
serviceTokenDataSchema
|
||||||
|
);
|
||||||
|
|
||||||
export default ServiceTokenData;
|
export default ServiceTokenData;
|
||||||
|
|||||||
@@ -1,72 +1,79 @@
|
|||||||
import express from 'express';
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import {
|
||||||
requireAuth,
|
requireAuth,
|
||||||
requireWorkspaceAuth,
|
requireWorkspaceAuth,
|
||||||
requireServiceTokenDataAuth,
|
requireServiceTokenDataAuth,
|
||||||
validateRequest
|
validateRequest,
|
||||||
} from '../../middleware';
|
} from "../../middleware";
|
||||||
import { param, body } from 'express-validator';
|
import { param, body } from "express-validator";
|
||||||
import {
|
import {
|
||||||
ADMIN,
|
ADMIN,
|
||||||
MEMBER,
|
MEMBER,
|
||||||
PERMISSION_WRITE_SECRETS,
|
PERMISSION_WRITE_SECRETS,
|
||||||
AUTH_MODE_JWT,
|
AUTH_MODE_JWT,
|
||||||
AUTH_MODE_SERVICE_ACCOUNT,
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
AUTH_MODE_SERVICE_TOKEN
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
} from '../../variables';
|
} from "../../variables";
|
||||||
import { serviceTokenDataController } from '../../controllers/v2';
|
import { serviceTokenDataController } from "../../controllers/v2";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/',
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AUTH_MODE_SERVICE_TOKEN]
|
acceptedAuthModes: [AUTH_MODE_SERVICE_TOKEN],
|
||||||
}),
|
}),
|
||||||
serviceTokenDataController.getServiceTokenData
|
serviceTokenDataController.getServiceTokenData
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/',
|
"/",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT]
|
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT],
|
||||||
}),
|
}),
|
||||||
requireWorkspaceAuth({
|
requireWorkspaceAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
locationWorkspaceId: 'body',
|
locationWorkspaceId: "body",
|
||||||
locationEnvironment: 'body',
|
locationEnvironment: "body",
|
||||||
requiredPermissions: [PERMISSION_WRITE_SECRETS]
|
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
||||||
}),
|
}),
|
||||||
body('name').exists().isString().trim(),
|
body("name").exists().isString().trim(),
|
||||||
body('workspaceId').exists().isString().trim(),
|
body("workspaceId").exists().isString().trim(),
|
||||||
body('environment').exists().isString().trim(),
|
body("environment").exists().isString().trim(),
|
||||||
body('encryptedKey').exists().isString().trim(),
|
body("encryptedKey").exists().isString().trim(),
|
||||||
body('iv').exists().isString().trim(),
|
body("iv").exists().isString().trim(),
|
||||||
body('tag').exists().isString().trim(),
|
body("secretPath").isString().default("/").trim(),
|
||||||
body('expiresIn').exists().isNumeric(), // measured in ms
|
body("tag").exists().isString().trim(),
|
||||||
body('permissions').isArray({ min: 1 }).custom((value: string[]) => {
|
body("expiresIn").exists().isNumeric(), // measured in ms
|
||||||
const allowedPermissions = ['read', 'write'];
|
body("permissions")
|
||||||
const invalidValues = value.filter((v) => !allowedPermissions.includes(v));
|
.isArray({ min: 1 })
|
||||||
if (invalidValues.length > 0) {
|
.custom((value: string[]) => {
|
||||||
throw new Error(`permissions contains invalid values: ${invalidValues.join(', ')}`);
|
const allowedPermissions = ["read", "write"];
|
||||||
}
|
const invalidValues = value.filter(
|
||||||
|
(v) => !allowedPermissions.includes(v)
|
||||||
|
);
|
||||||
|
if (invalidValues.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`permissions contains invalid values: ${invalidValues.join(", ")}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true;
|
||||||
}),
|
}),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
serviceTokenDataController.createServiceTokenData
|
serviceTokenDataController.createServiceTokenData
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
'/:serviceTokenDataId',
|
"/:serviceTokenDataId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: [AUTH_MODE_JWT]
|
acceptedAuthModes: [AUTH_MODE_JWT],
|
||||||
}),
|
}),
|
||||||
requireServiceTokenDataAuth({
|
requireServiceTokenDataAuth({
|
||||||
acceptedRoles: [ADMIN, MEMBER]
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
}),
|
}),
|
||||||
param('serviceTokenDataId').exists().trim(),
|
param("serviceTokenDataId").exists().trim(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
serviceTokenDataController.deleteServiceTokenData
|
serviceTokenDataController.deleteServiceTokenData
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
+142
-137
@@ -1,157 +1,162 @@
|
|||||||
import express from 'express';
|
import express from "express";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
import {
|
import {
|
||||||
requireAuth,
|
requireAuth,
|
||||||
requireWorkspaceAuth,
|
requireWorkspaceAuth,
|
||||||
validateRequest
|
validateRequest,
|
||||||
} from '../../middleware';
|
} from "../../middleware";
|
||||||
import { body, param, query } from 'express-validator';
|
import { body, param, query } from "express-validator";
|
||||||
import { secretsController } from '../../controllers/v3';
|
import { secretsController } from "../../controllers/v3";
|
||||||
import {
|
import {
|
||||||
AUTH_MODE_JWT,
|
AUTH_MODE_JWT,
|
||||||
AUTH_MODE_API_KEY,
|
AUTH_MODE_API_KEY,
|
||||||
AUTH_MODE_SERVICE_TOKEN,
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
AUTH_MODE_SERVICE_ACCOUNT,
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
ADMIN,
|
ADMIN,
|
||||||
MEMBER,
|
MEMBER,
|
||||||
PERMISSION_WRITE_SECRETS,
|
PERMISSION_WRITE_SECRETS,
|
||||||
SECRET_SHARED,
|
SECRET_SHARED,
|
||||||
SECRET_PERSONAL,
|
SECRET_PERSONAL,
|
||||||
PERMISSION_READ_SECRETS
|
PERMISSION_READ_SECRETS,
|
||||||
} from '../../variables';
|
} from "../../variables";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/',
|
"/",
|
||||||
query('workspaceId').exists().isString().trim(),
|
query("workspaceId").exists().isString().trim(),
|
||||||
query('environment').exists().isString().trim(),
|
query("environment").exists().isString().trim(),
|
||||||
validateRequest,
|
query("secretPath").default("/").isString().trim(),
|
||||||
requireAuth({
|
validateRequest,
|
||||||
acceptedAuthModes: [
|
requireAuth({
|
||||||
AUTH_MODE_JWT,
|
acceptedAuthModes: [
|
||||||
AUTH_MODE_API_KEY,
|
AUTH_MODE_JWT,
|
||||||
AUTH_MODE_SERVICE_TOKEN,
|
AUTH_MODE_API_KEY,
|
||||||
AUTH_MODE_SERVICE_ACCOUNT
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
]
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
}),
|
],
|
||||||
requireWorkspaceAuth({
|
}),
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
requireWorkspaceAuth({
|
||||||
locationWorkspaceId: 'query',
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
locationEnvironment: 'query',
|
locationWorkspaceId: "query",
|
||||||
requiredPermissions: [PERMISSION_READ_SECRETS],
|
locationEnvironment: "query",
|
||||||
requireBlindIndicesEnabled: true,
|
requiredPermissions: [PERMISSION_READ_SECRETS],
|
||||||
}),
|
requireBlindIndicesEnabled: true,
|
||||||
secretsController.getSecrets
|
}),
|
||||||
|
secretsController.getSecrets
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/:secretName',
|
"/:secretName",
|
||||||
body('workspaceId').exists().isString().trim(),
|
body("workspaceId").exists().isString().trim(),
|
||||||
body('environment').exists().isString().trim(),
|
body("environment").exists().isString().trim(),
|
||||||
body('type').exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
body('secretKeyCiphertext').exists().isString().trim(),
|
body("secretKeyCiphertext").exists().isString().trim(),
|
||||||
body('secretKeyIV').exists().isString().trim(),
|
body("secretKeyIV").exists().isString().trim(),
|
||||||
body('secretKeyTag').exists().isString().trim(),
|
body("secretKeyTag").exists().isString().trim(),
|
||||||
body('secretValueCiphertext').exists().isString().trim(),
|
body("secretValueCiphertext").exists().isString().trim(),
|
||||||
body('secretValueIV').exists().isString().trim(),
|
body("secretValueIV").exists().isString().trim(),
|
||||||
body('secretValueTag').exists().isString().trim(),
|
body("secretValueTag").exists().isString().trim(),
|
||||||
body('secretCommentCiphertext').optional().isString().trim(),
|
body("secretCommentCiphertext").optional().isString().trim(),
|
||||||
body('secretCommentIV').optional().isString().trim(),
|
body("secretCommentIV").optional().isString().trim(),
|
||||||
body('secretCommentTag').optional().isString().trim(),
|
body("secretCommentTag").optional().isString().trim(),
|
||||||
validateRequest,
|
body("secretPath").default("/").isString().trim(),
|
||||||
requireAuth({
|
validateRequest,
|
||||||
acceptedAuthModes: [
|
requireAuth({
|
||||||
AUTH_MODE_JWT,
|
acceptedAuthModes: [
|
||||||
AUTH_MODE_API_KEY,
|
AUTH_MODE_JWT,
|
||||||
AUTH_MODE_SERVICE_TOKEN,
|
AUTH_MODE_API_KEY,
|
||||||
AUTH_MODE_SERVICE_ACCOUNT
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
]
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
}),
|
],
|
||||||
requireWorkspaceAuth({
|
}),
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
requireWorkspaceAuth({
|
||||||
locationWorkspaceId: 'body',
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
locationEnvironment: 'body',
|
locationWorkspaceId: "body",
|
||||||
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
locationEnvironment: "body",
|
||||||
requireBlindIndicesEnabled: true,
|
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
||||||
}),
|
requireBlindIndicesEnabled: true,
|
||||||
secretsController.createSecret
|
}),
|
||||||
|
secretsController.createSecret
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/:secretName',
|
"/:secretName",
|
||||||
param('secretName').exists().isString().trim(),
|
param("secretName").exists().isString().trim(),
|
||||||
query('workspaceId').exists().isString().trim(),
|
query("workspaceId").exists().isString().trim(),
|
||||||
query('environment').exists().isString().trim(),
|
query("environment").exists().isString().trim(),
|
||||||
query('type').optional().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
query("secretPath").default("/").isString().trim(),
|
||||||
validateRequest,
|
query("type").optional().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
requireAuth({
|
validateRequest,
|
||||||
acceptedAuthModes: [
|
requireAuth({
|
||||||
AUTH_MODE_JWT,
|
acceptedAuthModes: [
|
||||||
AUTH_MODE_API_KEY,
|
AUTH_MODE_JWT,
|
||||||
AUTH_MODE_SERVICE_TOKEN,
|
AUTH_MODE_API_KEY,
|
||||||
AUTH_MODE_SERVICE_ACCOUNT
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
]
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
}),
|
],
|
||||||
requireWorkspaceAuth({
|
}),
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
requireWorkspaceAuth({
|
||||||
locationWorkspaceId: 'query',
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
locationEnvironment: 'query',
|
locationWorkspaceId: "query",
|
||||||
requiredPermissions: [PERMISSION_READ_SECRETS],
|
locationEnvironment: "query",
|
||||||
requireBlindIndicesEnabled: true,
|
requiredPermissions: [PERMISSION_READ_SECRETS],
|
||||||
}),
|
requireBlindIndicesEnabled: true,
|
||||||
secretsController.getSecretByName
|
}),
|
||||||
|
secretsController.getSecretByName
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
'/:secretName',
|
"/:secretName",
|
||||||
param('secretName').exists().isString().trim(),
|
param("secretName").exists().isString().trim(),
|
||||||
body('workspaceId').exists().isString().trim(),
|
body("workspaceId").exists().isString().trim(),
|
||||||
body('environment').exists().isString().trim(),
|
body("environment").exists().isString().trim(),
|
||||||
body('type').exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
body('secretValueCiphertext').exists().isString().trim(),
|
body("secretValueCiphertext").exists().isString().trim(),
|
||||||
body('secretValueIV').exists().isString().trim(),
|
body("secretValueIV").exists().isString().trim(),
|
||||||
body('secretValueTag').exists().isString().trim(),
|
body("secretValueTag").exists().isString().trim(),
|
||||||
validateRequest,
|
body("secretPath").default("/").isString().trim(),
|
||||||
requireAuth({
|
validateRequest,
|
||||||
acceptedAuthModes: [
|
requireAuth({
|
||||||
AUTH_MODE_JWT,
|
acceptedAuthModes: [
|
||||||
AUTH_MODE_API_KEY,
|
AUTH_MODE_JWT,
|
||||||
AUTH_MODE_SERVICE_TOKEN,
|
AUTH_MODE_API_KEY,
|
||||||
AUTH_MODE_SERVICE_ACCOUNT
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
]
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
}),
|
],
|
||||||
requireWorkspaceAuth({
|
}),
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
requireWorkspaceAuth({
|
||||||
locationWorkspaceId: 'body',
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
locationEnvironment: 'body',
|
locationWorkspaceId: "body",
|
||||||
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
locationEnvironment: "body",
|
||||||
requireBlindIndicesEnabled: true,
|
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
||||||
}),
|
requireBlindIndicesEnabled: true,
|
||||||
secretsController.updateSecretByName
|
}),
|
||||||
|
secretsController.updateSecretByName
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
'/:secretName',
|
"/:secretName",
|
||||||
param('secretName').exists().isString().trim(),
|
param("secretName").exists().isString().trim(),
|
||||||
body('workspaceId').exists().isString().trim(),
|
body("workspaceId").exists().isString().trim(),
|
||||||
body('environment').exists().isString().trim(),
|
body("environment").exists().isString().trim(),
|
||||||
body('type').exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
body("secretPath").default("/").isString().trim(),
|
||||||
validateRequest,
|
body("type").exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
|
||||||
requireAuth({
|
validateRequest,
|
||||||
acceptedAuthModes: [
|
requireAuth({
|
||||||
AUTH_MODE_JWT,
|
acceptedAuthModes: [
|
||||||
AUTH_MODE_API_KEY,
|
AUTH_MODE_JWT,
|
||||||
AUTH_MODE_SERVICE_TOKEN,
|
AUTH_MODE_API_KEY,
|
||||||
AUTH_MODE_SERVICE_ACCOUNT
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
]
|
AUTH_MODE_SERVICE_ACCOUNT,
|
||||||
}),
|
],
|
||||||
requireWorkspaceAuth({
|
}),
|
||||||
acceptedRoles: [ADMIN, MEMBER],
|
requireWorkspaceAuth({
|
||||||
locationWorkspaceId: 'body',
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
locationEnvironment: 'body',
|
locationWorkspaceId: "body",
|
||||||
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
locationEnvironment: "body",
|
||||||
requireBlindIndicesEnabled: true,
|
requiredPermissions: [PERMISSION_WRITE_SECRETS],
|
||||||
}),
|
requireBlindIndicesEnabled: true,
|
||||||
secretsController.deleteSecretByName
|
}),
|
||||||
|
secretsController.deleteSecretByName
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -174,6 +174,11 @@ export const searchByFolderIdWithDir = (
|
|||||||
// to get folder of a path given
|
// to get folder of a path given
|
||||||
// Like /frontend/folder#1
|
// Like /frontend/folder#1
|
||||||
export const getFolderByPath = (folders: TFolderSchema, searchPath: string) => {
|
export const getFolderByPath = (folders: TFolderSchema, searchPath: string) => {
|
||||||
|
// corner case when its just / return root
|
||||||
|
if (searchPath === "/") {
|
||||||
|
return folders.id === "root" ? folders : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const path = searchPath.split("/").filter(Boolean);
|
const path = searchPath.split("/").filter(Boolean);
|
||||||
const queue = [folders];
|
const queue = [folders];
|
||||||
let segment: TFolderSchema | undefined;
|
let segment: TFolderSchema | undefined;
|
||||||
|
|||||||
@@ -1,171 +1,169 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from "mongoose";
|
||||||
|
import { ISecret } from "../models";
|
||||||
import {
|
import {
|
||||||
ISecret
|
CreateSecretParams,
|
||||||
} from '../models';
|
GetSecretsParams,
|
||||||
|
GetSecretParams,
|
||||||
|
UpdateSecretParams,
|
||||||
|
DeleteSecretParams,
|
||||||
|
} from "../interfaces/services/SecretService";
|
||||||
import {
|
import {
|
||||||
CreateSecretParams,
|
createSecretBlindIndexDataHelper,
|
||||||
GetSecretsParams,
|
getSecretBlindIndexSaltHelper,
|
||||||
GetSecretParams,
|
generateSecretBlindIndexWithSaltHelper,
|
||||||
UpdateSecretParams,
|
generateSecretBlindIndexHelper,
|
||||||
DeleteSecretParams
|
createSecretHelper,
|
||||||
} from '../interfaces/services/SecretService';
|
getSecretsHelper,
|
||||||
import {
|
getSecretHelper,
|
||||||
createSecretBlindIndexDataHelper,
|
updateSecretHelper,
|
||||||
getSecretBlindIndexSaltHelper,
|
deleteSecretHelper,
|
||||||
generateSecretBlindIndexWithSaltHelper,
|
} from "../helpers/secrets";
|
||||||
generateSecretBlindIndexHelper,
|
|
||||||
createSecretHelper,
|
|
||||||
getSecretsHelper,
|
|
||||||
getSecretHelper,
|
|
||||||
updateSecretHelper,
|
|
||||||
deleteSecretHelper
|
|
||||||
} from '../helpers/secrets';
|
|
||||||
|
|
||||||
class SecretService {
|
class SecretService {
|
||||||
|
/**
|
||||||
|
* Create secret blind index data containing encrypted blind index salt
|
||||||
|
* for workspace with id [workspaceId]
|
||||||
|
* @param {Object} obj
|
||||||
|
* @param {Buffer} obj.salt - 16-byte random salt
|
||||||
|
* @param {Types.ObjectId} obj.workspaceId
|
||||||
|
*/
|
||||||
|
static async createSecretBlindIndexData({
|
||||||
|
workspaceId,
|
||||||
|
}: {
|
||||||
|
workspaceId: Types.ObjectId;
|
||||||
|
}) {
|
||||||
|
return await createSecretBlindIndexDataHelper({
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create secret blind index data containing encrypted blind index salt
|
* Get secret blind index salt for workspace with id [workspaceId]
|
||||||
* for workspace with id [workspaceId]
|
* @param {Object} obj
|
||||||
* @param {Object} obj
|
* @param {Types.ObjectId} obj.workspaceId - id of workspace to get salt for
|
||||||
* @param {Buffer} obj.salt - 16-byte random salt
|
* @returns
|
||||||
* @param {Types.ObjectId} obj.workspaceId
|
*/
|
||||||
*/
|
static async getSecretBlindIndexSalt({
|
||||||
static async createSecretBlindIndexData({
|
workspaceId,
|
||||||
workspaceId,
|
}: {
|
||||||
}: {
|
workspaceId: Types.ObjectId;
|
||||||
workspaceId: Types.ObjectId;
|
}) {
|
||||||
}) {
|
return await getSecretBlindIndexSaltHelper({
|
||||||
return await createSecretBlindIndexDataHelper({
|
workspaceId,
|
||||||
workspaceId
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get secret blind index salt for workspace with id [workspaceId]
|
* Generate blind index for secret with name [secretName]
|
||||||
* @param {Object} obj
|
* and salt [salt]
|
||||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace to get salt for
|
* @param {Object} obj
|
||||||
* @returns
|
* @param {Object} obj.secretName - name of secret to generate blind index for
|
||||||
*/
|
* @param {String} obj.salt - base64-salt
|
||||||
static async getSecretBlindIndexSalt({
|
*/
|
||||||
workspaceId
|
static async generateSecretBlindIndexWithSalt({
|
||||||
}: {
|
secretName,
|
||||||
workspaceId: Types.ObjectId;
|
salt,
|
||||||
}) {
|
}: {
|
||||||
return await getSecretBlindIndexSaltHelper({
|
secretName: string;
|
||||||
workspaceId
|
salt: string;
|
||||||
});
|
}) {
|
||||||
}
|
return await generateSecretBlindIndexWithSaltHelper({
|
||||||
|
secretName,
|
||||||
|
salt,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate blind index for secret with name [secretName]
|
* Create and return blind index for secret with
|
||||||
* and salt [salt]
|
* name [secretName] part of workspace with id [workspaceId]
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {Object} obj.secretName - name of secret to generate blind index for
|
* @param {String} obj.secretName - name of secret to generate blind index for
|
||||||
* @param {String} obj.salt - base64-salt
|
* @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to
|
||||||
*/
|
*/
|
||||||
static async generateSecretBlindIndexWithSalt({
|
static async generateSecretBlindIndex({
|
||||||
secretName,
|
secretName,
|
||||||
salt
|
workspaceId,
|
||||||
}: {
|
}: {
|
||||||
secretName: string;
|
secretName: string;
|
||||||
salt: string;
|
workspaceId: Types.ObjectId;
|
||||||
}) {
|
}) {
|
||||||
return await generateSecretBlindIndexWithSaltHelper({
|
return await generateSecretBlindIndexHelper({
|
||||||
secretName,
|
secretName,
|
||||||
salt
|
workspaceId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and return blind index for secret with
|
* Create secret with name [secretName]
|
||||||
* name [secretName] part of workspace with id [workspaceId]
|
* @param {Object} obj
|
||||||
* @param {Object} obj
|
* @param {String} obj.secretName - name of secret to create
|
||||||
* @param {String} obj.secretName - name of secret to generate blind index for
|
* @param {Types.ObjectId} obj.workspaceId - id of workspace to create secret for
|
||||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to
|
* @param {String} obj.environment - environment in workspace to create secret for
|
||||||
*/
|
* @param {'shared' | 'personal'} obj.type - type of secret
|
||||||
static async generateSecretBlindIndex({
|
* @param {AuthData} obj.authData - authentication data on request
|
||||||
secretName,
|
* @returns
|
||||||
workspaceId,
|
*/
|
||||||
}: {
|
static async createSecret(createSecretParams: CreateSecretParams) {
|
||||||
secretName: string;
|
return await createSecretHelper(createSecretParams);
|
||||||
workspaceId: Types.ObjectId;
|
}
|
||||||
}) {
|
|
||||||
return await generateSecretBlindIndexHelper({
|
|
||||||
secretName,
|
|
||||||
workspaceId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create secret with name [secretName]
|
* Get secrets for workspace with id [workspaceId] and environment [environment]
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {String} obj.secretName - name of secret to create
|
* @param {Types.ObjectId} obj.workspaceId - id of workspace
|
||||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace to create secret for
|
* @param {String} obj.environment - environment in workspace
|
||||||
* @param {String} obj.environment - environment in workspace to create secret for
|
* @param {AuthData} obj.authData - authentication data on request
|
||||||
* @param {'shared' | 'personal'} obj.type - type of secret
|
* @returns
|
||||||
* @param {AuthData} obj.authData - authentication data on request
|
*/
|
||||||
* @returns
|
static async getSecrets(getSecretsParams: GetSecretsParams) {
|
||||||
*/
|
return await getSecretsHelper(getSecretsParams);
|
||||||
static async createSecret(createSecretParams: CreateSecretParams) {
|
}
|
||||||
return await createSecretHelper(createSecretParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get secrets for workspace with id [workspaceId] and environment [environment]
|
* Get secret with name [secretName]
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace
|
* @param {String} obj.secretName - name of secret to get
|
||||||
* @param {String} obj.environment - environment in workspace
|
* @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to
|
||||||
* @param {AuthData} obj.authData - authentication data on request
|
* @param {String} obj.environment - environment in workspace that secret belongs to
|
||||||
* @returns
|
* @param {'shared' | 'personal'} obj.type - type of secret
|
||||||
*/
|
* @param {AuthData} obj.authData - authentication data on request
|
||||||
static async getSecrets(getSecretsParams: GetSecretsParams) {
|
* @returns
|
||||||
return await getSecretsHelper(getSecretsParams);
|
*/
|
||||||
}
|
static async getSecret(getSecretParams: GetSecretParams) {
|
||||||
|
// TODO(akhilmhdh) The one above is diff. Change this to some other name
|
||||||
|
return await getSecretHelper(getSecretParams);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get secret with name [secretName]
|
* Update secret with name [secretName]
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {String} obj.secretName - name of secret to get
|
* @param {String} obj.secretName - name of secret to update
|
||||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to
|
* @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to
|
||||||
* @param {String} obj.environment - environment in workspace that secret belongs to
|
* @param {String} obj.environment - environment in workspace that secret belongs to
|
||||||
* @param {'shared' | 'personal'} obj.type - type of secret
|
* @param {'shared' | 'personal'} obj.type - type of secret
|
||||||
* @param {AuthData} obj.authData - authentication data on request
|
* @param {String} obj.secretValueCiphertext - ciphertext of secret value
|
||||||
* @returns
|
* @param {String} obj.secretValueIV - IV of secret value
|
||||||
*/
|
* @param {String} obj.secretValueTag - tag of secret value
|
||||||
static async getSecret(getSecretParams: GetSecretParams) {
|
* @param {AuthData} obj.authData - authentication data on request
|
||||||
return await getSecretHelper(getSecretParams);
|
* @returns
|
||||||
}
|
*/
|
||||||
|
static async updateSecret(updateSecretParams: UpdateSecretParams) {
|
||||||
|
return await updateSecretHelper(updateSecretParams);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update secret with name [secretName]
|
* Delete secret with name [secretName]
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {String} obj.secretName - name of secret to update
|
* @param {String} obj.secretName - name of secret to delete
|
||||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to
|
* @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to
|
||||||
* @param {String} obj.environment - environment in workspace that secret belongs to
|
* @param {String} obj.environment - environment in workspace that secret belongs to
|
||||||
* @param {'shared' | 'personal'} obj.type - type of secret
|
* @param {'shared' | 'personal'} obj.type - type of secret
|
||||||
* @param {String} obj.secretValueCiphertext - ciphertext of secret value
|
* @param {AuthData} obj.authData - authentication data on request
|
||||||
* @param {String} obj.secretValueIV - IV of secret value
|
* @returns
|
||||||
* @param {String} obj.secretValueTag - tag of secret value
|
*/
|
||||||
* @param {AuthData} obj.authData - authentication data on request
|
static async deleteSecret(deleteSecretParams: DeleteSecretParams) {
|
||||||
* @returns
|
return await deleteSecretHelper(deleteSecretParams);
|
||||||
*/
|
}
|
||||||
static async updateSecret(updateSecretParams: UpdateSecretParams) {
|
|
||||||
return await updateSecretHelper(updateSecretParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete secret with name [secretName]
|
|
||||||
* @param {Object} obj
|
|
||||||
* @param {String} obj.secretName - name of secret to delete
|
|
||||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace that secret belongs to
|
|
||||||
* @param {String} obj.environment - environment in workspace that secret belongs to
|
|
||||||
* @param {'shared' | 'personal'} obj.type - type of secret
|
|
||||||
* @param {AuthData} obj.authData - authentication data on request
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
static async deleteSecret(deleteSecretParams: DeleteSecretParams) {
|
|
||||||
return await deleteSecretHelper(deleteSecretParams);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SecretService;
|
export default SecretService;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { encryptSymmetric128BitHexKeyUTF8 } from "../crypto";
|
import { encryptSymmetric128BitHexKeyUTF8 } from "../crypto";
|
||||||
@@ -11,6 +12,7 @@ import {
|
|||||||
Bot,
|
Bot,
|
||||||
BackupPrivateKey,
|
BackupPrivateKey,
|
||||||
IntegrationAuth,
|
IntegrationAuth,
|
||||||
|
ServiceTokenData,
|
||||||
} from "../../models";
|
} from "../../models";
|
||||||
import { generateKeyPair } from "../../utils/crypto";
|
import { generateKeyPair } from "../../utils/crypto";
|
||||||
import { client, getEncryptionKey, getRootEncryptionKey } from "../../config";
|
import { client, getEncryptionKey, getRootEncryptionKey } from "../../config";
|
||||||
@@ -64,7 +66,7 @@ export const backfillSecretVersions = async () => {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log("Migration: Secret version migration v1 complete")
|
console.log("Migration: Secret version migration v1 complete");
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -380,13 +382,15 @@ export const backfillSecretFolders = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const newSnapshots = Object.keys(groupSnapByEnv).map((snapEnv) => {
|
const newSnapshots = Object.keys(groupSnapByEnv).map((snapEnv) => {
|
||||||
const secretIdsOfEnvGroup = groupSnapByEnv[snapEnv] ? groupSnapByEnv[snapEnv].map(secretVersion => secretVersion._id) : []
|
const secretIdsOfEnvGroup = groupSnapByEnv[snapEnv]
|
||||||
|
? groupSnapByEnv[snapEnv].map((secretVersion) => secretVersion._id)
|
||||||
|
: [];
|
||||||
return {
|
return {
|
||||||
...secSnapshot.toObject({ virtuals: false }),
|
...secSnapshot.toObject({ virtuals: false }),
|
||||||
_id: new Types.ObjectId(),
|
_id: new Types.ObjectId(),
|
||||||
environment: snapEnv,
|
environment: snapEnv,
|
||||||
secretVersions: secretIdsOfEnvGroup,
|
secretVersions: secretIdsOfEnvGroup,
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
await SecretSnapshot.insertMany(newSnapshots);
|
await SecretSnapshot.insertMany(newSnapshots);
|
||||||
@@ -402,5 +406,22 @@ export const backfillSecretFolders = async () => {
|
|||||||
.limit(50);
|
.limit(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Migration: Folder migration v1 complete")
|
console.log("Migration: Folder migration v1 complete");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const backfillServiceToken = async () => {
|
||||||
|
// Back fill because tags were missing in secret versions
|
||||||
|
await ServiceTokenData.updateMany(
|
||||||
|
{
|
||||||
|
secretPath: {
|
||||||
|
$exists: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
secretPath: "/",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log("Migration: Service token migration v1 complete");
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,30 +1,31 @@
|
|||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from "@sentry/node";
|
||||||
import { DatabaseService, TelemetryService } from '../../services';
|
import { DatabaseService, TelemetryService } from "../../services";
|
||||||
import { setTransporter } from '../../helpers/nodemailer';
|
import { setTransporter } from "../../helpers/nodemailer";
|
||||||
import { EELicenseService } from '../../ee/services';
|
import { EELicenseService } from "../../ee/services";
|
||||||
import { initSmtp } from '../../services/smtp';
|
import { initSmtp } from "../../services/smtp";
|
||||||
import { createTestUserForDevelopment } from '../addDevelopmentUser';
|
import { createTestUserForDevelopment } from "../addDevelopmentUser";
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
import { validateEncryptionKeysConfig } from './validateConfig';
|
import { validateEncryptionKeysConfig } from "./validateConfig";
|
||||||
import {
|
import {
|
||||||
backfillSecretVersions,
|
backfillSecretVersions,
|
||||||
backfillBots,
|
backfillBots,
|
||||||
backfillSecretBlindIndexData,
|
backfillSecretBlindIndexData,
|
||||||
backfillEncryptionMetadata,
|
backfillEncryptionMetadata,
|
||||||
backfillSecretFolders,
|
backfillSecretFolders,
|
||||||
} from './backfillData';
|
backfillServiceToken,
|
||||||
|
} from "./backfillData";
|
||||||
import {
|
import {
|
||||||
reencryptBotPrivateKeys,
|
reencryptBotPrivateKeys,
|
||||||
reencryptSecretBlindIndexDataSalts,
|
reencryptSecretBlindIndexDataSalts,
|
||||||
} from './reencryptData';
|
} from "./reencryptData";
|
||||||
import {
|
import {
|
||||||
getNodeEnv,
|
getNodeEnv,
|
||||||
getMongoURL,
|
getMongoURL,
|
||||||
getSentryDSN,
|
getSentryDSN,
|
||||||
getClientSecretGoogle,
|
getClientSecretGoogle,
|
||||||
getClientIdGoogle,
|
getClientIdGoogle,
|
||||||
} from '../../config';
|
} from "../../config";
|
||||||
import { initializePassport } from '../auth';
|
import { initializePassport } from "../auth";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare Infisical upon startup. This includes tasks like:
|
* Prepare Infisical upon startup. This includes tasks like:
|
||||||
@@ -75,6 +76,7 @@ export const setup = async () => {
|
|||||||
await backfillSecretBlindIndexData();
|
await backfillSecretBlindIndexData();
|
||||||
await backfillEncryptionMetadata();
|
await backfillEncryptionMetadata();
|
||||||
await backfillSecretFolders();
|
await backfillSecretFolders();
|
||||||
|
await backfillServiceToken();
|
||||||
|
|
||||||
// re-encrypt any data previously encrypted under server hex 128-bit ENCRYPTION_KEY
|
// re-encrypt any data previously encrypted under server hex 128-bit ENCRYPTION_KEY
|
||||||
// to base64 256-bit ROOT_ENCRYPTION_KEY
|
// to base64 256-bit ROOT_ENCRYPTION_KEY
|
||||||
@@ -85,7 +87,7 @@ export const setup = async () => {
|
|||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: await getSentryDSN(),
|
dsn: await getSentryDSN(),
|
||||||
tracesSampleRate: 1.0,
|
tracesSampleRate: 1.0,
|
||||||
debug: (await getNodeEnv()) === 'production' ? false : true,
|
debug: (await getNodeEnv()) === "production" ? false : true,
|
||||||
environment: await getNodeEnv(),
|
environment: await getNodeEnv(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user