Merge branch 'main' into feat/#31

This commit is contained in:
mv-turtle
2023-01-12 17:24:48 -08:00
committed by GitHub
46 changed files with 895 additions and 765 deletions
+97 -94
View File
@@ -7,7 +7,7 @@ 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";
// import { postHogClient } from '../../services';
import { postHogClient } from '../../services';
/**
* Create secret for workspace with id [workspaceId] and environment [environment]
@@ -42,19 +42,19 @@ export const createSecret = async (req: Request, res: Response) => {
throw RouteValidationError({ message: error.message, stack: error.stack })
}
// if (postHogClient) {
// postHogClient.capture({
// event: 'secrets added',
// distinctId: req.user.email,
// properties: {
// numberOfSecrets: 1,
// workspaceId,
// environment,
// channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
// userAgent: req.headers?.['user-agent']
// }
// });
// }
if (postHogClient) {
postHogClient.capture({
event: 'secrets added',
distinctId: req.user.email,
properties: {
numberOfSecrets: 1,
workspaceId,
environment,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
res.status(200).send({
secret
@@ -103,19 +103,19 @@ export const createSecrets = async (req: Request, res: Response) => {
throw InternalServerError({ message: "Unable to process your batch create request. Please try again", stack: bulkCreateError.stack })
}
// if (postHogClient) {
// postHogClient.capture({
// event: 'secrets added',
// distinctId: req.user.email,
// properties: {
// numberOfSecrets: (secretsToCreate ?? []).length,
// workspaceId,
// environment,
// channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
// userAgent: req.headers?.['user-agent']
// }
// });
// }
if (postHogClient) {
postHogClient.capture({
event: 'secrets added',
distinctId: req.user.email,
properties: {
numberOfSecrets: (secretsToCreate ?? []).length,
workspaceId,
environment,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
res.status(200).send({
secrets
@@ -158,19 +158,19 @@ export const deleteSecrets = async (req: Request, res: Response) => {
throw InternalServerError()
}
// if (postHogClient) {
// postHogClient.capture({
// event: 'secrets deleted',
// distinctId: req.user.email,
// properties: {
// numberOfSecrets: numSecretsDeleted,
// environment: environmentName,
// workspaceId,
// channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
// userAgent: req.headers?.['user-agent']
// }
// });
// }
if (postHogClient) {
postHogClient.capture({
event: 'secrets deleted',
distinctId: req.user.email,
properties: {
numberOfSecrets: numSecretsDeleted,
environment: environmentName,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
res.status(200).send()
}
@@ -183,19 +183,19 @@ export const deleteSecrets = async (req: Request, res: Response) => {
export const deleteSecret = async (req: Request, res: Response) => {
await Secret.findByIdAndDelete(req._secret._id)
// if (postHogClient) {
// postHogClient.capture({
// event: 'secrets deleted',
// distinctId: req.user.email,
// properties: {
// numberOfSecrets: 1,
// workspaceId: req._secret.workspace.toString(),
// environment: req._secret.environment,
// channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
// userAgent: req.headers?.['user-agent']
// }
// });
// }
if (postHogClient) {
postHogClient.capture({
event: 'secrets deleted',
distinctId: req.user.email,
properties: {
numberOfSecrets: 1,
workspaceId: req._secret.workspace.toString(),
environment: req._secret.environment,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
res.status(200).send({
secret: req._secret
@@ -252,19 +252,19 @@ export const updateSecrets = async (req: Request, res: Response) => {
throw InternalServerError()
}
// if (postHogClient) {
// postHogClient.capture({
// event: 'secrets modified',
// distinctId: req.user.email,
// properties: {
// numberOfSecrets: (secretsModificationsRequested ?? []).length,
// environment: environmentName,
// workspaceId,
// channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
// userAgent: req.headers?.['user-agent']
// }
// });
// }
if (postHogClient) {
postHogClient.capture({
event: 'secrets modified',
distinctId: req.user.email,
properties: {
numberOfSecrets: (secretsModificationsRequested ?? []).length,
environment: environmentName,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
return res.status(200).send()
}
@@ -304,19 +304,19 @@ export const updateSecret = async (req: Request, res: Response) => {
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: error.stack })
}
// if (postHogClient) {
// postHogClient.capture({
// event: 'secrets modified',
// distinctId: req.user.email,
// properties: {
// numberOfSecrets: 1,
// environment: environmentName,
// workspaceId,
// channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
// userAgent: req.headers?.['user-agent']
// }
// });
// }
if (postHogClient) {
postHogClient.capture({
event: 'secrets modified',
distinctId: req.user.email,
properties: {
numberOfSecrets: 1,
environment: environmentName,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
return res.status(200).send(singleModificationUpdate)
}
@@ -332,13 +332,16 @@ export const getSecrets = async (req: Request, res: Response) => {
const { environment } = req.query;
const { workspaceId } = req.params;
let userId: string | undefined = undefined // used for getting personal secrets for user
let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user
let userEmail: Types.ObjectId | undefined = undefined // used for posthog
if (req.user) {
userId = req.user._id.toString();
userId = req.user._id;
userEmail = req.user.email;
}
if (req.serviceTokenData) {
userId = req.serviceTokenData.user._id
userEmail = req.serviceTokenData.user.email;
}
const [err, secrets] = await to(Secret.find(
@@ -354,19 +357,19 @@ export const getSecrets = async (req: Request, res: Response) => {
throw RouteValidationError({ message: "Failed to get secrets, please try again", stack: err.stack })
}
// if (postHogClient) {
// postHogClient.capture({
// event: 'secrets pulled',
// distinctId: req.user.email,
// properties: {
// numberOfSecrets: (secrets ?? []).length,
// environment,
// workspaceId,
// channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
// userAgent: req.headers?.['user-agent']
// }
// });
// }
if (postHogClient) {
postHogClient.capture({
event: 'secrets pulled',
distinctId: userEmail,
properties: {
numberOfSecrets: (secrets ?? []).length,
environment,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
return res.json(secrets)
}
+58 -60
View File
@@ -2,8 +2,8 @@ import to from 'await-to-js';
import { Types } from 'mongoose';
import { Request, Response } from 'express';
import { ISecret, Secret } from '../../models';
import {
SECRET_PERSONAL,
import {
SECRET_PERSONAL,
SECRET_SHARED,
ACTION_ADD_SECRETS,
ACTION_READ_SECRETS,
@@ -23,9 +23,9 @@ import { BadRequestError } from '../../utils/errors';
* @param res
*/
export const createSecrets = async (req: Request, res: Response) => {
const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli';
const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli';
const { workspaceId, environment } = req.body;
let toAdd;
if (Array.isArray(req.body.secrets)) {
// case: create multiple secrets
@@ -34,7 +34,7 @@ export const createSecrets = async (req: Request, res: Response) => {
// case: create 1 secret
toAdd = [req.body.secrets];
}
const newSecrets = await Secret.insertMany(
toAdd.map(({
type,
@@ -66,7 +66,7 @@ export const createSecrets = async (req: Request, res: Response) => {
secretValueTag
}))
);
// (EE) add secret versions for new secrets
EESecretService.addSecretVersions({
secretVersions: newSecrets.map(({
@@ -160,22 +160,25 @@ export const createSecrets = async (req: Request, res: Response) => {
*/
export const getSecrets = async (req: Request, res: Response) => {
const { workspaceId, environment } = req.query;
let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user
let userEmail: Types.ObjectId | undefined = undefined // used for posthog
if (req.user) {
userId = req.user._id;
userEmail = req.user.email;
}
if (req.serviceTokenData) {
userId = req.serviceTokenData.user._id
userEmail = req.serviceTokenData.user.email;
}
const [err, secrets] = await to(Secret.find(
{
workspace: workspaceId,
environment,
$or: [
{ user: userId },
{ user: userId },
{ user: { $exists: false } }
],
type: { $in: [SECRET_SHARED, SECRET_PERSONAL] }
@@ -183,9 +186,9 @@ export const getSecrets = async (req: Request, res: Response) => {
).then())
if (err) throw ValidationError({ message: 'Failed to get secrets', stack: err.stack });
const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli';
const readAction = await EELogService.createActionSecret({
name: ACTION_READ_SECRETS,
userId: req.user._id.toString(),
@@ -204,7 +207,7 @@ export const getSecrets = async (req: Request, res: Response) => {
if (postHogClient) {
postHogClient.capture({
event: 'secrets added',
distinctId: req.user.email,
distinctId: userEmail,
properties: {
numberOfSecrets: secrets.length,
environment,
@@ -214,7 +217,7 @@ export const getSecrets = async (req: Request, res: Response) => {
}
});
}
return res.status(200).send({
secrets
});
@@ -226,8 +229,8 @@ export const getSecrets = async (req: Request, res: Response) => {
* @param res
*/
export const updateSecrets = async (req: Request, res: Response) => {
const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli';
const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli';
// TODO: move type
interface PatchSecret {
id: string;
@@ -242,7 +245,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
secretCommentTag: string;
}
const ops = req.body.secrets.map((secret: PatchSecret) => {
const updateOperationsToPerform = req.body.secrets.map((secret: PatchSecret) => {
const {
secretKeyCiphertext,
secretKeyIV,
@@ -254,6 +257,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
secretCommentIV,
secretCommentTag
} = secret;
return ({
updateOne: {
filter: { _id: new Types.ObjectId(secret.id) },
@@ -268,8 +272,8 @@ export const updateSecrets = async (req: Request, res: Response) => {
secretValueIV,
secretValueTag,
...((
secretCommentCiphertext &&
secretCommentIV &&
secretCommentCiphertext &&
secretCommentIV &&
secretCommentTag
) ? {
secretCommentCiphertext,
@@ -280,15 +284,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) => {
newSecretsObj[secret.id] = secret;
secretModificationsBySecretId[secret.id] = secret;
});
await EESecretService.addSecretVersions({
secretVersions: req.secrets.map((secret: ISecret) => {
const ListOfSecretsBeforeModifications = req.secrets
const secretVersions = {
secretVersions: ListOfSecretsBeforeModifications.map((secret: ISecret) => {
const {
secretKeyCiphertext,
secretKeyIV,
@@ -298,37 +304,29 @@ export const updateSecrets = async (req: Request, res: Response) => {
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
} = newSecretsObj[secret._id.toString()]
secretCommentTag,
} = secretModificationsBySecretId[secret._id.toString()]
return ({
secret: secret._id,
version: secret.version + 1,
workspace: secret.workspace,
type: secret.type,
environment: secret.environment,
isDeleted: false,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
...((
secretCommentCiphertext &&
secretCommentIV &&
secretCommentTag
) ? {
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
} : {
secretCommentCiphertext: '',
secretCommentIV: '',
secretCommentTag: ''
})
secretKeyCiphertext: secretKeyCiphertext ? secretKeyCiphertext : secret.secretKeyCiphertext,
secretKeyIV: secretKeyIV ? secretKeyIV : secret.secretKeyIV,
secretKeyTag: secretKeyTag ? secretKeyTag : secret.secretKeyTag,
secretValueCiphertext: secretValueCiphertext ? secretValueCiphertext : secret.secretValueCiphertext,
secretValueIV: secretValueIV ? secretValueIV : secret.secretValueIV,
secretValueTag: secretValueTag ? secretValueTag : secret.secretValueTag,
secretCommentCiphertext: secretCommentCiphertext ? secretCommentCiphertext : secret.secretCommentCiphertext,
secretCommentIV: secretCommentIV ? secretCommentIV : secret.secretCommentIV,
secretCommentTag: secretCommentTag ? secretCommentTag : secret.secretCommentTag,
});
})
});
}
await EESecretService.addSecretVersions(secretVersions);
// group secrets into workspaces so updated secrets can
@@ -355,7 +353,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
userId: req.user._id.toString(),
workspaceId: key,
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
});
});
// (EE) create (audit) log
updateAction && await EELogService.createLog({
@@ -367,9 +365,9 @@ export const updateSecrets = async (req: Request, res: Response) => {
});
// (EE) take a secret snapshot
await EESecretService.takeSecretSnapshot({
workspaceId: key
})
await EESecretService.takeSecretSnapshot({
workspaceId: key
})
if (postHogClient) {
postHogClient.capture({
@@ -385,7 +383,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
});
}
});
return res.status(200).send({
secrets: await Secret.find({
_id: {
@@ -401,15 +399,15 @@ export const updateSecrets = async (req: Request, res: Response) => {
* @param res
*/
export const deleteSecrets = async (req: Request, res: Response) => {
const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli';
const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli';
const toDelete = req.secrets.map((s: any) => s._id);
await Secret.deleteMany({
_id: {
$in: toDelete
}
});
await EESecretService.markDeletedSecretVersions({
secretIds: toDelete
});
@@ -437,7 +435,7 @@ export const deleteSecrets = async (req: Request, res: Response) => {
userId: req.user._id.toString(),
workspaceId: key,
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
});
});
// (EE) create (audit) log
deleteAction && await EELogService.createLog({
@@ -449,9 +447,9 @@ export const deleteSecrets = async (req: Request, res: Response) => {
});
// (EE) take a secret snapshot
await EESecretService.takeSecretSnapshot({
workspaceId: key
})
await EESecretService.takeSecretSnapshot({
workspaceId: key
})
if (postHogClient) {
postHogClient.capture({
@@ -467,7 +465,7 @@ export const deleteSecrets = async (req: Request, res: Response) => {
});
}
});
return res.status(200).send({
secrets: req.secrets
});
+46 -46
View File
@@ -1,11 +1,11 @@
import { Types } from 'mongoose';
import * as Sentry from '@sentry/node';
import {
Secret,
Secret,
ISecret
} from '../../models';
import {
SecretSnapshot,
SecretSnapshot,
SecretVersion,
ISecretVersion
} from '../models';
@@ -18,24 +18,24 @@ import {
* @param {String} obj.workspaceId
* @returns {SecretSnapshot} secretSnapshot - new secret snapshot
*/
const takeSecretSnapshotHelper = async ({
const takeSecretSnapshotHelper = async ({
workspaceId
}: {
workspaceId: string;
}) => {
let secretSnapshot;
try {
const secretIds = (await Secret.find({
workspace: workspaceId
}, '_id')).map((s) => s._id);
const latestSecretVersions = (await SecretVersion.aggregate([
{
$match: {
secret: {
$in: secretIds
}
$match: {
secret: {
$in: secretIds
}
}
},
{
@@ -48,14 +48,14 @@ import {
{
$sort: { version: -1 }
}
])
])
.exec())
.map((s) => s.versionId);
const latestSecretSnapshot = await SecretSnapshot.findOne({
workspace: workspaceId
}).sort({ version: -1 });
secretSnapshot = await new SecretSnapshot({
workspace: workspaceId,
version: latestSecretSnapshot ? latestSecretSnapshot.version + 1 : 1,
@@ -66,7 +66,7 @@ import {
Sentry.captureException(err);
throw new Error('Failed to take a secret snapshot');
}
return secretSnapshot;
}
@@ -87,9 +87,9 @@ const addSecretVersionsHelper = async ({
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to add secret versions');
throw new Error(`Failed to add secret versions [err=${err}]`);
}
return newSecretVersions;
}
@@ -120,39 +120,39 @@ const markDeletedSecretVersionsHelper = async ({
const initSecretVersioningHelper = async () => {
try {
await Secret.updateMany(
await Secret.updateMany(
{ version: { $exists: false } },
{ $set: { version: 1 } }
);
const unversionedSecrets: ISecret[] = await Secret.aggregate([
{
$lookup: {
from: 'secretversions',
localField: '_id',
foreignField: 'secret',
as: 'versions',
},
},
{
$match: {
versions: { $size: 0 },
},
},
]);
if (unversionedSecrets.length > 0) {
await addSecretVersionsHelper({
secretVersions: unversionedSecrets.map((s, idx) => ({
...s,
secret: s._id,
version: s.version ? s.version : 1,
isDeleted: false,
workspace: s.workspace,
environment: s.environment
}))
});
}
const unversionedSecrets: ISecret[] = await Secret.aggregate([
{
$lookup: {
from: 'secretversions',
localField: '_id',
foreignField: 'secret',
as: 'versions',
},
},
{
$match: {
versions: { $size: 0 },
},
},
]);
if (unversionedSecrets.length > 0) {
await addSecretVersionsHelper({
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) {
Sentry.setUser(null);
@@ -162,7 +162,7 @@ const initSecretVersioningHelper = async () => {
}
export {
takeSecretSnapshotHelper,
takeSecretSnapshotHelper,
addSecretVersionsHelper,
markDeletedSecretVersionsHelper,
initSecretVersioningHelper
+25 -25
View File
@@ -6,14 +6,14 @@ import {
export interface ISecretVersion {
_id: Types.ObjectId;
secret: Types.ObjectId;
version: number;
secret: Types.ObjectId;
version: number;
workspace: Types.ObjectId; // new
type: string; // new
user: Types.ObjectId; // new
environment: string; // new
isDeleted: boolean;
secretKeyCiphertext: string;
isDeleted: boolean;
secretKeyCiphertext: string;
secretKeyIV: string;
secretKeyTag: string;
secretKeyHash: string;
@@ -24,17 +24,17 @@ export interface ISecretVersion {
}
const secretVersionSchema = new Schema<ISecretVersion>(
{
secret: { // could be deleted
type: Schema.Types.ObjectId,
ref: 'Secret',
required: true
},
version: {
type: Number,
default: 1,
required: true
},
{
secret: { // could be deleted
type: Schema.Types.ObjectId,
ref: 'Secret',
required: true
},
version: {
type: Number,
default: 1,
required: true
},
workspace: {
type: Schema.Types.ObjectId,
ref: 'Workspace',
@@ -54,12 +54,12 @@ const secretVersionSchema = new Schema<ISecretVersion>(
type: String,
required: true
},
isDeleted: { // consider removing field
type: Boolean,
default: false,
required: true
},
secretKeyCiphertext: {
isDeleted: { // consider removing field
type: Boolean,
default: false,
required: true
},
secretKeyCiphertext: {
type: String,
required: true
},
@@ -89,10 +89,10 @@ const secretVersionSchema = new Schema<ISecretVersion>(
secretValueHash: {
type: String
}
},
{
timestamps: true
}
},
{
timestamps: true
}
);
const SecretVersion = model<ISecretVersion>('SecretVersion', secretVersionSchema);
+16 -28
View File
@@ -8,8 +8,8 @@ import {
} from '../../middleware';
import { query, check, body } from 'express-validator';
import { secretsController } from '../../controllers/v2';
import {
ADMIN,
import {
ADMIN,
MEMBER,
SECRET_PERSONAL,
SECRET_SHARED
@@ -27,7 +27,7 @@ router.post(
if (value.length === 0) throw new Error('secrets cannot be an empty array')
for (const secret of value) {
if (
!secret.type ||
!secret.type ||
!(secret.type === SECRET_PERSONAL || secret.type === SECRET_SHARED) ||
!secret.secretKeyCiphertext ||
!secret.secretKeyIV ||
@@ -42,7 +42,7 @@ router.post(
} else if (typeof value === 'object') {
// case: update 1 secret
if (
!value.type ||
!value.type ||
!(value.type === SECRET_PERSONAL || value.type === SECRET_SHARED) ||
!value.secretKeyCiphertext ||
!value.secretKeyIV ||
@@ -52,13 +52,13 @@ router.post(
!value.secretValueTag
) {
throw new Error('secrets object is missing required secret properties');
}
}
} else {
throw new Error('secrets must be an object or an array of objects')
}
return true;
}),
}),
validateRequest,
requireAuth({
acceptedAuthModes: ['jwt']
@@ -95,36 +95,24 @@ router.patch(
if (value.length === 0) throw new Error('secrets cannot be an empty array')
for (const secret of value) {
if (
!secret.id ||
!secret.secretKeyCiphertext ||
!secret.secretKeyIV ||
!secret.secretKeyTag ||
!secret.secretValueCiphertext ||
!secret.secretValueIV ||
!secret.secretValueTag
!secret.id
) {
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') {
// case: update 1 secret
if (
!value.id ||
!value.secretKeyCiphertext ||
!value.secretKeyIV ||
!value.secretKeyTag ||
!value.secretValueCiphertext ||
!value.secretValueIV ||
!value.secretValueTag
!value.id
) {
throw new Error('secrets object is missing required secret properties');
}
throw new Error('secret must contain a ID property');
}
} else {
throw new Error('secrets must be an object or an array of objects')
}
return true;
}),
}),
validateRequest,
requireAuth({
acceptedAuthModes: ['jwt']
@@ -142,13 +130,13 @@ router.delete(
.custom((value) => {
// case: delete 1 secret
if (typeof value === 'string') return true;
if (Array.isArray(value)) {
// case: delete multiple secrets
if (value.length === 0) throw new Error('secrets cannot be an empty array');
return value.every((id: string) => typeof id === 'string')
}
throw new Error('secretIds must be a string or an array of strings');
})
.not()
+33 -33
View File
@@ -11,10 +11,10 @@ const INTEGRATION_VERCEL = 'vercel';
const INTEGRATION_NETLIFY = 'netlify';
const INTEGRATION_GITHUB = 'github';
const INTEGRATION_SET = new Set([
INTEGRATION_HEROKU,
INTEGRATION_VERCEL,
INTEGRATION_NETLIFY,
INTEGRATION_GITHUB
INTEGRATION_HEROKU,
INTEGRATION_VERCEL,
INTEGRATION_NETLIFY,
INTEGRATION_GITHUB
]);
// integration types
@@ -23,10 +23,10 @@ const INTEGRATION_OAUTH2 = 'oauth2';
// integration oauth endpoints
const INTEGRATION_HEROKU_TOKEN_URL = 'https://id.heroku.com/oauth/token';
const INTEGRATION_VERCEL_TOKEN_URL =
'https://api.vercel.com/v2/oauth/access_token';
'https://api.vercel.com/v2/oauth/access_token';
const INTEGRATION_NETLIFY_TOKEN_URL = 'https://api.netlify.com/oauth/token';
const INTEGRATION_GITHUB_TOKEN_URL =
'https://github.com/login/oauth/access_token';
'https://github.com/login/oauth/access_token';
// integration apps endpoints
const INTEGRATION_HEROKU_API_URL = 'https://api.heroku.com';
@@ -37,7 +37,7 @@ const INTEGRATION_OPTIONS = [
{
name: 'Heroku',
slug: 'heroku',
image: 'Heroku',
image: 'Heroku',
isAvailable: true,
type: 'oauth2',
clientId: CLIENT_ID_HEROKU,
@@ -46,8 +46,8 @@ const INTEGRATION_OPTIONS = [
{
name: 'Vercel',
slug: 'vercel',
image: 'Vercel',
isAvailable: true,
image: 'Vercel',
isAvailable: false,
type: 'vercel',
clientId: '',
clientSlug: CLIENT_SLUG_VERCEL,
@@ -56,8 +56,8 @@ const INTEGRATION_OPTIONS = [
{
name: 'Netlify',
slug: 'netlify',
image: 'Netlify',
isAvailable: true,
image: 'Netlify',
isAvailable: false,
type: 'oauth2',
clientId: CLIENT_ID_NETLIFY,
docsLink: ''
@@ -65,17 +65,17 @@ const INTEGRATION_OPTIONS = [
{
name: 'GitHub',
slug: 'github',
image: 'GitHub',
isAvailable: true,
image: 'GitHub',
isAvailable: false,
type: 'oauth2',
clientId: CLIENT_ID_GITHUB,
docsLink: ''
},
{
name: 'Google Cloud Platform',
slug: 'gcp',
image: 'Google Cloud Platform',
image: 'Google Cloud Platform',
isAvailable: false,
type: '',
clientId: '',
@@ -84,7 +84,7 @@ const INTEGRATION_OPTIONS = [
{
name: 'Amazon Web Services',
slug: 'aws',
image: 'Amazon Web Services',
image: 'Amazon Web Services',
isAvailable: false,
type: '',
clientId: '',
@@ -93,7 +93,7 @@ const INTEGRATION_OPTIONS = [
{
name: 'Microsoft Azure',
slug: 'azure',
image: 'Microsoft Azure',
image: 'Microsoft Azure',
isAvailable: false,
type: '',
clientId: '',
@@ -102,7 +102,7 @@ const INTEGRATION_OPTIONS = [
{
name: 'Travis CI',
slug: 'travisci',
image: 'Travis CI',
image: 'Travis CI',
isAvailable: false,
type: '',
clientId: '',
@@ -111,7 +111,7 @@ const INTEGRATION_OPTIONS = [
{
name: 'Circle CI',
slug: 'circleci',
image: 'Circle CI',
image: 'Circle CI',
isAvailable: false,
type: '',
clientId: '',
@@ -120,18 +120,18 @@ const INTEGRATION_OPTIONS = [
]
export {
INTEGRATION_HEROKU,
INTEGRATION_VERCEL,
INTEGRATION_NETLIFY,
INTEGRATION_GITHUB,
INTEGRATION_SET,
INTEGRATION_OAUTH2,
INTEGRATION_HEROKU_TOKEN_URL,
INTEGRATION_VERCEL_TOKEN_URL,
INTEGRATION_NETLIFY_TOKEN_URL,
INTEGRATION_GITHUB_TOKEN_URL,
INTEGRATION_HEROKU_API_URL,
INTEGRATION_VERCEL_API_URL,
INTEGRATION_NETLIFY_API_URL,
INTEGRATION_OPTIONS
INTEGRATION_HEROKU,
INTEGRATION_VERCEL,
INTEGRATION_NETLIFY,
INTEGRATION_GITHUB,
INTEGRATION_SET,
INTEGRATION_OAUTH2,
INTEGRATION_HEROKU_TOKEN_URL,
INTEGRATION_VERCEL_TOKEN_URL,
INTEGRATION_NETLIFY_TOKEN_URL,
INTEGRATION_GITHUB_TOKEN_URL,
INTEGRATION_HEROKU_API_URL,
INTEGRATION_VERCEL_API_URL,
INTEGRATION_NETLIFY_API_URL,
INTEGRATION_OPTIONS
};