mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-17 14:40:39 +00:00
Add endpoints for rolling back a workspace to a secret snapshot and rolling back a secret to a version
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { Types } from 'mongoose';
|
||||
import { Secret } from '../../models';
|
||||
import { SecretVersion, Action } from '../models';
|
||||
import {
|
||||
getLatestSecretVersionIds,
|
||||
getLatestNSecretSecretVersionIds
|
||||
} from '../helpers/secretVersion';
|
||||
import { ACTION_UPDATE_SECRETS } from '../../variables';
|
||||
|
||||
/**
|
||||
@@ -30,65 +33,23 @@ const createActionSecretHelper = async ({
|
||||
if (name === ACTION_UPDATE_SECRETS) {
|
||||
// case: action is updating secrets
|
||||
// -> add old and new secret versions
|
||||
|
||||
// TODO: make query more efficient
|
||||
latestSecretVersions = (await SecretVersion.aggregate([
|
||||
{
|
||||
$match: {
|
||||
secret: {
|
||||
$in: secretIds,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$sort: { version: -1 },
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: "$secret",
|
||||
versions: { $push: "$$ROOT" },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
secret: "$_id",
|
||||
versions: { $slice: ["$versions", 2] },
|
||||
},
|
||||
}
|
||||
]))
|
||||
.map((s) => ({
|
||||
oldSecretVersion: s.versions[0]._id,
|
||||
newSecretVersion: s.versions[1]._id
|
||||
}));
|
||||
|
||||
|
||||
latestSecretVersions = (await getLatestNSecretSecretVersionIds({
|
||||
secretIds,
|
||||
n: 2
|
||||
}))
|
||||
.map((s) => ({
|
||||
oldSecretVersion: s.versions[0]._id,
|
||||
newSecretVersion: s.versions[1]._id
|
||||
}));
|
||||
} else {
|
||||
// case: action is adding, deleting, or reading secrets
|
||||
// -> add new secret versions
|
||||
latestSecretVersions = (await SecretVersion.aggregate([
|
||||
{
|
||||
$match: {
|
||||
secret: {
|
||||
$in: secretIds
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$secret',
|
||||
version: { $max: '$version' },
|
||||
versionId: { $max: '$_id' } // secret version id
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: { version: -1 }
|
||||
}
|
||||
])
|
||||
.exec())
|
||||
.map((s) => ({
|
||||
newSecretVersion: s.versionId
|
||||
}));
|
||||
latestSecretVersions = (await getLatestSecretVersionIds({
|
||||
secretIds
|
||||
}))
|
||||
.map((s) => ({
|
||||
newSecretVersion: s.versionId
|
||||
}));
|
||||
}
|
||||
|
||||
action = await new Action({
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { Types } from 'mongoose';
|
||||
import { SecretVersion } from '../models';
|
||||
|
||||
/**
|
||||
* Return latest secret versions for secrets with ids [secretIds]
|
||||
* @param {Object} obj
|
||||
* @param {Object} obj.secretIds = ids of secrets to get latest versions for
|
||||
* @returns
|
||||
*/
|
||||
const getLatestSecretVersionIds = async ({
|
||||
secretIds
|
||||
}: {
|
||||
secretIds: Types.ObjectId[];
|
||||
}) => {
|
||||
|
||||
interface LatestSecretVersionId {
|
||||
_id: Types.ObjectId;
|
||||
version: number;
|
||||
versionId: Types.ObjectId;
|
||||
}
|
||||
|
||||
let latestSecretVersionIds: LatestSecretVersionId[];
|
||||
try {
|
||||
latestSecretVersionIds = (await SecretVersion.aggregate([
|
||||
{
|
||||
$match: {
|
||||
secret: {
|
||||
$in: secretIds
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$secret',
|
||||
version: { $max: '$version' },
|
||||
versionId: { $max: '$_id' } // id of latest secret version
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: { version: -1 }
|
||||
}
|
||||
])
|
||||
.exec());
|
||||
|
||||
} catch (err) {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
throw new Error('Failed to get latest secret versions');
|
||||
}
|
||||
|
||||
return latestSecretVersionIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return latest [n] secret versions for secrets with ids [secretIds]
|
||||
* @param {Object} obj
|
||||
* @param {Object} obj.secretIds = ids of secrets to get latest versions for
|
||||
* @param {Number} obj.n - number of latest secret versions to return for each secret
|
||||
* @returns
|
||||
*/
|
||||
const getLatestNSecretSecretVersionIds = async ({
|
||||
secretIds,
|
||||
n
|
||||
}: {
|
||||
secretIds: Types.ObjectId[];
|
||||
n: number;
|
||||
}) => {
|
||||
|
||||
// TODO: optimize query
|
||||
let latestNSecretVersions;
|
||||
try {
|
||||
latestNSecretVersions = (await SecretVersion.aggregate([
|
||||
{
|
||||
$match: {
|
||||
secret: {
|
||||
$in: secretIds,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$sort: { version: -1 },
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: "$secret",
|
||||
versions: { $push: "$$ROOT" },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
secret: "$_id",
|
||||
versions: { $slice: ["$versions", n] },
|
||||
},
|
||||
}
|
||||
]));
|
||||
} catch (err) {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
throw new Error('Failed to get latest n secret versions');
|
||||
}
|
||||
|
||||
return latestNSecretVersions;
|
||||
}
|
||||
|
||||
export {
|
||||
getLatestSecretVersionIds,
|
||||
getLatestNSecretSecretVersionIds
|
||||
}
|
||||
Reference in New Issue
Block a user