Skip to content

Commit

Permalink
Enable all auth clients for secrets v3, remove serviceTokenData .popu…
Browse files Browse the repository at this point in the history
…late in middleware, make secret versions and rollbacks compatible with blind indexing
  • Loading branch information
dangtony98 committed Apr 19, 2023
1 parent acb90ee commit ad5852f
Show file tree
Hide file tree
Showing 21 changed files with 313 additions and 133 deletions.
11 changes: 8 additions & 3 deletions backend/src/controllers/v2/secretController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { BadRequestError, InternalServerError, UnauthorizedRequestError, Validat
import { AnyBulkWriteOperation } from 'mongodb';
import { SECRET_PERSONAL, SECRET_SHARED } from "../../variables";
import { TelemetryService } from '../../services';
import { User } from "../../models";
import { AccountNotFoundError } from '../../utils/errors';

/**
* Create secret for workspace with id [workspaceId] and environment [environment]
Expand Down Expand Up @@ -340,15 +342,18 @@ export const getSecrets = async (req: Request, res: Response) => {
const { workspaceId } = req.params;

let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user
let userEmail: Types.ObjectId | undefined = undefined // used for posthog
let userEmail: string | 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;
userId = req.serviceTokenData.user;

const user = await User.findById(req.serviceTokenData.user, 'email');
if (!user) throw AccountNotFoundError();
userEmail = user.email;
}

const [err, secrets] = await to(Secret.find(
Expand Down
72 changes: 46 additions & 26 deletions backend/src/controllers/v2/secretsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,14 @@ export const createSecrets = async (req: Request, res: Response) => {
listOfSecretsToCreate = [req.body.secrets];
}

// get secret blind index salt
const salt = await SecretService.getSecretBlindIndexSalt({
workspaceId: new Types.ObjectId(workspaceId)
});

type secretsToCreateType = {
type: string;
secretName?: string;
secretKeyCiphertext: string;
secretKeyIV: string;
secretKeyTag: string;
Expand All @@ -388,25 +394,10 @@ export const createSecrets = async (req: Request, res: Response) => {
tags: string[]
}

const secretsToInsert: ISecret[] = listOfSecretsToCreate.map(({
type,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag,
tags
}: secretsToCreateType) => {
return ({
version: 1,
workspace: new Types.ObjectId(workspaceId),
const secretsToInsert: ISecret[] = await Promise.all(
listOfSecretsToCreate.map(async ({
type,
user: (req.user && type === SECRET_PERSONAL) ? req.user : undefined,
environment,
secretName,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
Expand All @@ -417,11 +408,38 @@ export const createSecrets = async (req: Request, res: Response) => {
secretCommentIV,
secretCommentTag,
tags
});
});
}: secretsToCreateType) => {
let secretBlindIndex;
if (secretName) {
secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
secretName,
salt
});
}

return ({
version: 1,
workspace: new Types.ObjectId(workspaceId),
type,
...(secretBlindIndex ? { secretBlindIndex } : {}),
user: (req.user && type === SECRET_PERSONAL) ? req.user : undefined,
environment,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag,
tags
});
})
);

const newlyCreatedSecrets: ISecret[] = (await Secret.insertMany(secretsToInsert)).map((insertedSecret) => insertedSecret.toObject());

setTimeout(async () => {
// trigger event - push secrets
await EventService.handleEvent({
Expand All @@ -440,6 +458,7 @@ export const createSecrets = async (req: Request, res: Response) => {
type,
user,
environment,
secretBlindIndex,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
Expand All @@ -453,6 +472,7 @@ export const createSecrets = async (req: Request, res: Response) => {
type,
user,
environment,
secretBlindIndex,
isDeleted: false,
secretKeyCiphertext,
secretKeyIV,
Expand Down Expand Up @@ -492,7 +512,7 @@ export const createSecrets = async (req: Request, res: Response) => {
if (postHogClient) {
postHogClient.capture({
event: 'secrets added',
distinctId: TelemetryService.getDistinctId({
distinctId: await TelemetryService.getDistinctId({
authData: req.authData
}),
properties: {
Expand Down Expand Up @@ -607,7 +627,7 @@ export const getSecrets = async (req: Request, res: Response) => {

// case: client authorization is via service token
if (req.serviceTokenData) {
const userId = req.serviceTokenData.user._id
const userId = req.serviceTokenData.user;

const secretQuery: any = {
workspace: workspaceId,
Expand Down Expand Up @@ -667,7 +687,7 @@ export const getSecrets = async (req: Request, res: Response) => {
if (postHogClient) {
postHogClient.capture({
event: 'secrets pulled',
distinctId: TelemetryService.getDistinctId({
distinctId: await TelemetryService.getDistinctId({
authData: req.authData
}),
properties: {
Expand Down Expand Up @@ -889,7 +909,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
if (postHogClient) {
postHogClient.capture({
event: 'secrets modified',
distinctId: TelemetryService.getDistinctId({
distinctId: await TelemetryService.getDistinctId({
authData: req.authData
}),
properties: {
Expand Down Expand Up @@ -1023,7 +1043,7 @@ export const deleteSecrets = async (req: Request, res: Response) => {
if (postHogClient) {
postHogClient.capture({
event: 'secrets deleted',
distinctId: TelemetryService.getDistinctId({
distinctId: await TelemetryService.getDistinctId({
authData: req.authData
}),
properties: {
Expand Down
14 changes: 12 additions & 2 deletions backend/src/controllers/v2/serviceTokenDataController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissi
import {
PERMISSION_READ_SECRETS,
AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT
AUTH_MODE_SERVICE_ACCOUNT,
AUTH_MODE_SERVICE_TOKEN
} from '../../variables';
import { getSaltRounds } from '../../config';
import { BadRequestError } from '../../utils/errors';

/**
* Return service token data associated with service token on request
Expand Down Expand Up @@ -48,7 +50,15 @@ export const getServiceTokenData = async (req: Request, res: Response) => {
}
*/

return res.status(200).json(req.serviceTokenData);
if (!(req.authData.authPayload instanceof ServiceTokenData)) throw BadRequestError({
message: 'Failed accepted client validation for service token data'
});

const serviceTokenData = await ServiceTokenData
.findById(req.authData.authPayload._id)
.populate('user');

return res.status(200).json(serviceTokenData);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion backend/src/controllers/v2/workspaceController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export const pullSecrets = async (req: Request, res: Response) => {
if (req.user) {
userId = req.user._id.toString();
} else if (req.serviceTokenData) {
userId = req.serviceTokenData.user._id
userId = req.serviceTokenData.user.toString();
}
// validate environment
const workspaceEnvs = req.membership.workspace.environments;
Expand Down
12 changes: 10 additions & 2 deletions backend/src/controllers/v3/secretsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ export const createSecret = async (req: Request, res: Response) => {
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
} = req.body;

const secret = await SecretService.createSecret({
Expand All @@ -84,7 +87,12 @@ export const createSecret = async (req: Request, res: Response) => {
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag
secretValueTag,
...((secretCommentCiphertext && secretCommentIV && secretCommentTag) ? {
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
} : {})
});

await EventService.handleEvent({
Expand Down
5 changes: 4 additions & 1 deletion backend/src/ee/controllers/v1/secretController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => {
const oldSecretVersion = await SecretVersion.findOne({
secret: secretId,
version
});
}).select('+secretBlindIndex')

if (!oldSecretVersion) throw new Error('Failed to find secret version');

Expand All @@ -155,6 +155,7 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => {
type,
user,
environment,
secretBlindIndex,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
Expand All @@ -174,6 +175,7 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => {
type,
user,
environment,
...(secretBlindIndex ? { secretBlindIndex } : {}),
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
Expand All @@ -197,6 +199,7 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => {
user,
environment,
isDeleted: false,
...(secretBlindIndex ? { secretBlindIndex } : {}),
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
Expand Down
8 changes: 6 additions & 2 deletions backend/src/ee/controllers/v1/workspaceController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons
}
}
*/

let secrets;
try {
const { workspaceId } = req.params;
Expand All @@ -182,7 +183,10 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons
const secretSnapshot = await SecretSnapshot.findOne({
workspace: workspaceId,
version
}).populate<{ secretVersions: ISecretVersion[]}>('secretVersions');
}).populate<{ secretVersions: ISecretVersion[]}>({
path: 'secretVersions',
select: '+secretBlindIndex'
});

if (!secretSnapshot) throw new Error('Failed to find secret snapshot');

Expand Down Expand Up @@ -259,7 +263,7 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons
);

// add secret versions
await SecretVersion.insertMany(
const secretV = await SecretVersion.insertMany(
secrets.map(({
_id,
version,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/helpers/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ const getAuthSTDPayload = async ({
}, {
new: true
})
.select('+encryptedKey +iv +tag').populate('user serviceAccount');
.select('+encryptedKey +iv +tag');

if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });

Expand Down
2 changes: 2 additions & 0 deletions backend/src/helpers/database.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import mongoose from 'mongoose';
import { EESecretService } from '../ee/services';
import { SecretService } from '../services';
import { getLogger } from '../utils/logger';

/**
Expand All @@ -22,6 +23,7 @@ const initDatabaseHelper = async ({
getLogger("database").info("Database connection established");

await EESecretService.initSecretVersioning();
await SecretService.initSecretBlindIndexDataHelper();
} catch (err) {
getLogger("database").error(`Unable to establish Database connection due to the error.\n${err}`);
}
Expand Down
Loading

0 comments on commit ad5852f

Please sign in to comment.