mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-17 16:27:39 +00:00
Merge branch 'main' into activity-logs
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import * as workspaceController from './workspaceController';
|
||||
import * as serviceTokenDataController from './serviceTokenDataController';
|
||||
import * as apiKeyDataController from './apiKeyDataController';
|
||||
import * as secretController from './secretController';
|
||||
|
||||
export {
|
||||
export {
|
||||
workspaceController,
|
||||
serviceTokenDataController,
|
||||
apiKeyDataController
|
||||
apiKeyDataController,
|
||||
secretController
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
import to from "await-to-js";
|
||||
import { Request, Response } from "express";
|
||||
import mongoose, { Types } from "mongoose";
|
||||
import Secret, { ISecret } from "../../models/secret";
|
||||
import { CreateSecretRequestBody, ModifySecretRequestBody, SanitizedSecretForCreate, SanitizedSecretModify } from "../../types/secret/types";
|
||||
const { ValidationError } = mongoose.Error;
|
||||
import { BadRequestError, InternalServerError, UnauthorizedRequestError, ValidationError as RouteValidationError } from '../../utils/errors';
|
||||
import { AnyBulkWriteOperation } from 'mongodb';
|
||||
import { SECRET_PERSONAL, SECRET_SHARED } from "../../variables";
|
||||
|
||||
export const batchCreateSecrets = async (req: Request, res: Response) => {
|
||||
const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets;
|
||||
const { workspaceId, environmentName } = req.params
|
||||
const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = []
|
||||
|
||||
secretsToCreate.forEach(rawSecret => {
|
||||
const safeUpdateFields: SanitizedSecretForCreate = {
|
||||
secretKeyCiphertext: rawSecret.secretKeyCiphertext,
|
||||
secretKeyIV: rawSecret.secretKeyIV,
|
||||
secretKeyTag: rawSecret.secretKeyTag,
|
||||
secretKeyHash: rawSecret.secretKeyHash,
|
||||
secretValueCiphertext: rawSecret.secretValueCiphertext,
|
||||
secretValueIV: rawSecret.secretValueIV,
|
||||
secretValueTag: rawSecret.secretValueTag,
|
||||
secretValueHash: rawSecret.secretValueHash,
|
||||
secretCommentCiphertext: rawSecret.secretCommentCiphertext,
|
||||
secretCommentIV: rawSecret.secretCommentIV,
|
||||
secretCommentTag: rawSecret.secretCommentTag,
|
||||
secretCommentHash: rawSecret.secretCommentHash,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment: environmentName,
|
||||
type: rawSecret.type,
|
||||
user: new Types.ObjectId(req.user._id)
|
||||
}
|
||||
|
||||
sanitizedSecretesToCreate.push(safeUpdateFields)
|
||||
})
|
||||
|
||||
const [bulkCreateError, newlyCreatedSecrets] = await to(Secret.insertMany(sanitizedSecretesToCreate).then())
|
||||
if (bulkCreateError) {
|
||||
if (bulkCreateError instanceof ValidationError) {
|
||||
throw RouteValidationError({ message: bulkCreateError.message, stack: bulkCreateError.stack })
|
||||
}
|
||||
|
||||
throw InternalServerError({ message: "Unable to process your batch create request. Please try again", stack: bulkCreateError.stack })
|
||||
}
|
||||
|
||||
res.status(200).send()
|
||||
}
|
||||
|
||||
|
||||
export const createSingleSecret = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const secretFromDB = await Secret.findById(req.params.secretId)
|
||||
return res.status(200).send(secretFromDB);
|
||||
} catch (e) {
|
||||
throw BadRequestError({ message: "Unable to find the requested secret" })
|
||||
}
|
||||
}
|
||||
|
||||
export const batchDeleteSecrets = async (req: Request, res: Response) => {
|
||||
const { workspaceId, environmentName } = req.params
|
||||
const secretIdsToDelete: string[] = req.body.secretIds
|
||||
|
||||
const [secretIdsUserCanDeleteError, secretIdsUserCanDelete] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
|
||||
if (secretIdsUserCanDeleteError) {
|
||||
throw InternalServerError({ message: `Unable to fetch secrets you own: [error=${secretIdsUserCanDeleteError.message}]` })
|
||||
}
|
||||
|
||||
const secretsUserCanDeleteSet: Set<string> = new Set(secretIdsUserCanDelete.map(objectId => objectId._id.toString()));
|
||||
const deleteOperationsToPerform: AnyBulkWriteOperation<ISecret>[] = []
|
||||
|
||||
secretIdsToDelete.forEach(secretIdToDelete => {
|
||||
if (secretsUserCanDeleteSet.has(secretIdToDelete)) {
|
||||
const deleteOperation = { deleteOne: { filter: { _id: new Types.ObjectId(secretIdToDelete) } } }
|
||||
deleteOperationsToPerform.push(deleteOperation)
|
||||
} else {
|
||||
throw RouteValidationError({ message: "You cannot delete secrets that you do not have access to" })
|
||||
}
|
||||
})
|
||||
|
||||
const [bulkDeleteError, bulkDelete] = await to(Secret.bulkWrite(deleteOperationsToPerform).then())
|
||||
if (bulkDeleteError) {
|
||||
if (bulkDeleteError instanceof ValidationError) {
|
||||
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: bulkDeleteError.stack })
|
||||
}
|
||||
throw InternalServerError()
|
||||
}
|
||||
|
||||
res.status(200).send()
|
||||
}
|
||||
|
||||
export const batchModifySecrets = async (req: Request, res: Response) => {
|
||||
const { workspaceId, environmentName } = req.params
|
||||
const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets;
|
||||
const [secretIdsUserCanModifyError, secretIdsUserCanModify] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
|
||||
if (secretIdsUserCanModifyError) {
|
||||
throw InternalServerError({ message: "Unable to fetch secrets you own" })
|
||||
}
|
||||
|
||||
const secretsUserCanModifySet: Set<string> = new Set(secretIdsUserCanModify.map(objectId => objectId._id.toString()));
|
||||
const updateOperationsToPerform: any = []
|
||||
|
||||
|
||||
secretsModificationsRequested.forEach(userModifiedSecret => {
|
||||
if (secretsUserCanModifySet.has(userModifiedSecret._id.toString())) {
|
||||
const sanitizedSecret: SanitizedSecretModify = {
|
||||
secretKeyCiphertext: userModifiedSecret.secretKeyCiphertext,
|
||||
secretKeyIV: userModifiedSecret.secretKeyIV,
|
||||
secretKeyTag: userModifiedSecret.secretKeyTag,
|
||||
secretKeyHash: userModifiedSecret.secretKeyHash,
|
||||
secretValueCiphertext: userModifiedSecret.secretValueCiphertext,
|
||||
secretValueIV: userModifiedSecret.secretValueIV,
|
||||
secretValueTag: userModifiedSecret.secretValueTag,
|
||||
secretValueHash: userModifiedSecret.secretValueHash,
|
||||
secretCommentCiphertext: userModifiedSecret.secretCommentCiphertext,
|
||||
secretCommentIV: userModifiedSecret.secretCommentIV,
|
||||
secretCommentTag: userModifiedSecret.secretCommentTag,
|
||||
secretCommentHash: userModifiedSecret.secretCommentHash,
|
||||
}
|
||||
|
||||
const updateOperation = { updateOne: { filter: { _id: userModifiedSecret._id, workspace: workspaceId }, update: { $inc: { version: 1 }, $set: sanitizedSecret } } }
|
||||
updateOperationsToPerform.push(updateOperation)
|
||||
} else {
|
||||
throw UnauthorizedRequestError({ message: "You do not have permission to modify one or more of the requested secrets" })
|
||||
}
|
||||
})
|
||||
|
||||
const [bulkModificationInfoError, bulkModificationInfo] = await to(Secret.bulkWrite(updateOperationsToPerform).then())
|
||||
if (bulkModificationInfoError) {
|
||||
if (bulkModificationInfoError instanceof ValidationError) {
|
||||
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: bulkModificationInfoError.stack })
|
||||
}
|
||||
|
||||
throw InternalServerError()
|
||||
}
|
||||
|
||||
return res.status(200).send()
|
||||
}
|
||||
|
||||
export const fetchAllSecrets = async (req: Request, res: Response) => {
|
||||
const { environment } = req.query;
|
||||
const { workspaceId } = req.params;
|
||||
|
||||
let userId: string | undefined = undefined // Used for choosing the personal secrets to fetch in
|
||||
if (req.user) {
|
||||
userId = req.user._id.toString();
|
||||
}
|
||||
|
||||
if (req.serviceTokenData) {
|
||||
userId = req.serviceTokenData.user._id
|
||||
}
|
||||
|
||||
const [retriveAllSecretsError, allSecrets] = await to(Secret.find(
|
||||
{
|
||||
workspace: workspaceId,
|
||||
environment,
|
||||
$or: [{ user: userId }, { user: { $exists: false } }],
|
||||
type: { $in: [SECRET_SHARED, SECRET_PERSONAL] }
|
||||
}
|
||||
).then())
|
||||
|
||||
if (retriveAllSecretsError instanceof ValidationError) {
|
||||
throw RouteValidationError({ message: "Unable to get secrets, please try again", stack: retriveAllSecretsError.stack })
|
||||
}
|
||||
|
||||
return res.json(allSecrets)
|
||||
}
|
||||
@@ -15,9 +15,7 @@ import {
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getServiceTokenData = async (req: Request, res: Response) => res.status(200).send({
|
||||
serviceTokenData: req.serviceTokenData
|
||||
});
|
||||
export const getServiceTokenData = async (req: Request, res: Response) => res.status(200).json(req.serviceTokenData);
|
||||
|
||||
/**
|
||||
* Create new service token data for workspace with id [workspaceId] and
|
||||
@@ -29,9 +27,9 @@ export const getServiceTokenData = async (req: Request, res: Response) => res.st
|
||||
export const createServiceTokenData = async (req: Request, res: Response) => {
|
||||
let serviceToken, serviceTokenData;
|
||||
try {
|
||||
const {
|
||||
const {
|
||||
name,
|
||||
workspaceId,
|
||||
workspaceId,
|
||||
environment,
|
||||
encryptedKey,
|
||||
iv,
|
||||
@@ -41,10 +39,10 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
||||
|
||||
const secret = crypto.randomBytes(16).toString('hex');
|
||||
const secretHash = await bcrypt.hash(secret, SALT_ROUNDS);
|
||||
|
||||
const expiresAt = new Date();
|
||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||
|
||||
|
||||
const expiresAt = new Date();
|
||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||
|
||||
serviceTokenData = await new ServiceTokenData({
|
||||
name,
|
||||
workspace: workspaceId,
|
||||
@@ -56,12 +54,12 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
||||
iv,
|
||||
tag
|
||||
}).save();
|
||||
|
||||
|
||||
// return service token data without sensitive data
|
||||
serviceTokenData = await ServiceTokenData.findById(serviceTokenData._id);
|
||||
|
||||
|
||||
if (!serviceTokenData) throw new Error('Failed to find service token data');
|
||||
|
||||
|
||||
serviceToken = `st.${serviceTokenData._id.toString()}.${secret}`;
|
||||
|
||||
} catch (err) {
|
||||
@@ -90,7 +88,7 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
||||
const { serviceTokenDataId } = req.params;
|
||||
|
||||
serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId);
|
||||
|
||||
|
||||
} catch (err) {
|
||||
Sentry.setUser({ email: req.user.email });
|
||||
Sentry.captureException(err);
|
||||
@@ -98,7 +96,7 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
||||
message: 'Failed to delete service token data'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).send({
|
||||
serviceTokenData
|
||||
});
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { Request, Response } from 'express';
|
||||
import * as Sentry from '@sentry/node';
|
||||
import {
|
||||
Key,
|
||||
Workspace,
|
||||
Membership,
|
||||
MembershipOrg,
|
||||
Integration,
|
||||
IntegrationAuth,
|
||||
Key,
|
||||
IUser,
|
||||
ServiceToken,
|
||||
ServiceTokenData
|
||||
} from '../../models';
|
||||
import {
|
||||
@@ -68,7 +75,7 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||
workspaceId,
|
||||
keys
|
||||
});
|
||||
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: 'secrets pushed',
|
||||
@@ -115,7 +122,7 @@ export const pullSecrets = async (req: Request, res: Response) => {
|
||||
const environment: string = req.query.environment as string;
|
||||
const channel: string = req.query.channel as string;
|
||||
const { workspaceId } = req.params;
|
||||
|
||||
|
||||
let userId;
|
||||
if (req.user) {
|
||||
userId = req.user._id.toString();
|
||||
@@ -130,7 +137,7 @@ export const pullSecrets = async (req: Request, res: Response) => {
|
||||
channel: channel ? channel : 'cli',
|
||||
ipAddress: req.ip
|
||||
});
|
||||
|
||||
|
||||
if (channel !== 'cli') {
|
||||
secrets = reformatPullSecrets({ secrets });
|
||||
}
|
||||
@@ -170,7 +177,7 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
|
||||
workspace: workspaceId,
|
||||
receiver: req.user._id
|
||||
}).populate('sender', '+publicKey');
|
||||
|
||||
|
||||
if (!key) throw new Error('Failed to find workspace key');
|
||||
} catch (err) {
|
||||
Sentry.setUser({ email: req.user.email });
|
||||
@@ -180,9 +187,7 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
key
|
||||
});
|
||||
return res.status(200).json(key);
|
||||
}
|
||||
export const getWorkspaceServiceTokenData = async (
|
||||
req: Request,
|
||||
@@ -205,7 +210,7 @@ export const getWorkspaceServiceTokenData = async (
|
||||
message: 'Failed to get workspace service token data'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).send({
|
||||
serviceTokenData
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user