mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 10:35:17 +00:00
Finish v1 audit logs, secret versioning, version all unversioned secrets
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { ISecret, Secret } from '../models';
|
||||
import { EESecretService } from '../ee/services';
|
||||
import { getLogger } from '../utils/logger';
|
||||
|
||||
/**
|
||||
* Initialize database connection
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.mongoURL - mongo connection string
|
||||
* @returns
|
||||
*/
|
||||
const initDatabaseHelper = async ({
|
||||
mongoURL
|
||||
}: {
|
||||
mongoURL: string;
|
||||
}) => {
|
||||
try {
|
||||
await mongoose.connect(mongoURL);
|
||||
getLogger("database").info("Database connection established");
|
||||
|
||||
await prepareDatabase();
|
||||
} catch (err) {
|
||||
getLogger("database").error(`Unable to establish Database connection due to the error.\n${err}`);
|
||||
}
|
||||
|
||||
return mongoose.connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare database by:
|
||||
* - Setting unversioned secrets to version 1
|
||||
* - Initializing secret versions for unversioned secrets
|
||||
*/
|
||||
const prepareDatabase = async () => {
|
||||
try {
|
||||
// set previously unversioned secrets in Secret to version 1
|
||||
await Secret.updateMany(
|
||||
{ version: { $exists: false } },
|
||||
{ $set: { version: 1 } }
|
||||
);
|
||||
|
||||
// initialize secret versions for unversioned secrets
|
||||
const unversionedSecrets: ISecret[] = await Secret.aggregate([
|
||||
{
|
||||
$lookup: {
|
||||
from: 'secretversions',
|
||||
localField: '_id',
|
||||
foreignField: 'secret',
|
||||
as: 'versions',
|
||||
},
|
||||
},
|
||||
{
|
||||
$match: {
|
||||
versions: { $size: 0 },
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
if (unversionedSecrets.length > 0) {
|
||||
await EESecretService.addSecretVersions({
|
||||
secretVersions: unversionedSecrets.map((s, idx) => ({
|
||||
...s,
|
||||
secret: s._id,
|
||||
version: s.version ? s.version : 1,
|
||||
isDeleted: false,
|
||||
workspace: s.workspace,
|
||||
environment: s.environment
|
||||
}))
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
getLogger('database').error('Failed to prepare database');
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
initDatabaseHelper
|
||||
}
|
||||
+42
-157
@@ -285,6 +285,9 @@ const v1PushSecrets = async ({
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: optimize this route.
|
||||
// TODO: ensure that it's possible to query for and filter logs
|
||||
|
||||
/**
|
||||
* Push secrets for user with id [userId] to workspace
|
||||
* with id [workspaceId] with environment [environment]. Follow steps:
|
||||
@@ -341,42 +344,19 @@ const v1PushSecrets = async ({
|
||||
await Secret.deleteMany({
|
||||
_id: { $in: toDelete }
|
||||
});
|
||||
|
||||
await EESecretService.markDeletedSecretVersions({
|
||||
secretIds: toDelete
|
||||
});
|
||||
|
||||
await SecretVersion.updateMany({
|
||||
secret: { $in: toDelete }
|
||||
}, {
|
||||
isDeleted: true
|
||||
}, {
|
||||
new: true
|
||||
const deleteAction = await EELogService.createActionSecret({
|
||||
name: ACTION_DELETE_SECRETS,
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds: toDelete
|
||||
});
|
||||
|
||||
// add audit log for deleted secrets
|
||||
const deletedLatestSecretVersions = (await SecretVersion.aggregate([
|
||||
{
|
||||
$match: { secret: { $in: toDelete } }
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$secret',
|
||||
version: { $max: '$version' }
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: { version: -1 }
|
||||
}
|
||||
])
|
||||
.exec())
|
||||
.map((s) => s._id);
|
||||
|
||||
const deleteAction = await new Action({
|
||||
name: ACTION_DELETE_SECRETS,
|
||||
user: new Types.ObjectId(userId),
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
payload: {
|
||||
secretVersions: deletedLatestSecretVersions
|
||||
}
|
||||
}).save();
|
||||
actions.push(deleteAction);
|
||||
deleteAction && actions.push(deleteAction);
|
||||
}
|
||||
|
||||
const toUpdate = oldSecrets
|
||||
@@ -459,10 +439,6 @@ const v1PushSecrets = async ({
|
||||
secretValueIV,
|
||||
secretValueTag,
|
||||
secretValueHash,
|
||||
secretCommentCiphertext,
|
||||
secretCommentIV,
|
||||
secretCommentTag,
|
||||
secretCommentHash,
|
||||
} = newSecretsObj[`${s.type}-${s.secretKeyHash}`];
|
||||
|
||||
return ({
|
||||
@@ -481,34 +457,14 @@ const v1PushSecrets = async ({
|
||||
})
|
||||
});
|
||||
|
||||
// add audit log for updated secrets
|
||||
const updatedLatestSecretVersions = (await SecretVersion.aggregate([
|
||||
{
|
||||
$match: { secret: { $in: toUpdate.map((u) => u._id) } }
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$secret',
|
||||
version: { $max: '$version' }
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: { version: -1 }
|
||||
}
|
||||
])
|
||||
.exec())
|
||||
.map((s) => s._id);
|
||||
|
||||
const updateAction = await new Action({
|
||||
const updateAction = await EELogService.createActionSecret({
|
||||
name: ACTION_UPDATE_SECRETS,
|
||||
user: new Types.ObjectId(userId),
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
payload: {
|
||||
secretVersions: updatedLatestSecretVersions
|
||||
}
|
||||
}).save();
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds: toUpdate.map((u) => u._id)
|
||||
});
|
||||
|
||||
actions.push(updateAction);
|
||||
updateAction && actions.push(updateAction);
|
||||
}
|
||||
|
||||
// handle adding new secrets
|
||||
@@ -517,45 +473,14 @@ const v1PushSecrets = async ({
|
||||
if (toAdd.length > 0) {
|
||||
// add secrets
|
||||
const newSecrets = await Secret.insertMany(
|
||||
toAdd.map(({
|
||||
secretKeyCiphertext,
|
||||
secretKeyIV,
|
||||
secretKeyTag,
|
||||
secretKeyHash,
|
||||
secretValueCiphertext,
|
||||
secretValueIV,
|
||||
secretValueTag,
|
||||
secretValueHash,
|
||||
secretCommentCiphertext,
|
||||
secretCommentIV,
|
||||
secretCommentTag,
|
||||
secretCommentHash,
|
||||
}, idx) => {
|
||||
const obj: any = {
|
||||
version: 1,
|
||||
workspace: workspaceId,
|
||||
type: toAdd[idx].type,
|
||||
environment,
|
||||
secretKeyCiphertext,
|
||||
secretKeyIV,
|
||||
secretKeyTag,
|
||||
secretKeyHash,
|
||||
secretValueCiphertext,
|
||||
secretValueIV,
|
||||
secretValueTag,
|
||||
secretValueHash,
|
||||
secretCommentCiphertext,
|
||||
secretCommentIV,
|
||||
secretCommentTag,
|
||||
secretCommentHash
|
||||
};
|
||||
|
||||
if (toAdd[idx].type === 'personal') {
|
||||
obj['user' as keyof typeof obj] = userId;
|
||||
}
|
||||
|
||||
return obj;
|
||||
})
|
||||
toAdd.map((s, idx) => ({
|
||||
...s,
|
||||
version: 1,
|
||||
workspace: workspaceId,
|
||||
type: toAdd[idx].type,
|
||||
environment,
|
||||
...( toAdd[idx].type === 'personal' ? { user: userId } : {})
|
||||
}))
|
||||
);
|
||||
|
||||
// (EE) add secret versions for new secrets
|
||||
@@ -584,35 +509,14 @@ const v1PushSecrets = async ({
|
||||
secretValueHash
|
||||
}))
|
||||
});
|
||||
|
||||
// add audit log for new secrets
|
||||
const newLatestSecretVersions = (await SecretVersion.aggregate([
|
||||
{
|
||||
$match: { secret: { $in: newSecrets.map((n) => n._id) } }
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$secret',
|
||||
version: { $max: '$version' }
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: { version: -1 }
|
||||
}
|
||||
])
|
||||
.exec())
|
||||
.map((s) => s._id);
|
||||
|
||||
const addAction = await new Action({
|
||||
const addAction = await EELogService.createActionSecret({
|
||||
name: ACTION_ADD_SECRETS,
|
||||
user: new Types.ObjectId(userId),
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
payload: {
|
||||
secretVersions: newLatestSecretVersions
|
||||
}
|
||||
}).save();
|
||||
|
||||
actions.push(addAction);
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds: newSecrets.map((n) => n._id)
|
||||
});
|
||||
addAction && actions.push(addAction);
|
||||
}
|
||||
|
||||
// (EE) take a secret snapshot
|
||||
@@ -620,6 +524,7 @@ const v1PushSecrets = async ({
|
||||
workspaceId
|
||||
})
|
||||
|
||||
// (EE) create (audit) log
|
||||
if (actions.length > 0) {
|
||||
await EELogService.createLog({
|
||||
userId,
|
||||
@@ -637,7 +542,7 @@ const v1PushSecrets = async ({
|
||||
};
|
||||
|
||||
/**
|
||||
* Pull secrets for user with id [userId] for workspace
|
||||
* Get secrets for user with id [userId] for workspace
|
||||
* with id [workspaceId] with environment [environment]
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.userId -id of user to pull secrets for
|
||||
@@ -704,7 +609,7 @@ const pullSecrets = async ({
|
||||
channel: string;
|
||||
ipAddress: string;
|
||||
}): Promise<ISecret[]> => {
|
||||
let secrets: any; // TODO: FIX any
|
||||
let secrets: any;
|
||||
|
||||
try {
|
||||
secrets = await getSecrets({
|
||||
@@ -712,35 +617,15 @@ const pullSecrets = async ({
|
||||
workspaceId,
|
||||
environment
|
||||
})
|
||||
|
||||
// add audit log for new secrets
|
||||
const readLatestSecretVersions = (await SecretVersion.aggregate([
|
||||
{
|
||||
$match: { secret: { $in: secrets.map((n: any) => n._id) } }
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$secret',
|
||||
version: { $max: '$version' }
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: { version: -1 }
|
||||
}
|
||||
])
|
||||
.exec())
|
||||
.map((s) => s._id);
|
||||
|
||||
const readAction = await new Action({
|
||||
const readAction = await EELogService.createActionSecret({
|
||||
name: ACTION_READ_SECRETS,
|
||||
user: new Types.ObjectId(userId),
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
payload: {
|
||||
secretVersions: readLatestSecretVersions
|
||||
}
|
||||
}).save();
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds: secrets.map((n: any) => n._id)
|
||||
});
|
||||
|
||||
await EELogService.createLog({
|
||||
readAction && await EELogService.createLog({
|
||||
userId,
|
||||
workspaceId,
|
||||
actions: [readAction],
|
||||
|
||||
Reference in New Issue
Block a user