mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 14:28:20 +00:00
Update modify secrets api v2 so that fields are optional
This commit is contained in:
@@ -242,7 +242,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
secretCommentTag: string;
|
secretCommentTag: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ops = req.body.secrets.map((secret: PatchSecret) => {
|
const updateOperationsToPerform = req.body.secrets.map((secret: PatchSecret) => {
|
||||||
const {
|
const {
|
||||||
secretKeyCiphertext,
|
secretKeyCiphertext,
|
||||||
secretKeyIV,
|
secretKeyIV,
|
||||||
@@ -254,6 +254,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag
|
secretCommentTag
|
||||||
} = secret;
|
} = secret;
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
updateOne: {
|
updateOne: {
|
||||||
filter: { _id: new Types.ObjectId(secret.id) },
|
filter: { _id: new Types.ObjectId(secret.id) },
|
||||||
@@ -280,15 +281,17 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
await Secret.bulkWrite(ops);
|
|
||||||
|
|
||||||
const newSecretsObj: { [key: string]: PatchSecret } = {};
|
await Secret.bulkWrite(updateOperationsToPerform);
|
||||||
|
|
||||||
|
const secretModificationsBySecretId: { [key: string]: PatchSecret } = {};
|
||||||
req.body.secrets.forEach((secret: PatchSecret) => {
|
req.body.secrets.forEach((secret: PatchSecret) => {
|
||||||
newSecretsObj[secret.id] = secret;
|
secretModificationsBySecretId[secret.id] = secret;
|
||||||
});
|
});
|
||||||
|
|
||||||
await EESecretService.addSecretVersions({
|
const ListOfSecretsBeforeModifications = req.secrets
|
||||||
secretVersions: req.secrets.map((secret: ISecret) => {
|
const secretVersions = {
|
||||||
|
secretVersions: ListOfSecretsBeforeModifications.map((secret: ISecret) => {
|
||||||
const {
|
const {
|
||||||
secretKeyCiphertext,
|
secretKeyCiphertext,
|
||||||
secretKeyIV,
|
secretKeyIV,
|
||||||
@@ -298,37 +301,29 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
secretValueTag,
|
secretValueTag,
|
||||||
secretCommentCiphertext,
|
secretCommentCiphertext,
|
||||||
secretCommentIV,
|
secretCommentIV,
|
||||||
secretCommentTag
|
secretCommentTag,
|
||||||
} = newSecretsObj[secret._id.toString()]
|
} = secretModificationsBySecretId[secret._id.toString()]
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
secret: secret._id,
|
secret: secret._id,
|
||||||
version: secret.version + 1,
|
version: secret.version + 1,
|
||||||
workspace: secret.workspace,
|
workspace: secret.workspace,
|
||||||
type: secret.type,
|
type: secret.type,
|
||||||
environment: secret.environment,
|
environment: secret.environment,
|
||||||
isDeleted: false,
|
secretKeyCiphertext: secretKeyCiphertext ? secretKeyCiphertext : secret.secretKeyCiphertext,
|
||||||
secretKeyCiphertext,
|
secretKeyIV: secretKeyIV ? secretKeyIV : secret.secretKeyIV,
|
||||||
secretKeyIV,
|
secretKeyTag: secretKeyTag ? secretKeyTag : secret.secretKeyTag,
|
||||||
secretKeyTag,
|
secretValueCiphertext: secretValueCiphertext ? secretValueCiphertext : secret.secretValueCiphertext,
|
||||||
secretValueCiphertext,
|
secretValueIV: secretValueIV ? secretValueIV : secret.secretValueIV,
|
||||||
secretValueIV,
|
secretValueTag: secretValueTag ? secretValueTag : secret.secretValueTag,
|
||||||
secretValueTag,
|
secretCommentCiphertext: secretCommentCiphertext ? secretCommentCiphertext : secret.secretCommentCiphertext,
|
||||||
...((
|
secretCommentIV: secretCommentIV ? secretCommentIV : secret.secretCommentIV,
|
||||||
secretCommentCiphertext &&
|
secretCommentTag: secretCommentTag ? secretCommentTag : secret.secretCommentTag,
|
||||||
secretCommentIV &&
|
|
||||||
secretCommentTag
|
|
||||||
) ? {
|
|
||||||
secretCommentCiphertext,
|
|
||||||
secretCommentIV,
|
|
||||||
secretCommentTag
|
|
||||||
} : {
|
|
||||||
secretCommentCiphertext: '',
|
|
||||||
secretCommentIV: '',
|
|
||||||
secretCommentTag: ''
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
}
|
||||||
|
|
||||||
|
await EESecretService.addSecretVersions(secretVersions);
|
||||||
|
|
||||||
|
|
||||||
// group secrets into workspaces so updated secrets can
|
// group secrets into workspaces so updated secrets can
|
||||||
@@ -367,9 +362,9 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// (EE) take a secret snapshot
|
// (EE) take a secret snapshot
|
||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId: key
|
workspaceId: key
|
||||||
})
|
})
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
@@ -449,9 +444,9 @@ export const deleteSecrets = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// (EE) take a secret snapshot
|
// (EE) take a secret snapshot
|
||||||
await EESecretService.takeSecretSnapshot({
|
await EESecretService.takeSecretSnapshot({
|
||||||
workspaceId: key
|
workspaceId: key
|
||||||
})
|
})
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Types } from 'mongoose';
|
import { Types } from 'mongoose';
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
import {
|
import {
|
||||||
Secret,
|
Secret,
|
||||||
ISecret
|
ISecret
|
||||||
} from '../../models';
|
} from '../../models';
|
||||||
import {
|
import {
|
||||||
SecretSnapshot,
|
SecretSnapshot,
|
||||||
SecretVersion,
|
SecretVersion,
|
||||||
ISecretVersion
|
ISecretVersion
|
||||||
} from '../models';
|
} from '../models';
|
||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
* @param {String} obj.workspaceId
|
* @param {String} obj.workspaceId
|
||||||
* @returns {SecretSnapshot} secretSnapshot - new secret snapshot
|
* @returns {SecretSnapshot} secretSnapshot - new secret snapshot
|
||||||
*/
|
*/
|
||||||
const takeSecretSnapshotHelper = async ({
|
const takeSecretSnapshotHelper = async ({
|
||||||
workspaceId
|
workspaceId
|
||||||
}: {
|
}: {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
@@ -48,7 +48,7 @@ import {
|
|||||||
{
|
{
|
||||||
$sort: { version: -1 }
|
$sort: { version: -1 }
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
.exec())
|
.exec())
|
||||||
.map((s) => s.versionId);
|
.map((s) => s.versionId);
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ const addSecretVersionsHelper = async ({
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
throw new Error('Failed to add secret versions');
|
throw new Error(`Failed to add secret versions [err=${err}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newSecretVersions;
|
return newSecretVersions;
|
||||||
@@ -125,34 +125,34 @@ const initSecretVersioningHelper = async () => {
|
|||||||
{ $set: { version: 1 } }
|
{ $set: { version: 1 } }
|
||||||
);
|
);
|
||||||
|
|
||||||
const unversionedSecrets: ISecret[] = await Secret.aggregate([
|
const unversionedSecrets: ISecret[] = await Secret.aggregate([
|
||||||
{
|
{
|
||||||
$lookup: {
|
$lookup: {
|
||||||
from: 'secretversions',
|
from: 'secretversions',
|
||||||
localField: '_id',
|
localField: '_id',
|
||||||
foreignField: 'secret',
|
foreignField: 'secret',
|
||||||
as: 'versions',
|
as: 'versions',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$match: {
|
$match: {
|
||||||
versions: { $size: 0 },
|
versions: { $size: 0 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (unversionedSecrets.length > 0) {
|
if (unversionedSecrets.length > 0) {
|
||||||
await addSecretVersionsHelper({
|
await addSecretVersionsHelper({
|
||||||
secretVersions: unversionedSecrets.map((s, idx) => ({
|
secretVersions: unversionedSecrets.map((s, idx) => ({
|
||||||
...s,
|
...s,
|
||||||
secret: s._id,
|
secret: s._id,
|
||||||
version: s.version ? s.version : 1,
|
version: s.version ? s.version : 1,
|
||||||
isDeleted: false,
|
isDeleted: false,
|
||||||
workspace: s.workspace,
|
workspace: s.workspace,
|
||||||
environment: s.environment
|
environment: s.environment
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
@@ -162,7 +162,7 @@ const initSecretVersioningHelper = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
takeSecretSnapshotHelper,
|
takeSecretSnapshotHelper,
|
||||||
addSecretVersionsHelper,
|
addSecretVersionsHelper,
|
||||||
markDeletedSecretVersionsHelper,
|
markDeletedSecretVersionsHelper,
|
||||||
initSecretVersioningHelper
|
initSecretVersioningHelper
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ import {
|
|||||||
|
|
||||||
export interface ISecretVersion {
|
export interface ISecretVersion {
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
secret: Types.ObjectId;
|
secret: Types.ObjectId;
|
||||||
version: number;
|
version: number;
|
||||||
workspace: Types.ObjectId; // new
|
workspace: Types.ObjectId; // new
|
||||||
type: string; // new
|
type: string; // new
|
||||||
user: Types.ObjectId; // new
|
user: Types.ObjectId; // new
|
||||||
environment: string; // new
|
environment: string; // new
|
||||||
isDeleted: boolean;
|
isDeleted: boolean;
|
||||||
secretKeyCiphertext: string;
|
secretKeyCiphertext: string;
|
||||||
secretKeyIV: string;
|
secretKeyIV: string;
|
||||||
secretKeyTag: string;
|
secretKeyTag: string;
|
||||||
secretKeyHash: string;
|
secretKeyHash: string;
|
||||||
@@ -28,17 +28,17 @@ export interface ISecretVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const secretVersionSchema = new Schema<ISecretVersion>(
|
const secretVersionSchema = new Schema<ISecretVersion>(
|
||||||
{
|
{
|
||||||
secret: { // could be deleted
|
secret: { // could be deleted
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'Secret',
|
ref: 'Secret',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
version: {
|
version: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 1,
|
default: 1,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
workspace: {
|
workspace: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'Workspace',
|
ref: 'Workspace',
|
||||||
@@ -59,12 +59,12 @@ const secretVersionSchema = new Schema<ISecretVersion>(
|
|||||||
enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD],
|
enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD],
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
isDeleted: { // consider removing field
|
isDeleted: { // consider removing field
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
secretKeyCiphertext: {
|
secretKeyCiphertext: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
@@ -94,10 +94,10 @@ const secretVersionSchema = new Schema<ISecretVersion>(
|
|||||||
secretValueHash: {
|
secretValueHash: {
|
||||||
type: String
|
type: String
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
timestamps: true
|
timestamps: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const SecretVersion = model<ISecretVersion>('SecretVersion', secretVersionSchema);
|
const SecretVersion = model<ISecretVersion>('SecretVersion', secretVersionSchema);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ router.post(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt']
|
acceptedAuthModes: ['jwt']
|
||||||
@@ -95,36 +95,24 @@ router.patch(
|
|||||||
if (value.length === 0) throw new Error('secrets cannot be an empty array')
|
if (value.length === 0) throw new Error('secrets cannot be an empty array')
|
||||||
for (const secret of value) {
|
for (const secret of value) {
|
||||||
if (
|
if (
|
||||||
!secret.id ||
|
!secret.id
|
||||||
!secret.secretKeyCiphertext ||
|
|
||||||
!secret.secretKeyIV ||
|
|
||||||
!secret.secretKeyTag ||
|
|
||||||
!secret.secretValueCiphertext ||
|
|
||||||
!secret.secretValueIV ||
|
|
||||||
!secret.secretValueTag
|
|
||||||
) {
|
) {
|
||||||
throw new Error('secrets array must contain objects that have required secret properties');
|
throw new Error('Each secret must contain a ID property');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (typeof value === 'object') {
|
} else if (typeof value === 'object') {
|
||||||
// case: update 1 secret
|
// case: update 1 secret
|
||||||
if (
|
if (
|
||||||
!value.id ||
|
!value.id
|
||||||
!value.secretKeyCiphertext ||
|
|
||||||
!value.secretKeyIV ||
|
|
||||||
!value.secretKeyTag ||
|
|
||||||
!value.secretValueCiphertext ||
|
|
||||||
!value.secretValueIV ||
|
|
||||||
!value.secretValueTag
|
|
||||||
) {
|
) {
|
||||||
throw new Error('secrets object is missing required secret properties');
|
throw new Error('secret must contain a ID property');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('secrets must be an object or an array of objects')
|
throw new Error('secrets must be an object or an array of objects')
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt']
|
acceptedAuthModes: ['jwt']
|
||||||
|
|||||||
Reference in New Issue
Block a user