mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 13:22:08 +00:00
Remove/deprecate service accounts + old logs/actions
This commit is contained in:
@@ -6,14 +6,8 @@ const jsrp = require("jsrp");
|
||||
import { LoginSRPDetail, TokenVersion, User } from "../../models";
|
||||
import { clearTokens, createToken, issueAuthTokens } from "../../helpers/auth";
|
||||
import { checkUserDevice } from "../../helpers/user";
|
||||
import {
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
AuthTokenType
|
||||
} from "../../variables";
|
||||
import { AuthTokenType } from "../../variables";
|
||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||
import { EELogService } from "../../ee/services";
|
||||
import { getUserAgentType } from "../../utils/posthog";
|
||||
import {
|
||||
getAuthSecret,
|
||||
getHttpsEnabled,
|
||||
@@ -145,19 +139,6 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
secure: await getHttpsEnabled()
|
||||
});
|
||||
|
||||
const loginAction = await EELogService.createAction({
|
||||
name: ACTION_LOGIN,
|
||||
userId: user._id
|
||||
});
|
||||
|
||||
loginAction &&
|
||||
(await EELogService.createLog({
|
||||
userId: user._id,
|
||||
actions: [loginAction],
|
||||
channel: getUserAgentType(req.headers["user-agent"]),
|
||||
ipAddress: req.realIP
|
||||
}));
|
||||
|
||||
// return (access) token in response
|
||||
return res.status(200).send({
|
||||
token: tokens.token,
|
||||
@@ -194,19 +175,6 @@ export const logout = async (req: Request, res: Response) => {
|
||||
secure: (await getHttpsEnabled()) as boolean
|
||||
});
|
||||
|
||||
const logoutAction = await EELogService.createAction({
|
||||
name: ACTION_LOGOUT,
|
||||
userId: req.user._id
|
||||
});
|
||||
|
||||
logoutAction &&
|
||||
(await EELogService.createLog({
|
||||
userId: req.user._id,
|
||||
actions: [logoutAction],
|
||||
channel: getUserAgentType(req.headers["user-agent"]),
|
||||
ipAddress: req.realIP
|
||||
}));
|
||||
|
||||
return res.status(200).send({
|
||||
message: "Successfully logged out."
|
||||
});
|
||||
|
||||
@@ -8,10 +8,8 @@ import { createToken, issueAuthTokens } from "../../helpers/auth";
|
||||
import { checkUserDevice } from "../../helpers/user";
|
||||
import { sendMail } from "../../helpers/nodemailer";
|
||||
import { TokenService } from "../../services";
|
||||
import { EELogService } from "../../ee/services";
|
||||
import { BadRequestError, InternalServerError } from "../../utils/errors";
|
||||
import { ACTION_LOGIN, AuthTokenType, TOKEN_EMAIL_MFA } from "../../variables";
|
||||
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
|
||||
import { AuthTokenType, TOKEN_EMAIL_MFA } from "../../variables";
|
||||
import { getAuthSecret, getHttpsEnabled, getJwtMfaLifetime } from "../../config";
|
||||
import { validateRequest } from "../../helpers/validation";
|
||||
import * as reqValidator from "../../validation/auth";
|
||||
@@ -190,19 +188,6 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
response.protectedKeyTag = user.protectedKeyTag;
|
||||
}
|
||||
|
||||
const loginAction = await EELogService.createAction({
|
||||
name: ACTION_LOGIN,
|
||||
userId: user._id
|
||||
});
|
||||
|
||||
loginAction &&
|
||||
(await EELogService.createLog({
|
||||
userId: user._id,
|
||||
actions: [loginAction],
|
||||
channel: getUserAgentType(req.headers["user-agent"]),
|
||||
ipAddress: req.ip
|
||||
}));
|
||||
|
||||
return res.status(200).send(response);
|
||||
}
|
||||
|
||||
@@ -330,18 +315,5 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
||||
resObj.protectedKeyTag = user.protectedKeyTag;
|
||||
}
|
||||
|
||||
const loginAction = await EELogService.createAction({
|
||||
name: ACTION_LOGIN,
|
||||
userId: user._id
|
||||
});
|
||||
|
||||
loginAction &&
|
||||
(await EELogService.createLog({
|
||||
userId: user._id,
|
||||
actions: [loginAction],
|
||||
channel: getUserAgentType(req.headers["user-agent"]),
|
||||
ipAddress: req.realIP
|
||||
}));
|
||||
|
||||
return res.status(200).send(resObj);
|
||||
};
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
} from "../../ee/services/ProjectRoleService";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { SecretImport } from "../../models";
|
||||
import { ServiceAccountWorkspacePermission } from "../../models";
|
||||
import { Webhook } from "../../models";
|
||||
|
||||
/**
|
||||
@@ -400,11 +399,6 @@ export const renameWorkspaceEnvironment = async (req: Request, res: Response) =>
|
||||
{ arrayFilters: [{ "element.environment": oldEnvironmentSlug }] },
|
||||
);
|
||||
|
||||
await ServiceAccountWorkspacePermission.updateMany(
|
||||
{ workspace: workspaceId, environment: oldEnvironmentSlug },
|
||||
{ environment: environmentSlug }
|
||||
);
|
||||
|
||||
await Webhook.updateMany(
|
||||
{ workspace: workspaceId, environment: oldEnvironmentSlug },
|
||||
{ environment: environmentSlug }
|
||||
|
||||
@@ -6,7 +6,6 @@ import * as workspaceController from "./workspaceController";
|
||||
import * as serviceTokenDataController from "./serviceTokenDataController";
|
||||
import * as secretController from "./secretController";
|
||||
import * as secretsController from "./secretsController";
|
||||
import * as serviceAccountsController from "./serviceAccountsController";
|
||||
import * as environmentController from "./environmentController";
|
||||
import * as tagController from "./tagController";
|
||||
import * as membershipController from "./membershipController";
|
||||
@@ -20,7 +19,6 @@ export {
|
||||
serviceTokenDataController,
|
||||
secretController,
|
||||
secretsController,
|
||||
serviceAccountsController,
|
||||
environmentController,
|
||||
tagController,
|
||||
membershipController
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Types } from "mongoose";
|
||||
import {
|
||||
Membership,
|
||||
MembershipOrg,
|
||||
ServiceAccount,
|
||||
Workspace
|
||||
} from "../../models";
|
||||
import { Role } from "../../ee/models";
|
||||
@@ -330,23 +329,6 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return service accounts for organization with id [organizationId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getOrganizationServiceAccounts = async (req: Request, res: Response) => {
|
||||
const { organizationId } = req.params;
|
||||
|
||||
const serviceAccounts = await ServiceAccount.find({
|
||||
organization: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccounts
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create new organization named [organizationName]
|
||||
* and add user as owner
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { Types } from "mongoose";
|
||||
import { Request, Response } from "express";
|
||||
import { Folder, ISecret, Secret, ServiceTokenData, Tag } from "../../models";
|
||||
import { AuditLog, EventType, IAction, SecretVersion } from "../../ee/models";
|
||||
import { AuditLog, EventType, SecretVersion } from "../../ee/models";
|
||||
import {
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
K8_USER_AGENT_NAME,
|
||||
@@ -15,7 +11,7 @@ import {
|
||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||
import { EventService } from "../../services";
|
||||
import { eventPushSecrets } from "../../events";
|
||||
import { EEAuditLogService, EELogService, EESecretService } from "../../ee/services";
|
||||
import { EEAuditLogService, EESecretService } from "../../ee/services";
|
||||
import { SecretService, TelemetryService } from "../../services";
|
||||
import { getUserAgentType } from "../../utils/posthog";
|
||||
import { PERMISSION_WRITE_SECRETS } from "../../variables";
|
||||
@@ -82,7 +78,6 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
||||
const createSecrets: any[] = [];
|
||||
const updateSecrets: any[] = [];
|
||||
const deleteSecrets: { _id: Types.ObjectId; secretName: string }[] = [];
|
||||
const actions: IAction[] = [];
|
||||
|
||||
// get secret blind index salt
|
||||
const salt = await SecretService.getSecretBlindIndexSalt({
|
||||
@@ -224,16 +219,6 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
||||
|
||||
await AuditLog.insertMany(auditLogs);
|
||||
|
||||
const addAction = (await EELogService.createAction({
|
||||
name: ACTION_ADD_SECRETS,
|
||||
userId: req.user?._id,
|
||||
serviceAccountId: req.serviceAccount?._id,
|
||||
serviceTokenDataId: req.serviceTokenData?._id,
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
secretIds: createdSecrets.map((n) => n._id)
|
||||
})) as IAction;
|
||||
actions.push(addAction);
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: "secrets added",
|
||||
@@ -350,14 +335,6 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
||||
|
||||
await AuditLog.insertMany(auditLogs);
|
||||
|
||||
const updateAction = (await EELogService.createAction({
|
||||
name: ACTION_UPDATE_SECRETS,
|
||||
userId: req.user._id,
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
secretIds: updatedSecrets.map((u) => u._id)
|
||||
})) as IAction;
|
||||
actions.push(updateAction);
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: "secrets modified",
|
||||
@@ -428,14 +405,6 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
||||
|
||||
await AuditLog.insertMany(auditLogs);
|
||||
|
||||
const deleteAction = (await EELogService.createAction({
|
||||
name: ACTION_DELETE_SECRETS,
|
||||
userId: req.user._id,
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
secretIds: deleteSecretIds
|
||||
})) as IAction;
|
||||
actions.push(deleteAction);
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: "secrets deleted",
|
||||
@@ -451,17 +420,6 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (actions.length > 0) {
|
||||
// (EE) create (audit) log
|
||||
await EELogService.createLog({
|
||||
userId: req.user._id.toString(),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
actions,
|
||||
channel,
|
||||
ipAddress: req.realIP
|
||||
});
|
||||
}
|
||||
|
||||
// // trigger event - push secrets
|
||||
await EventService.handleEvent({
|
||||
event: eventPushSecrets({
|
||||
@@ -731,27 +689,6 @@ export const createSecrets = async (req: Request, res: Response) => {
|
||||
)
|
||||
});
|
||||
|
||||
const addAction = await EELogService.createAction({
|
||||
name: ACTION_ADD_SECRETS,
|
||||
userId: req.user?._id,
|
||||
serviceAccountId: req.serviceAccount?._id,
|
||||
serviceTokenDataId: req.serviceTokenData?._id,
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
secretIds: newlyCreatedSecrets.map((n) => n._id)
|
||||
});
|
||||
|
||||
// (EE) create (audit) log
|
||||
addAction &&
|
||||
(await EELogService.createLog({
|
||||
userId: req.user?._id,
|
||||
serviceAccountId: req.serviceAccount?._id,
|
||||
serviceTokenDataId: req.serviceTokenData?._id,
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
actions: [addAction],
|
||||
channel,
|
||||
ipAddress: req.realIP
|
||||
}));
|
||||
|
||||
// (EE) take a secret snapshot
|
||||
await EESecretService.takeSecretSnapshot({
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
@@ -960,22 +897,6 @@ export const getSecrets = async (req: Request, res: Response) => {
|
||||
secrets = await Secret.find(secretQuery).populate("tags");
|
||||
}
|
||||
|
||||
// case: client authorization is via service account
|
||||
if (req.serviceAccount) {
|
||||
const secretQuery: any = {
|
||||
workspace: workspaceId,
|
||||
environment,
|
||||
folder: folderId,
|
||||
user: { $exists: false } // shared secrets only from workspace
|
||||
};
|
||||
|
||||
if (tagIds.length > 0) {
|
||||
secretQuery.tags = { $in: tagIds };
|
||||
}
|
||||
|
||||
secrets = await Secret.find(secretQuery).populate("tags");
|
||||
}
|
||||
|
||||
// TODO(akhilmhdh) - secret-imp change this to org type
|
||||
let importedSecrets: any[] = [];
|
||||
if (include_imports) {
|
||||
@@ -988,28 +909,6 @@ export const getSecrets = async (req: Request, res: Response) => {
|
||||
);
|
||||
}
|
||||
|
||||
const channel = getUserAgentType(req.headers["user-agent"]);
|
||||
|
||||
const readAction = await EELogService.createAction({
|
||||
name: ACTION_READ_SECRETS,
|
||||
userId: req.user?._id,
|
||||
serviceAccountId: req.serviceAccount?._id,
|
||||
serviceTokenDataId: req.serviceTokenData?._id,
|
||||
workspaceId: new Types.ObjectId(workspaceId as string),
|
||||
secretIds: secrets.map((n: any) => n._id)
|
||||
});
|
||||
|
||||
readAction &&
|
||||
(await EELogService.createLog({
|
||||
userId: req.user?._id,
|
||||
serviceAccountId: req.serviceAccount?._id,
|
||||
serviceTokenDataId: req.serviceTokenData?._id,
|
||||
workspaceId: new Types.ObjectId(workspaceId as string),
|
||||
actions: [readAction],
|
||||
channel,
|
||||
ipAddress: req.realIP
|
||||
}));
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
{
|
||||
@@ -1247,27 +1146,6 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
||||
// });
|
||||
// }, 10000);
|
||||
|
||||
const updateAction = await EELogService.createAction({
|
||||
name: ACTION_UPDATE_SECRETS,
|
||||
userId: req.user?._id,
|
||||
serviceAccountId: req.serviceAccount?._id,
|
||||
serviceTokenDataId: req.serviceTokenData?._id,
|
||||
workspaceId: new Types.ObjectId(key),
|
||||
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
|
||||
});
|
||||
|
||||
// (EE) create (audit) log
|
||||
updateAction &&
|
||||
(await EELogService.createLog({
|
||||
userId: req.user?._id,
|
||||
serviceAccountId: req.serviceAccount?._id,
|
||||
serviceTokenDataId: req.serviceTokenData?._id,
|
||||
workspaceId: new Types.ObjectId(key),
|
||||
actions: [updateAction],
|
||||
channel,
|
||||
ipAddress: req.realIP
|
||||
}));
|
||||
|
||||
// (EE) take a secret snapshot
|
||||
// IMP(akhilmhdh): commented out due to unknown where the environment is
|
||||
// await EESecretService.takeSecretSnapshot({
|
||||
@@ -1387,26 +1265,6 @@ export const deleteSecrets = async (req: Request, res: Response) => {
|
||||
// workspaceId: new Types.ObjectId(key)
|
||||
// })
|
||||
// });
|
||||
const deleteAction = await EELogService.createAction({
|
||||
name: ACTION_DELETE_SECRETS,
|
||||
userId: req.user?._id,
|
||||
serviceAccountId: req.serviceAccount?._id,
|
||||
serviceTokenDataId: req.serviceTokenData?._id,
|
||||
workspaceId: new Types.ObjectId(key),
|
||||
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
|
||||
});
|
||||
|
||||
// (EE) create (audit) log
|
||||
deleteAction &&
|
||||
(await EELogService.createLog({
|
||||
userId: req.user?._id,
|
||||
serviceAccountId: req.serviceAccount?._id,
|
||||
serviceTokenDataId: req.serviceTokenData?._id,
|
||||
workspaceId: new Types.ObjectId(key),
|
||||
actions: [deleteAction],
|
||||
channel,
|
||||
ipAddress: req.realIP
|
||||
}));
|
||||
|
||||
// (EE) take a secret snapshot
|
||||
// IMP(akhilmhdh): Not sure how to take secretSnapshot
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import crypto from "crypto";
|
||||
import bcrypt from "bcrypt";
|
||||
import {
|
||||
ServiceAccount,
|
||||
ServiceAccountKey,
|
||||
ServiceAccountOrganizationPermission,
|
||||
ServiceAccountWorkspacePermission,
|
||||
} from "../../models";
|
||||
import {
|
||||
CreateServiceAccountDto,
|
||||
} from "../../interfaces/serviceAccounts/dto";
|
||||
import { BadRequestError, ServiceAccountNotFoundError } from "../../utils/errors";
|
||||
import { getSaltRounds } from "../../config";
|
||||
|
||||
/**
|
||||
* Return service account tied to the request (service account) client
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getCurrentServiceAccount = async (req: Request, res: Response) => {
|
||||
const serviceAccount = await ServiceAccount.findById(req.serviceAccount._id);
|
||||
|
||||
if (!serviceAccount) {
|
||||
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccount,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return service account with id [serviceAccountId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getServiceAccountById = async (req: Request, res: Response) => {
|
||||
const { serviceAccountId } = req.params;
|
||||
|
||||
const serviceAccount = await ServiceAccount.findById(serviceAccountId);
|
||||
|
||||
if (!serviceAccount) {
|
||||
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccount,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new service account under organization with id [organizationId]
|
||||
* that has access to workspaces [workspaces]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const createServiceAccount = async (req: Request, res: Response) => {
|
||||
const {
|
||||
name,
|
||||
organizationId,
|
||||
publicKey,
|
||||
expiresIn,
|
||||
}: CreateServiceAccountDto = req.body;
|
||||
|
||||
let expiresAt;
|
||||
if (expiresIn) {
|
||||
expiresAt = new Date();
|
||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||
}
|
||||
|
||||
const secret = crypto.randomBytes(16).toString("base64");
|
||||
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||
|
||||
// create service account
|
||||
const serviceAccount = await new ServiceAccount({
|
||||
name,
|
||||
organization: new Types.ObjectId(organizationId),
|
||||
user: req.user,
|
||||
publicKey,
|
||||
lastUsed: new Date(),
|
||||
expiresAt,
|
||||
secretHash,
|
||||
}).save()
|
||||
|
||||
const serviceAccountObj = serviceAccount.toObject();
|
||||
|
||||
delete (serviceAccountObj as any).secretHash;
|
||||
|
||||
// provision default org-level permission for service account
|
||||
await new ServiceAccountOrganizationPermission({
|
||||
serviceAccount: serviceAccount._id,
|
||||
}).save();
|
||||
|
||||
const secretId = Buffer.from(serviceAccount._id.toString(), "hex").toString("base64");
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountAccessKey: `sa.${secretId}.${secret}`,
|
||||
serviceAccount: serviceAccountObj,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change name of service account with id [serviceAccountId] to [name]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const changeServiceAccountName = async (req: Request, res: Response) => {
|
||||
const { serviceAccountId } = req.params;
|
||||
const { name } = req.body;
|
||||
|
||||
const serviceAccount = await ServiceAccount.findOneAndUpdate(
|
||||
{
|
||||
_id: new Types.ObjectId(serviceAccountId),
|
||||
},
|
||||
{
|
||||
name,
|
||||
},
|
||||
{
|
||||
new: true,
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccount,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a service account key to service account with id [serviceAccountId]
|
||||
* for workspace with id [workspaceId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const addServiceAccountKey = async (req: Request, res: Response) => {
|
||||
const {
|
||||
workspaceId,
|
||||
encryptedKey,
|
||||
nonce,
|
||||
} = req.body;
|
||||
|
||||
const serviceAccountKey = await new ServiceAccountKey({
|
||||
encryptedKey,
|
||||
nonce,
|
||||
sender: req.user._id,
|
||||
serviceAccount: req.serviceAccount._d,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
}).save();
|
||||
|
||||
return serviceAccountKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return workspace-level permission for service account with id [serviceAccountId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getServiceAccountWorkspacePermissions = async (req: Request, res: Response) => {
|
||||
const serviceAccountWorkspacePermissions = await ServiceAccountWorkspacePermission.find({
|
||||
serviceAccount: req.serviceAccount._id,
|
||||
}).populate("workspace");
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountWorkspacePermissions,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a workspace permission to service account with id [serviceAccountId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const addServiceAccountWorkspacePermission = async (req: Request, res: Response) => {
|
||||
const { serviceAccountId } = req.params;
|
||||
const {
|
||||
environment,
|
||||
workspaceId,
|
||||
read = false,
|
||||
write = false,
|
||||
encryptedKey,
|
||||
nonce,
|
||||
} = req.body;
|
||||
|
||||
if (!req.membership.workspace.environments.some((e: { name: string; slug: string }) => e.slug === environment)) {
|
||||
return res.status(400).send({
|
||||
message: "Failed to validate workspace environment",
|
||||
});
|
||||
}
|
||||
|
||||
const existingPermission = await ServiceAccountWorkspacePermission.findOne({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment,
|
||||
});
|
||||
|
||||
if (existingPermission) throw BadRequestError({ message: "Failed to add workspace permission to service account due to already-existing " });
|
||||
|
||||
const serviceAccountWorkspacePermission = await new ServiceAccountWorkspacePermission({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment,
|
||||
read,
|
||||
write,
|
||||
}).save();
|
||||
|
||||
const existingServiceAccountKey = await ServiceAccountKey.findOne({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
});
|
||||
|
||||
if (!existingServiceAccountKey) {
|
||||
await new ServiceAccountKey({
|
||||
encryptedKey,
|
||||
nonce,
|
||||
sender: req.user._id,
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
}).save();
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountWorkspacePermission,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete workspace permission from service account with id [serviceAccountId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const deleteServiceAccountWorkspacePermission = async (req: Request, res: Response) => {
|
||||
const { serviceAccountWorkspacePermissionId } = req.params;
|
||||
const serviceAccountWorkspacePermission = await ServiceAccountWorkspacePermission.findByIdAndDelete(serviceAccountWorkspacePermissionId);
|
||||
|
||||
if (serviceAccountWorkspacePermission) {
|
||||
const { serviceAccount, workspace } = serviceAccountWorkspacePermission;
|
||||
const count = await ServiceAccountWorkspacePermission.countDocuments({
|
||||
serviceAccount,
|
||||
workspace,
|
||||
});
|
||||
|
||||
if (count === 0) {
|
||||
await ServiceAccountKey.findOneAndDelete({
|
||||
serviceAccount,
|
||||
workspace,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountWorkspacePermission,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete service account with id [serviceAccountId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const deleteServiceAccount = async (req: Request, res: Response) => {
|
||||
const { serviceAccountId } = req.params;
|
||||
|
||||
const serviceAccount = await ServiceAccount.findByIdAndDelete(serviceAccountId);
|
||||
|
||||
if (serviceAccount) {
|
||||
await ServiceAccountKey.deleteMany({
|
||||
serviceAccount: serviceAccount._id,
|
||||
});
|
||||
|
||||
await ServiceAccountOrganizationPermission.deleteMany({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
});
|
||||
|
||||
await ServiceAccountWorkspacePermission.deleteMany({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccount,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return service account keys for service account with id [serviceAccountId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getServiceAccountKeys = async (req: Request, res: Response) => {
|
||||
const workspaceId = req.query.workspaceId as string;
|
||||
|
||||
const serviceAccountKeys = await ServiceAccountKey.find({
|
||||
serviceAccount: req.serviceAccount._id,
|
||||
...(workspaceId ? { workspace: new Types.ObjectId(workspaceId) } : {}),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountKeys,
|
||||
});
|
||||
}
|
||||
@@ -8,10 +8,8 @@ import { createToken, issueAuthTokens, validateProviderAuthToken } from "../../h
|
||||
import { checkUserDevice } from "../../helpers/user";
|
||||
import { sendMail } from "../../helpers/nodemailer";
|
||||
import { TokenService } from "../../services";
|
||||
import { EELogService } from "../../ee/services";
|
||||
import { BadRequestError, InternalServerError } from "../../utils/errors";
|
||||
import { ACTION_LOGIN, AuthTokenType, TOKEN_EMAIL_MFA } from "../../variables";
|
||||
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
|
||||
import { AuthTokenType, TOKEN_EMAIL_MFA } from "../../variables";
|
||||
import { getAuthSecret, getHttpsEnabled, getJwtMfaLifetime } from "../../config";
|
||||
import { AuthMethod } from "../../models/user";
|
||||
import { validateRequest } from "../../helpers/validation";
|
||||
@@ -215,19 +213,6 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
response.protectedKeyTag = user.protectedKeyTag;
|
||||
}
|
||||
|
||||
const loginAction = await EELogService.createAction({
|
||||
name: ACTION_LOGIN,
|
||||
userId: user._id
|
||||
});
|
||||
|
||||
loginAction &&
|
||||
(await EELogService.createLog({
|
||||
userId: user._id,
|
||||
actions: [loginAction],
|
||||
channel: getUserAgentType(req.headers["user-agent"]),
|
||||
ipAddress: req.realIP
|
||||
}));
|
||||
|
||||
return res.status(200).send(response);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Request, Response } from "express";
|
||||
import { Action } from "../../models";
|
||||
import { ActionNotFoundError } from "../../../utils/errors";
|
||||
import { validateRequest } from "../../../helpers/validation";
|
||||
import * as reqValidator from "../../../validation/action";
|
||||
|
||||
export const getAction = async (req: Request, res: Response) => {
|
||||
let action;
|
||||
try {
|
||||
const {
|
||||
params: { actionId }
|
||||
} = await validateRequest(reqValidator.GetActionV1, req);
|
||||
|
||||
action = await Action.findById(actionId).populate([
|
||||
"payload.secretVersions.oldSecretVersion",
|
||||
"payload.secretVersions.newSecretVersion"
|
||||
]);
|
||||
|
||||
if (!action)
|
||||
throw ActionNotFoundError({
|
||||
message: "Failed to find action"
|
||||
});
|
||||
} catch (err) {
|
||||
throw ActionNotFoundError({
|
||||
message: "Failed to find action"
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
action
|
||||
});
|
||||
};
|
||||
@@ -4,7 +4,6 @@ import * as organizationsController from "./organizationsController";
|
||||
import * as ssoController from "./ssoController";
|
||||
import * as usersController from "./usersController";
|
||||
import * as workspaceController from "./workspaceController";
|
||||
import * as actionController from "./actionController";
|
||||
import * as membershipController from "./membershipController";
|
||||
import * as cloudProductsController from "./cloudProductsController";
|
||||
import * as roleController from "./roleController";
|
||||
@@ -20,7 +19,6 @@ export {
|
||||
ssoController,
|
||||
usersController,
|
||||
workspaceController,
|
||||
actionController,
|
||||
membershipController,
|
||||
cloudProductsController,
|
||||
roleController,
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
FolderVersion,
|
||||
IPType,
|
||||
ISecretVersion,
|
||||
Log,
|
||||
SecretSnapshot,
|
||||
SecretVersion,
|
||||
ServiceActor,
|
||||
@@ -38,7 +37,6 @@ import {
|
||||
DeleteWorkspaceTrustedIpV1,
|
||||
GetWorkspaceAuditLogActorFilterOptsV1,
|
||||
GetWorkspaceAuditLogsV1,
|
||||
GetWorkspaceLogsV1,
|
||||
GetWorkspaceSecretSnapshotsCountV1,
|
||||
GetWorkspaceSecretSnapshotsV1,
|
||||
GetWorkspaceTrustedIpsV1,
|
||||
@@ -563,112 +561,6 @@ export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Respons
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return (audit) logs for workspace with id [workspaceId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getWorkspaceLogs = async (req: Request, res: Response) => {
|
||||
/*
|
||||
#swagger.summary = 'Return project (audit) logs'
|
||||
#swagger.description = 'Return project (audit) logs'
|
||||
|
||||
#swagger.security = [{
|
||||
"apiKeyAuth": []
|
||||
}]
|
||||
|
||||
#swagger.parameters['workspaceId'] = {
|
||||
"description": "ID of project",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['userId'] = {
|
||||
"description": "ID of project member",
|
||||
"required": false,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['offset'] = {
|
||||
"description": "Number of logs to skip",
|
||||
"required": false,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['limit'] = {
|
||||
"description": "Maximum number of logs to return",
|
||||
"required": false,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.parameters['sortBy'] = {
|
||||
"description": "Order to sort the logs by",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"@enum": ["oldest", "recent"]
|
||||
},
|
||||
"required": false
|
||||
}
|
||||
|
||||
#swagger.parameters['actionNames'] = {
|
||||
"description": "Names of log actions (comma-separated)",
|
||||
"required": false,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
#swagger.responses[200] = {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"logs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
$ref: "#/components/schemas/Log"
|
||||
},
|
||||
"description": "Project logs"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
const {
|
||||
query: { limit, offset, userId, sortBy, actionNames },
|
||||
params: { workspaceId }
|
||||
} = await validateRequest(GetWorkspaceLogsV1, req);
|
||||
|
||||
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.AuditLogs
|
||||
);
|
||||
|
||||
const logs = await Log.find({
|
||||
workspace: workspaceId,
|
||||
...(userId ? { user: userId } : {}),
|
||||
...(actionNames
|
||||
? {
|
||||
actionNames: {
|
||||
$in: actionNames.split(",")
|
||||
}
|
||||
}
|
||||
: {})
|
||||
})
|
||||
.sort({ createdAt: sortBy === "recent" ? -1 : 1 })
|
||||
.skip(offset)
|
||||
.limit(limit)
|
||||
.populate("actions")
|
||||
.populate("user serviceAccount serviceTokenData");
|
||||
|
||||
return res.status(200).send({
|
||||
logs
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return audit logs for workspace with id [workspaceId]
|
||||
* @param req
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
import { Types } from "mongoose";
|
||||
import { Action } from "../models";
|
||||
import {
|
||||
getLatestNSecretSecretVersionIds,
|
||||
getLatestSecretVersionIds,
|
||||
} from "../helpers/secretVersion";
|
||||
import {
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
} from "../../variables";
|
||||
|
||||
/**
|
||||
* Create an (audit) action for updating secrets
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.name - name of action
|
||||
* @param {Types.ObjectId} obj.secretIds - ids of relevant secrets
|
||||
* @returns {Action} action - new action
|
||||
*/
|
||||
const createActionUpdateSecret = async ({
|
||||
name,
|
||||
userId,
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
secretIds,
|
||||
}: {
|
||||
name: string;
|
||||
userId?: Types.ObjectId;
|
||||
serviceAccountId?: Types.ObjectId;
|
||||
serviceTokenDataId?: Types.ObjectId;
|
||||
workspaceId: Types.ObjectId;
|
||||
secretIds: Types.ObjectId[];
|
||||
}) => {
|
||||
const latestSecretVersions = (await getLatestNSecretSecretVersionIds({
|
||||
secretIds,
|
||||
n: 2,
|
||||
}))
|
||||
.map((s) => ({
|
||||
oldSecretVersion: s.versions[0]._id,
|
||||
newSecretVersion: s.versions[1]._id,
|
||||
}));
|
||||
|
||||
const action = await new Action({
|
||||
name,
|
||||
user: userId,
|
||||
serviceAccount: serviceAccountId,
|
||||
serviceTokenData: serviceTokenDataId,
|
||||
workspace: workspaceId,
|
||||
payload: {
|
||||
secretVersions: latestSecretVersions,
|
||||
},
|
||||
}).save();
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an (audit) action for creating, reading, and deleting
|
||||
* secrets
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.name - name of action
|
||||
* @param {Types.ObjectId} obj.secretIds - ids of relevant secrets
|
||||
* @returns {Action} action - new action
|
||||
*/
|
||||
const createActionSecret = async ({
|
||||
name,
|
||||
userId,
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
secretIds,
|
||||
}: {
|
||||
name: string;
|
||||
userId?: Types.ObjectId;
|
||||
serviceAccountId?: Types.ObjectId;
|
||||
serviceTokenDataId?: Types.ObjectId;
|
||||
workspaceId: Types.ObjectId;
|
||||
secretIds: Types.ObjectId[];
|
||||
}) => {
|
||||
// case: action is adding, deleting, or reading secrets
|
||||
// -> add new secret versions
|
||||
const latestSecretVersions = (await getLatestSecretVersionIds({
|
||||
secretIds,
|
||||
}))
|
||||
.map((s) => ({
|
||||
newSecretVersion: s.versionId,
|
||||
}));
|
||||
|
||||
const action = await new Action({
|
||||
name,
|
||||
user: userId,
|
||||
serviceAccount: serviceAccountId,
|
||||
serviceTokenData: serviceTokenDataId,
|
||||
workspace: workspaceId,
|
||||
payload: {
|
||||
secretVersions: latestSecretVersions,
|
||||
},
|
||||
}).save();
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an (audit) action for client with id [userId],
|
||||
* [serviceAccountId], or [serviceTokenDataId]
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.name - name of action
|
||||
* @param {String} obj.userId - id of user associated with action
|
||||
* @returns
|
||||
*/
|
||||
const createActionClient = ({
|
||||
name,
|
||||
userId,
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
}: {
|
||||
name: string;
|
||||
userId?: Types.ObjectId;
|
||||
serviceAccountId?: Types.ObjectId;
|
||||
serviceTokenDataId?: Types.ObjectId;
|
||||
}) => {
|
||||
const action = new Action({
|
||||
name,
|
||||
user: userId,
|
||||
serviceAccount: serviceAccountId,
|
||||
serviceTokenData: serviceTokenDataId,
|
||||
}).save();
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an (audit) action.
|
||||
* @param {Object} obj
|
||||
* @param {Object} obj.name - name of action
|
||||
* @param {Types.ObjectId} obj.userId - id of user associated with action
|
||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with action
|
||||
* @param {Types.ObjectId[]} obj.secretIds - ids of secrets associated with action
|
||||
*/
|
||||
const createActionHelper = async ({
|
||||
name,
|
||||
userId,
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
secretIds,
|
||||
}: {
|
||||
name: string;
|
||||
userId?: Types.ObjectId;
|
||||
serviceAccountId?: Types.ObjectId;
|
||||
serviceTokenDataId?: Types.ObjectId;
|
||||
workspaceId?: Types.ObjectId;
|
||||
secretIds?: Types.ObjectId[];
|
||||
}) => {
|
||||
let action;
|
||||
switch (name) {
|
||||
case ACTION_LOGIN:
|
||||
case ACTION_LOGOUT:
|
||||
action = await createActionClient({
|
||||
name,
|
||||
userId,
|
||||
});
|
||||
break;
|
||||
case ACTION_ADD_SECRETS:
|
||||
case ACTION_READ_SECRETS:
|
||||
case ACTION_DELETE_SECRETS:
|
||||
if (!workspaceId || !secretIds) throw new Error("Missing required params workspace id or secret ids to create action secret");
|
||||
action = await createActionSecret({
|
||||
name,
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds,
|
||||
});
|
||||
break;
|
||||
case ACTION_UPDATE_SECRETS:
|
||||
if (!workspaceId || !secretIds) throw new Error("Missing required params workspace id or secret ids to create action secret");
|
||||
action = await createActionUpdateSecret({
|
||||
name,
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
export {
|
||||
createActionHelper,
|
||||
};
|
||||
@@ -1,50 +0,0 @@
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
IAction,
|
||||
Log,
|
||||
} from "../models";
|
||||
|
||||
/**
|
||||
* Create an (audit) log
|
||||
* @param {Object} obj
|
||||
* @param {Types.ObjectId} obj.userId - id of user associated with the log
|
||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with the log
|
||||
* @param {IAction[]} obj.actions - actions to include in log
|
||||
* @param {String} obj.channel - channel (web/cli/auto) associated with the log
|
||||
* @param {String} obj.ipAddress - ip address associated with the log
|
||||
* @returns {Log} log - new audit log
|
||||
*/
|
||||
const createLogHelper = async ({
|
||||
userId,
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
actions,
|
||||
channel,
|
||||
ipAddress,
|
||||
}: {
|
||||
userId?: Types.ObjectId;
|
||||
serviceAccountId?: Types.ObjectId;
|
||||
serviceTokenDataId?: Types.ObjectId;
|
||||
workspaceId?: Types.ObjectId;
|
||||
actions: IAction[];
|
||||
channel: string;
|
||||
ipAddress: string;
|
||||
}) => {
|
||||
const log = await new Log({
|
||||
user: userId,
|
||||
serviceAccount: serviceAccountId,
|
||||
serviceTokenData: serviceTokenDataId,
|
||||
workspace: workspaceId ?? undefined,
|
||||
actionNames: actions.map((a) => a.name),
|
||||
actions,
|
||||
channel,
|
||||
ipAddress,
|
||||
}).save();
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
export {
|
||||
createLogHelper,
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
import {
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
} from "../../variables";
|
||||
|
||||
export interface IAction {
|
||||
name: string;
|
||||
user?: Types.ObjectId,
|
||||
serviceAccount?: Types.ObjectId,
|
||||
serviceTokenData?: Types.ObjectId,
|
||||
workspace?: Types.ObjectId,
|
||||
payload?: {
|
||||
secretVersions?: Types.ObjectId[]
|
||||
}
|
||||
}
|
||||
|
||||
const actionSchema = new Schema<IAction>(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
enum: [
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
],
|
||||
},
|
||||
user: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
},
|
||||
serviceAccount: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "ServiceAccount",
|
||||
},
|
||||
serviceTokenData: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "ServiceTokenData",
|
||||
},
|
||||
workspace: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Workspace",
|
||||
},
|
||||
payload: {
|
||||
secretVersions: [{
|
||||
oldSecretVersion: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "SecretVersion",
|
||||
},
|
||||
newSecretVersion: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "SecretVersion",
|
||||
},
|
||||
}],
|
||||
},
|
||||
}, {
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
export const Action = model<IAction>("Action", actionSchema);
|
||||
@@ -1,9 +1,7 @@
|
||||
export * from "./secretSnapshot";
|
||||
export * from "./secretVersion";
|
||||
export * from "./folderVersion";
|
||||
export * from "./log";
|
||||
export * from "./role";
|
||||
export * from "./action";
|
||||
export * from "./ssoConfig";
|
||||
export * from "./trustedIp";
|
||||
export * from "./auditLog";
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
import {
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
} from "../../variables";
|
||||
|
||||
export interface ILog {
|
||||
_id: Types.ObjectId;
|
||||
user?: Types.ObjectId;
|
||||
serviceAccount?: Types.ObjectId;
|
||||
serviceTokenData?: Types.ObjectId;
|
||||
workspace?: Types.ObjectId;
|
||||
actionNames: string[];
|
||||
actions: Types.ObjectId[];
|
||||
channel: string;
|
||||
ipAddress?: string;
|
||||
}
|
||||
|
||||
const logSchema = new Schema<ILog>(
|
||||
{
|
||||
user: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
},
|
||||
serviceAccount: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "ServiceAccount",
|
||||
},
|
||||
serviceTokenData: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "ServiceTokenData",
|
||||
},
|
||||
workspace: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Workspace",
|
||||
},
|
||||
actionNames: {
|
||||
type: [String],
|
||||
enum: [
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
actions: [{
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Action",
|
||||
required: true,
|
||||
}],
|
||||
channel: {
|
||||
type: String,
|
||||
enum: ["web", "cli", "auto", "k8-operator", "other"],
|
||||
required: true,
|
||||
},
|
||||
ipAddress: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
export const Log = model<ILog>("Log", logSchema);
|
||||
@@ -1,8 +0,0 @@
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import { actionController } from "../../controllers/v1";
|
||||
|
||||
// TODO: put into action controller
|
||||
router.get("/:actionId", actionController.getAction);
|
||||
|
||||
export default router;
|
||||
@@ -4,7 +4,6 @@ import organizations from "./organizations";
|
||||
import sso from "./sso";
|
||||
import users from "./users";
|
||||
import workspace from "./workspace";
|
||||
import action from "./action";
|
||||
import cloudProducts from "./cloudProducts";
|
||||
import secretScanning from "./secretScanning";
|
||||
import roles from "./role";
|
||||
@@ -20,7 +19,6 @@ export {
|
||||
sso,
|
||||
users,
|
||||
workspace,
|
||||
action,
|
||||
cloudProducts,
|
||||
secretScanning,
|
||||
roles,
|
||||
|
||||
@@ -28,14 +28,6 @@ router.post(
|
||||
workspaceController.rollbackWorkspaceSecretSnapshot
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:workspaceId/logs",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
workspaceController.getWorkspaceLogs
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:workspaceId/audit-logs",
|
||||
requireAuth({
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
IAction,
|
||||
} from "../models";
|
||||
import {
|
||||
createLogHelper,
|
||||
} from "../helpers/log";
|
||||
import {
|
||||
createActionHelper,
|
||||
} from "../helpers/action";
|
||||
import EELicenseService from "./EELicenseService";
|
||||
|
||||
/**
|
||||
* Class to handle Enterprise Edition log actions
|
||||
*/
|
||||
class EELogService {
|
||||
/**
|
||||
* Create an (audit) log
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.userId - id of user associated with the log
|
||||
* @param {String} obj.workspaceId - id of workspace associated with the log
|
||||
* @param {Action} obj.actions - actions to include in log
|
||||
* @param {String} obj.channel - channel (web/cli/auto) associated with the log
|
||||
* @param {String} obj.ipAddress - ip address associated with the log
|
||||
* @returns {Log} log - new audit log
|
||||
*/
|
||||
static async createLog({
|
||||
userId,
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
actions,
|
||||
channel,
|
||||
ipAddress,
|
||||
}: {
|
||||
userId?: Types.ObjectId;
|
||||
serviceAccountId?: Types.ObjectId;
|
||||
serviceTokenDataId?: Types.ObjectId;
|
||||
workspaceId?: Types.ObjectId;
|
||||
actions: IAction[];
|
||||
channel: string;
|
||||
ipAddress: string;
|
||||
}) {
|
||||
if (!EELicenseService.isLicenseValid) return null;
|
||||
return await createLogHelper({
|
||||
userId,
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
actions,
|
||||
channel,
|
||||
ipAddress,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an (audit) action
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.name - name of action
|
||||
* @param {Types.ObjectId} obj.userId - id of user associated with the action
|
||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with the action
|
||||
* @param {ObjectId[]} obj.secretIds - ids of secrets associated with the action
|
||||
* @returns {Action} action - new action
|
||||
*/
|
||||
static async createAction({
|
||||
name,
|
||||
userId,
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
secretIds,
|
||||
}: {
|
||||
name: string;
|
||||
userId?: Types.ObjectId;
|
||||
serviceAccountId?: Types.ObjectId;
|
||||
serviceTokenDataId?: Types.ObjectId;
|
||||
workspaceId?: Types.ObjectId;
|
||||
secretIds?: Types.ObjectId[];
|
||||
}) {
|
||||
return await createActionHelper({
|
||||
name,
|
||||
userId,
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
secretIds,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default EELogService;
|
||||
@@ -1,13 +1,11 @@
|
||||
import EELicenseService from "./EELicenseService";
|
||||
import EESecretService from "./EESecretService";
|
||||
import EELogService from "./EELogService";
|
||||
import EEAuditLogService from "./EEAuditLogService";
|
||||
import GithubSecretScanningService from "./GithubSecretScanning/GithubSecretScanningService"
|
||||
|
||||
export {
|
||||
EELicenseService,
|
||||
EESecretService,
|
||||
EELogService,
|
||||
EEAuditLogService,
|
||||
GithubSecretScanningService
|
||||
}
|
||||
@@ -23,13 +23,11 @@ import {
|
||||
Workspace
|
||||
} from "../models";
|
||||
import {
|
||||
Action,
|
||||
AuditLog,
|
||||
FolderVersion,
|
||||
GitAppInstallationSession,
|
||||
GitAppOrganizationInstallation,
|
||||
GitRisks,
|
||||
Log,
|
||||
Role,
|
||||
SSOConfig,
|
||||
SecretApprovalPolicy,
|
||||
@@ -288,18 +286,6 @@ export const deleteOrganization = async ({
|
||||
}
|
||||
});
|
||||
|
||||
await Log.deleteMany({
|
||||
workspace: {
|
||||
$in: workspaceIds
|
||||
}
|
||||
});
|
||||
|
||||
await Action.deleteMany({
|
||||
workspace: {
|
||||
$in: workspaceIds
|
||||
}
|
||||
});
|
||||
|
||||
await SecretApprovalPolicy.deleteMany({
|
||||
workspace: {
|
||||
$in: workspaceIds
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { Types } from "mongoose";
|
||||
import { ISecret, Secret } from "../models";
|
||||
import { EELogService, EESecretService } from "../ee/services";
|
||||
import { IAction, SecretVersion } from "../ee/models";
|
||||
import { EESecretService } from "../ee/services";
|
||||
import { SecretVersion } from "../ee/models";
|
||||
import {
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
SECRET_PERSONAL,
|
||||
@@ -320,7 +316,6 @@ export const v2PushSecrets = async ({
|
||||
ipAddress: string;
|
||||
}): Promise<void> => {
|
||||
// TODO: clean up function and fix up types
|
||||
const actions: IAction[] = [];
|
||||
|
||||
// construct useful data structures
|
||||
const oldSecrets = await getSecrets({
|
||||
@@ -356,15 +351,6 @@ export const v2PushSecrets = async ({
|
||||
await EESecretService.markDeletedSecretVersions({
|
||||
secretIds: toDelete,
|
||||
});
|
||||
|
||||
const deleteAction = await EELogService.createAction({
|
||||
name: ACTION_DELETE_SECRETS,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(userId),
|
||||
secretIds: toDelete,
|
||||
});
|
||||
|
||||
deleteAction && actions.push(deleteAction);
|
||||
}
|
||||
|
||||
const toUpdate = oldSecrets.filter((s) => {
|
||||
@@ -451,15 +437,6 @@ export const v2PushSecrets = async ({
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
const updateAction = await EELogService.createAction({
|
||||
name: ACTION_UPDATE_SECRETS,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
secretIds: toUpdate.map((u) => u._id),
|
||||
});
|
||||
|
||||
updateAction && actions.push(updateAction);
|
||||
}
|
||||
|
||||
// handle adding new secrets
|
||||
@@ -494,14 +471,6 @@ export const v2PushSecrets = async ({
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
const addAction = await EELogService.createAction({
|
||||
name: ACTION_ADD_SECRETS,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
secretIds: newSecrets.map((n) => n._id),
|
||||
});
|
||||
addAction && actions.push(addAction);
|
||||
}
|
||||
|
||||
// (EE) take a secret snapshot
|
||||
@@ -509,17 +478,6 @@ export const v2PushSecrets = async ({
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
environment,
|
||||
});
|
||||
|
||||
// (EE) create (audit) log
|
||||
if (actions.length > 0) {
|
||||
await EELogService.createLog({
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
actions,
|
||||
channel,
|
||||
ipAddress,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -589,22 +547,6 @@ export const pullSecrets = async ({
|
||||
environment,
|
||||
});
|
||||
|
||||
const readAction = await EELogService.createAction({
|
||||
name: ACTION_READ_SECRETS,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
secretIds: secrets.map((n: any) => n._id),
|
||||
});
|
||||
|
||||
readAction &&
|
||||
(await EELogService.createLog({
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
actions: [readAction],
|
||||
channel,
|
||||
ipAddress,
|
||||
}));
|
||||
|
||||
return secrets;
|
||||
};
|
||||
|
||||
|
||||
@@ -29,10 +29,6 @@ import {
|
||||
UnauthorizedRequestError
|
||||
} from "../utils/errors";
|
||||
import {
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_BASE64,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
@@ -48,8 +44,8 @@ import {
|
||||
} from "../utils/crypto";
|
||||
import { TelemetryService } from "../services";
|
||||
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
|
||||
import { EEAuditLogService, EELogService, EESecretService } from "../ee/services";
|
||||
import { getAuthDataPayloadIdObj, getAuthDataPayloadUserObj } from "../utils/authn/helpers";
|
||||
import { EEAuditLogService, EESecretService } from "../ee/services";
|
||||
import { getAuthDataPayloadUserObj } from "../utils/authn/helpers";
|
||||
import { getFolderByPath, getFolderIdFromServiceToken } from "../services/FolderService";
|
||||
import picomatch from "picomatch";
|
||||
import path from "path";
|
||||
@@ -478,23 +474,6 @@ export const createSecretHelper = async ({
|
||||
secretVersions: [secretVersion]
|
||||
});
|
||||
|
||||
// (EE) create (audit) log
|
||||
const action = await EELogService.createAction({
|
||||
name: ACTION_ADD_SECRETS,
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
secretIds: [secret._id]
|
||||
});
|
||||
|
||||
action &&
|
||||
(await EELogService.createLog({
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
actions: [action],
|
||||
channel: authData.userAgentType,
|
||||
ipAddress: authData.ipAddress
|
||||
}));
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
authData,
|
||||
{
|
||||
@@ -597,23 +576,6 @@ export const getSecretsHelper = async ({
|
||||
.lean()
|
||||
);
|
||||
|
||||
// (EE) create (audit) log
|
||||
const action = await EELogService.createAction({
|
||||
name: ACTION_READ_SECRETS,
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
secretIds: secrets.map((secret) => secret._id)
|
||||
});
|
||||
|
||||
action &&
|
||||
(await EELogService.createLog({
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
actions: [action],
|
||||
channel: authData.userAgentType,
|
||||
ipAddress: authData.ipAddress
|
||||
}));
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
authData,
|
||||
{
|
||||
@@ -725,23 +687,6 @@ export const getSecretHelper = async ({
|
||||
|
||||
if (!secret) throw SecretNotFoundError();
|
||||
|
||||
// (EE) create (audit) log
|
||||
const action = await EELogService.createAction({
|
||||
name: ACTION_READ_SECRETS,
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
secretIds: [secret._id]
|
||||
});
|
||||
|
||||
action &&
|
||||
(await EELogService.createLog({
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
actions: [action],
|
||||
channel: authData.userAgentType,
|
||||
ipAddress: authData.ipAddress
|
||||
}));
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
authData,
|
||||
{
|
||||
@@ -945,23 +890,6 @@ export const updateSecretHelper = async ({
|
||||
secretVersions: [secretVersion]
|
||||
});
|
||||
|
||||
// (EE) create (audit) log
|
||||
const action = await EELogService.createAction({
|
||||
name: ACTION_UPDATE_SECRETS,
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
secretIds: [secret._id]
|
||||
});
|
||||
|
||||
action &&
|
||||
(await EELogService.createLog({
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
actions: [action],
|
||||
channel: authData.userAgentType,
|
||||
ipAddress: authData.ipAddress
|
||||
}));
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
authData,
|
||||
{
|
||||
@@ -1089,23 +1017,6 @@ export const deleteSecretHelper = async ({
|
||||
secretIds: secrets.map((secret) => secret._id)
|
||||
});
|
||||
|
||||
// (EE) create (audit) log
|
||||
const action = await EELogService.createAction({
|
||||
name: ACTION_DELETE_SECRETS,
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
secretIds: secrets.map((secret) => secret._id)
|
||||
});
|
||||
|
||||
action &&
|
||||
(await EELogService.createLog({
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
actions: [action],
|
||||
channel: authData.userAgentType,
|
||||
ipAddress: authData.ipAddress
|
||||
}));
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
authData,
|
||||
{
|
||||
@@ -1769,22 +1680,6 @@ export const deleteSecretBatchHelper = async ({
|
||||
secretIds: deletedSecrets.map((secret) => secret._id)
|
||||
});
|
||||
|
||||
const action = await EELogService.createAction({
|
||||
name: ACTION_DELETE_SECRETS,
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
secretIds: deletedSecrets.map((secret) => secret._id)
|
||||
});
|
||||
|
||||
action &&
|
||||
(await EELogService.createLog({
|
||||
...getAuthDataPayloadIdObj(authData),
|
||||
workspaceId,
|
||||
actions: [action],
|
||||
channel: authData.userAgentType,
|
||||
ipAddress: authData.ipAddress
|
||||
}));
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
authData,
|
||||
{
|
||||
|
||||
@@ -10,10 +10,6 @@ import {
|
||||
User,
|
||||
UserAction
|
||||
} from "../models";
|
||||
import {
|
||||
Action,
|
||||
Log
|
||||
} from "../ee/models";
|
||||
import { sendMail } from "./nodemailer";
|
||||
import {
|
||||
InternalServerError,
|
||||
@@ -247,14 +243,6 @@ export const deleteUser = async ({
|
||||
user: user._id
|
||||
});
|
||||
|
||||
await Action.deleteMany({
|
||||
user: user._id
|
||||
});
|
||||
|
||||
await Log.deleteMany({
|
||||
user: user._id
|
||||
});
|
||||
|
||||
await TokenVersion.deleteMany({
|
||||
user: user._id
|
||||
});
|
||||
|
||||
@@ -19,11 +19,9 @@ import {
|
||||
Workspace
|
||||
} from "../models";
|
||||
import {
|
||||
Action,
|
||||
AuditLog,
|
||||
FolderVersion,
|
||||
IPType,
|
||||
Log,
|
||||
SecretApprovalPolicy,
|
||||
SecretApprovalRequest,
|
||||
SecretSnapshot,
|
||||
@@ -193,14 +191,6 @@ export const deleteWorkspace = async ({
|
||||
workspace: workspace._id
|
||||
});
|
||||
|
||||
await Log.deleteMany({
|
||||
workspace: workspace._id
|
||||
});
|
||||
|
||||
await Action.deleteMany({
|
||||
workspace: workspace._id
|
||||
});
|
||||
|
||||
await SecretApprovalPolicy.deleteMany({
|
||||
workspace: workspace._id
|
||||
});
|
||||
|
||||
@@ -18,7 +18,6 @@ const swaggerFile = require("../spec.json");
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
import { apiLimiter } from "./helpers/rateLimiter";
|
||||
import {
|
||||
action as eeActionRouter,
|
||||
cloudProducts as eeCloudProductsRouter,
|
||||
organizations as eeOrganizationsRouter,
|
||||
sso as eeSSORouter,
|
||||
@@ -193,7 +192,6 @@ const main = async () => {
|
||||
app.use("/api/v1/secret-snapshot", eeSecretSnapshotRouter);
|
||||
app.use("/api/v1/users", eeUsersRouter);
|
||||
app.use("/api/v1/workspace", eeWorkspaceRouter);
|
||||
app.use("/api/v1/action", eeActionRouter);
|
||||
app.use("/api/v1/organizations", eeOrganizationsRouter);
|
||||
app.use("/api/v1/sso", eeSSORouter);
|
||||
app.use("/api/v1/cloud-products", eeCloudProductsRouter);
|
||||
@@ -240,14 +238,13 @@ const main = async () => {
|
||||
app.use("/api/v2/secret", v2SecretRouter); // deprecate
|
||||
app.use("/api/v2/secrets", v2SecretsRouter); // note: in the process of moving to v3/secrets
|
||||
app.use("/api/v2/service-token", v2ServiceTokenDataRouter);
|
||||
// app.use("/api/v2/service-accounts", v2ServiceAccountsRouter); // new
|
||||
|
||||
// v3 routes (experimental)
|
||||
app.use("/api/v3/auth", v3AuthRouter);
|
||||
app.use("/api/v3/secrets", v3SecretsRouter);
|
||||
app.use("/api/v3/workspaces", v3WorkspacesRouter);
|
||||
app.use("/api/v3/signup", v3SignupRouter);
|
||||
app.use("/api/v3/users", v3UsersRouter);
|
||||
app.use("/api/v3/us", v3UsersRouter);
|
||||
|
||||
// api docs
|
||||
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerFile));
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
interface AddServiceAccountPermissionDto {
|
||||
name: string;
|
||||
workspaceId?: string;
|
||||
environment?: string;
|
||||
}
|
||||
|
||||
export default AddServiceAccountPermissionDto;
|
||||
@@ -1,8 +0,0 @@
|
||||
interface CreateServiceAccountDto {
|
||||
organizationId: string;
|
||||
name: string;
|
||||
publicKey: string;
|
||||
expiresIn: number;
|
||||
}
|
||||
|
||||
export default CreateServiceAccountDto;
|
||||
@@ -1,7 +0,0 @@
|
||||
import CreateServiceAccountDto from "./CreateServiceAccountDto";
|
||||
import AddServiceAccountPermissionDto from "./AddServiceAccountPermissionDto";
|
||||
|
||||
export {
|
||||
CreateServiceAccountDto,
|
||||
AddServiceAccountPermissionDto,
|
||||
}
|
||||
@@ -10,8 +10,6 @@ import requireIntegrationAuth from "./requireIntegrationAuth";
|
||||
import requireIntegrationAuthorizationAuth from "./requireIntegrationAuthorizationAuth";
|
||||
import requireServiceTokenAuth from "./requireServiceTokenAuth";
|
||||
import requireServiceTokenDataAuth from "./requireServiceTokenDataAuth";
|
||||
import requireServiceAccountAuth from "./requireServiceAccountAuth";
|
||||
import requireServiceAccountWorkspacePermissionAuth from "./requireServiceAccountWorkspacePermissionAuth";
|
||||
import requireSecretAuth from "./requireSecretAuth";
|
||||
import requireSecretsAuth from "./requireSecretsAuth";
|
||||
import requireBlindIndicesEnabled from "./requireBlindIndicesEnabled";
|
||||
@@ -32,8 +30,6 @@ export {
|
||||
requireIntegrationAuthorizationAuth,
|
||||
requireServiceTokenAuth,
|
||||
requireServiceTokenDataAuth,
|
||||
requireServiceAccountAuth,
|
||||
requireServiceAccountWorkspacePermissionAuth,
|
||||
requireSecretAuth,
|
||||
requireSecretsAuth,
|
||||
requireBlindIndicesEnabled,
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { validateClientForServiceAccount } from "../validation";
|
||||
|
||||
type req = "params" | "body" | "query";
|
||||
|
||||
const requireServiceAccountAuth = ({
|
||||
acceptedRoles,
|
||||
acceptedStatuses,
|
||||
locationServiceAccountId = "params",
|
||||
requiredPermissions = [],
|
||||
}: {
|
||||
acceptedRoles: string[];
|
||||
acceptedStatuses: string[];
|
||||
locationServiceAccountId?: req;
|
||||
requiredPermissions?: string[];
|
||||
}) => {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
const serviceAccountId = req[locationServiceAccountId].serviceAccountId;
|
||||
|
||||
req.serviceAccount = await validateClientForServiceAccount({
|
||||
authData: req.authData,
|
||||
serviceAccountId: new Types.ObjectId(serviceAccountId),
|
||||
requiredPermissions,
|
||||
});
|
||||
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
export default requireServiceAccountAuth;
|
||||
@@ -1,52 +0,0 @@
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import { ServiceAccount, ServiceAccountWorkspacePermission } from "../models";
|
||||
import {
|
||||
ServiceAccountNotFoundError,
|
||||
} from "../utils/errors";
|
||||
import {
|
||||
validateMembershipOrg,
|
||||
} from "../helpers/membershipOrg";
|
||||
|
||||
type req = "params" | "body" | "query";
|
||||
|
||||
const requireServiceAccountWorkspacePermissionAuth = ({
|
||||
acceptedRoles,
|
||||
acceptedStatuses,
|
||||
location = "params",
|
||||
}: {
|
||||
acceptedRoles: Array<"owner" | "admin" | "member">;
|
||||
acceptedStatuses: Array<"invited" | "accepted">;
|
||||
location?: req;
|
||||
}) => {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
const serviceAccountWorkspacePermissionId = req[location].serviceAccountWorkspacePermissionId;
|
||||
const serviceAccountWorkspacePermission = await ServiceAccountWorkspacePermission.findById(serviceAccountWorkspacePermissionId);
|
||||
|
||||
if (!serviceAccountWorkspacePermission) {
|
||||
return next(ServiceAccountNotFoundError({ message: "Failed to locate Service Account workspace permission" }));
|
||||
}
|
||||
|
||||
const serviceAccount = await ServiceAccount.findById(serviceAccountWorkspacePermission.serviceAccount);
|
||||
|
||||
if (!serviceAccount) {
|
||||
return next(ServiceAccountNotFoundError({ message: "Failed to locate Service Account" }));
|
||||
}
|
||||
|
||||
if (serviceAccount.user.toString() !== req.user.id.toString()) {
|
||||
// case: creator of the service account is different from
|
||||
// the user on the request -> apply middleware role/status validation
|
||||
await validateMembershipOrg({
|
||||
userId: req.user._id,
|
||||
organizationId: serviceAccount.organization,
|
||||
acceptedRoles,
|
||||
acceptedStatuses,
|
||||
});
|
||||
}
|
||||
|
||||
req.serviceAccount = serviceAccount;
|
||||
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
export default requireServiceAccountWorkspacePermissionAuth;
|
||||
@@ -15,10 +15,6 @@ export * from "./folder";
|
||||
export * from "./secretImports";
|
||||
export * from "./secretBlindIndexData";
|
||||
export * from "./serviceToken"; // TODO: deprecate
|
||||
export * from "./serviceAccount"; // TODO: deprecate
|
||||
export * from "./serviceAccountKey"; // TODO: deprecate
|
||||
export * from "./serviceAccountOrganizationPermission"; // TODO: deprecate
|
||||
export * from "./serviceAccountWorkspacePermission"; // TODO: deprecate
|
||||
export * from "./tokenData";
|
||||
export * from "./user";
|
||||
export * from "./userAction";
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { Document, Schema, Types, model } from "mongoose";
|
||||
|
||||
export interface IServiceAccount extends Document {
|
||||
_id: Types.ObjectId;
|
||||
name: string;
|
||||
organization: Types.ObjectId;
|
||||
user: Types.ObjectId;
|
||||
publicKey: string;
|
||||
lastUsed: Date;
|
||||
expiresAt: Date;
|
||||
secretHash: string;
|
||||
}
|
||||
|
||||
const serviceAccountSchema = new Schema<IServiceAccount>(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
organization: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Organization",
|
||||
required: true,
|
||||
},
|
||||
user: { // user who created the service account
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
required: true,
|
||||
},
|
||||
publicKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
lastUsed: {
|
||||
type: Date,
|
||||
},
|
||||
expiresAt: {
|
||||
type: Date,
|
||||
},
|
||||
secretHash: {
|
||||
type: String,
|
||||
required: true,
|
||||
select: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
export const ServiceAccount = model<IServiceAccount>("ServiceAccount", serviceAccountSchema);
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
|
||||
export interface IServiceAccountKey {
|
||||
_id: Types.ObjectId;
|
||||
encryptedKey: string;
|
||||
nonce: string;
|
||||
sender: Types.ObjectId;
|
||||
serviceAccount: Types.ObjectId;
|
||||
workspace: Types.ObjectId;
|
||||
}
|
||||
|
||||
const serviceAccountKeySchema = new Schema<IServiceAccountKey>(
|
||||
{
|
||||
encryptedKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
nonce: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
sender: {
|
||||
type: Schema.Types.ObjectId,
|
||||
required: true,
|
||||
},
|
||||
serviceAccount: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "ServiceAccount",
|
||||
required: true,
|
||||
},
|
||||
workspace: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Workspace",
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
export const ServiceAccountKey = model<IServiceAccountKey>("ServiceAccountKey", serviceAccountKeySchema);
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Document, Schema, Types, model } from "mongoose";
|
||||
|
||||
export interface IServiceAccountOrganizationPermission extends Document {
|
||||
_id: Types.ObjectId;
|
||||
serviceAccount: Types.ObjectId;
|
||||
}
|
||||
|
||||
const serviceAccountOrganizationPermissionSchema = new Schema<IServiceAccountOrganizationPermission>(
|
||||
{
|
||||
serviceAccount: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "ServiceAccount",
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
export const ServiceAccountOrganizationPermission = model<IServiceAccountOrganizationPermission>("ServiceAccountOrganizationPermission", serviceAccountOrganizationPermissionSchema);
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Document, Schema, Types, model } from "mongoose";
|
||||
|
||||
export interface IServiceAccountWorkspacePermission extends Document {
|
||||
_id: Types.ObjectId;
|
||||
serviceAccount: Types.ObjectId;
|
||||
workspace: Types.ObjectId;
|
||||
environment: string;
|
||||
read: boolean;
|
||||
write: boolean;
|
||||
}
|
||||
|
||||
const serviceAccountWorkspacePermissionSchema = new Schema<IServiceAccountWorkspacePermission>(
|
||||
{
|
||||
serviceAccount: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "ServiceAccount",
|
||||
required: true,
|
||||
},
|
||||
workspace:{
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: "Workspace",
|
||||
required: true,
|
||||
},
|
||||
environment: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
read: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
write: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
export const ServiceAccountWorkspacePermission = model<IServiceAccountWorkspacePermission>("ServiceAccountWorkspacePermission", serviceAccountWorkspacePermissionSchema);
|
||||
@@ -4,7 +4,6 @@ import membership from "./membership";
|
||||
import organizations from "./organizations";
|
||||
import secret from "./secret"; // deprecated
|
||||
import secrets from "./secrets";
|
||||
import serviceAccounts from "./serviceAccounts";
|
||||
import serviceTokenData from "./serviceTokenData";
|
||||
import signup from "./signup";
|
||||
import tags from "./tags";
|
||||
@@ -20,7 +19,6 @@ export {
|
||||
secret,
|
||||
secrets,
|
||||
serviceTokenData,
|
||||
serviceAccounts,
|
||||
environment,
|
||||
tags,
|
||||
membership
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import {
|
||||
requireAuth,
|
||||
requireOrganizationAuth
|
||||
} from "../../middleware";
|
||||
import { ACCEPTED, ADMIN, AuthMode } from "../../variables";
|
||||
import { requireAuth } from "../../middleware";
|
||||
import { AuthMode } from "../../variables";
|
||||
import { organizationsController } from "../../controllers/v2";
|
||||
|
||||
// TODO: /POST to create membership
|
||||
@@ -41,19 +38,6 @@ router.get(
|
||||
organizationsController.getOrganizationWorkspaces
|
||||
);
|
||||
|
||||
router.get(
|
||||
// TODO endpoint: deprecate service accounts
|
||||
"/:organizationId/service-accounts",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT]
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [ADMIN],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
}),
|
||||
organizationsController.getOrganizationServiceAccounts
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
requireAuth({
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
|
||||
// TODO endpoint: deprecate all
|
||||
|
||||
// import {
|
||||
// requireAuth,
|
||||
// requireOrganizationAuth,
|
||||
// requireServiceAccountAuth,
|
||||
// requireServiceAccountWorkspacePermissionAuth,
|
||||
// requireWorkspaceAuth,
|
||||
// validateRequest,
|
||||
// } from "../../middleware";
|
||||
// import { body, param, query } from "express-validator";
|
||||
// import {
|
||||
// ACCEPTED,
|
||||
// ADMIN,
|
||||
// MEMBER,
|
||||
// OWNER,
|
||||
// AuthMode
|
||||
// } from "../../variables";
|
||||
// import { serviceAccountsController } from "../../controllers/v2";
|
||||
|
||||
// router.get( // TODO: check
|
||||
// "/me",
|
||||
// requireAuth({
|
||||
// acceptedAuthModes: [AUTH_MODE_SERVICE_ACCOUNT],
|
||||
// }),
|
||||
// serviceAccountsController.getCurrentServiceAccount
|
||||
// );
|
||||
|
||||
// router.get(
|
||||
// "/:serviceAccountId",
|
||||
// param("serviceAccountId").exists().isString().trim(),
|
||||
// requireAuth({
|
||||
// acceptedAuthModes: [AUTH_MODE_JWT],
|
||||
// }),
|
||||
// requireServiceAccountAuth({
|
||||
// acceptedRoles: [OWNER, ADMIN],
|
||||
// acceptedStatuses: [ACCEPTED],
|
||||
// }),
|
||||
// serviceAccountsController.getServiceAccountById
|
||||
// );
|
||||
|
||||
// router.post(
|
||||
// "/",
|
||||
// body("organizationId").exists().isString().trim(),
|
||||
// body("name").exists().isString().trim(),
|
||||
// body("publicKey").exists().isString().trim(),
|
||||
// body("expiresIn").isNumeric(), // measured in ms
|
||||
// validateRequest,
|
||||
// requireAuth({
|
||||
// acceptedAuthModes: [AUTH_MODE_JWT],
|
||||
// }),
|
||||
// requireOrganizationAuth({
|
||||
// acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
// acceptedStatuses: [ACCEPTED],
|
||||
// locationOrganizationId: "body",
|
||||
// }),
|
||||
// serviceAccountsController.createServiceAccount
|
||||
// );
|
||||
|
||||
// router.patch(
|
||||
// "/:serviceAccountId/name",
|
||||
// param("serviceAccountId").exists().isString().trim(),
|
||||
// validateRequest,
|
||||
// requireAuth({
|
||||
// acceptedAuthModes: [AUTH_MODE_JWT],
|
||||
// }),
|
||||
// requireServiceAccountAuth({
|
||||
// acceptedRoles: [OWNER, ADMIN],
|
||||
// acceptedStatuses: [ACCEPTED],
|
||||
// }),
|
||||
// serviceAccountsController.changeServiceAccountName
|
||||
// );
|
||||
|
||||
// router.delete(
|
||||
// "/:serviceAccountId",
|
||||
// param("serviceAccountId").exists().isString().trim(),
|
||||
// validateRequest,
|
||||
// requireAuth({
|
||||
// acceptedAuthModes: [AUTH_MODE_JWT],
|
||||
// }),
|
||||
// requireServiceAccountAuth({
|
||||
// acceptedRoles: [OWNER, ADMIN],
|
||||
// acceptedStatuses: [ACCEPTED],
|
||||
// }),
|
||||
// serviceAccountsController.deleteServiceAccount
|
||||
// );
|
||||
|
||||
// router.get(
|
||||
// "/:serviceAccountId/permissions/workspace",
|
||||
// param("serviceAccountId").exists().isString().trim(),
|
||||
// validateRequest,
|
||||
// requireAuth({
|
||||
// acceptedAuthModes: [AUTH_MODE_JWT],
|
||||
// }),
|
||||
// requireServiceAccountAuth({
|
||||
// acceptedRoles: [OWNER, ADMIN],
|
||||
// acceptedStatuses: [ACCEPTED],
|
||||
// }),
|
||||
// serviceAccountsController.getServiceAccountWorkspacePermissions
|
||||
// );
|
||||
|
||||
// router.post(
|
||||
// "/:serviceAccountId/permissions/workspace",
|
||||
// param("serviceAccountId").exists().isString().trim(),
|
||||
// body("workspaceId").exists().isString().notEmpty(),
|
||||
// body("environment").exists().isString().notEmpty(),
|
||||
// body("read").isBoolean().optional(),
|
||||
// body("write").isBoolean().optional(),
|
||||
// body("encryptedKey").exists().isString().notEmpty(),
|
||||
// body("nonce").exists().isString().notEmpty(),
|
||||
// validateRequest,
|
||||
// requireAuth({
|
||||
// acceptedAuthModes: [AUTH_MODE_JWT],
|
||||
// }),
|
||||
// requireServiceAccountAuth({
|
||||
// acceptedRoles: [OWNER, ADMIN],
|
||||
// acceptedStatuses: [ACCEPTED],
|
||||
// }),
|
||||
// requireWorkspaceAuth({
|
||||
// acceptedRoles: [ADMIN, MEMBER],
|
||||
// locationWorkspaceId: "body",
|
||||
// }),
|
||||
// serviceAccountsController.addServiceAccountWorkspacePermission
|
||||
// );
|
||||
|
||||
// router.delete(
|
||||
// "/:serviceAccountId/permissions/workspace/:serviceAccountWorkspacePermissionId",
|
||||
// param("serviceAccountId").exists().isString().trim(),
|
||||
// param("serviceAccountWorkspacePermissionId").exists().isString().trim(),
|
||||
// validateRequest,
|
||||
// requireAuth({
|
||||
// acceptedAuthModes: [AUTH_MODE_JWT],
|
||||
// }),
|
||||
// requireServiceAccountAuth({
|
||||
// acceptedRoles: [OWNER, ADMIN],
|
||||
// acceptedStatuses: [ACCEPTED],
|
||||
// }),
|
||||
// requireServiceAccountWorkspacePermissionAuth({
|
||||
// acceptedRoles: [OWNER, ADMIN],
|
||||
// acceptedStatuses: [ACCEPTED],
|
||||
// }),
|
||||
// serviceAccountsController.deleteServiceAccountWorkspacePermission
|
||||
// );
|
||||
|
||||
// router.get(
|
||||
// "/:serviceAccountId/keys",
|
||||
// query("workspaceId").optional().isString(),
|
||||
// requireAuth({
|
||||
// acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT],
|
||||
// }),
|
||||
// requireServiceAccountAuth({
|
||||
// acceptedRoles: [OWNER, ADMIN],
|
||||
// acceptedStatuses: [ACCEPTED],
|
||||
// }),
|
||||
// serviceAccountsController.getServiceAccountKeys
|
||||
// );
|
||||
|
||||
export default router;
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
getTelemetryEnabled,
|
||||
} from "../config";
|
||||
import {
|
||||
ServiceAccount,
|
||||
ServiceTokenData,
|
||||
User,
|
||||
} from "../models";
|
||||
@@ -56,16 +55,11 @@ class Telemetry {
|
||||
let distinctId = "";
|
||||
if (authData.authPayload instanceof User) {
|
||||
distinctId = authData.authPayload.email;
|
||||
} else if (authData.authPayload instanceof ServiceAccount) {
|
||||
distinctId = `sa.${authData.authPayload._id.toString()}`;
|
||||
} else if (authData.authPayload instanceof ServiceTokenData) {
|
||||
|
||||
if (authData.authPayload.user) {
|
||||
const user = await User.findById(authData.authPayload.user, "email");
|
||||
if (!user) throw AccountNotFoundError();
|
||||
distinctId = user.email;
|
||||
} else if (authData.authPayload.serviceAccount) {
|
||||
distinctId = distinctId = `sa.${authData.authPayload.serviceAccount.toString()}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
-1
@@ -29,7 +29,6 @@ declare global {
|
||||
secrets: any;
|
||||
secretSnapshot: any;
|
||||
serviceToken: any;
|
||||
serviceAccount: any;
|
||||
accessToken: any;
|
||||
accessId: any;
|
||||
serviceTokenData: any;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { AuthData } from "../../../interfaces/middleware";
|
||||
import {
|
||||
ServiceAccount,
|
||||
ServiceTokenData,
|
||||
ServiceTokenDataV3,
|
||||
User
|
||||
@@ -16,10 +15,6 @@ import {
|
||||
return { userId: authData.authPayload._id };
|
||||
}
|
||||
|
||||
if (authData.authPayload instanceof ServiceAccount) {
|
||||
return { serviceAccountId: authData.authPayload._id };
|
||||
}
|
||||
|
||||
if (authData.authPayload instanceof ServiceTokenData) {
|
||||
return { serviceTokenDataId: authData.authPayload._id };
|
||||
}
|
||||
@@ -39,10 +34,6 @@ export const getAuthDataPayloadUserObj = (authData: AuthData) => {
|
||||
return { user: authData.authPayload._id };
|
||||
}
|
||||
|
||||
if (authData.authPayload instanceof ServiceAccount) {
|
||||
return { user: authData.authPayload.user };
|
||||
}
|
||||
|
||||
if (authData.authPayload instanceof ServiceTokenData) {
|
||||
return { user: authData.authPayload.user };
|
||||
}
|
||||
|
||||
@@ -182,16 +182,6 @@ export const SecretSnapshotNotFoundError = (error?: Partial<RequestErrorContext>
|
||||
stack: error?.stack,
|
||||
});
|
||||
|
||||
//* ----->[ACTION ERRORS]<-----
|
||||
export const ActionNotFoundError = (error?: Partial<RequestErrorContext>) => new RequestError({
|
||||
logLevel: error?.logLevel ?? LogLevel.ERROR,
|
||||
statusCode: error?.statusCode ?? 404,
|
||||
type: error?.type ?? "action_not_found_error",
|
||||
message: error?.message ?? "The requested action was not found",
|
||||
context: error?.context,
|
||||
stack: error?.stack,
|
||||
});
|
||||
|
||||
//* ----->[SERVICE TOKEN DATA ERRORS]<-----
|
||||
export const ServiceTokenDataNotFoundError = (error?: Partial<RequestErrorContext>) => new RequestError({
|
||||
logLevel: error?.logLevel ?? LogLevel.ERROR,
|
||||
@@ -212,25 +202,6 @@ export const APIKeyDataNotFoundError = (error?: Partial<RequestErrorContext>) =>
|
||||
stack: error?.stack,
|
||||
});
|
||||
|
||||
//* ----->[SERVICE_ACCOUNT ERRORS]<-----
|
||||
export const ServiceAccountNotFoundError = (error?: Partial<RequestErrorContext>) => new RequestError({
|
||||
logLevel: error?.logLevel ?? LogLevel.ERROR,
|
||||
statusCode: error?.statusCode ?? 404,
|
||||
type: error?.type ?? "service_account_not_found_error",
|
||||
message: error?.message ?? "The requested service account was not found",
|
||||
context: error?.context,
|
||||
stack: error?.stack,
|
||||
});
|
||||
|
||||
export const ServiceAccountKeyNotFoundError = (error?: Partial<RequestErrorContext>) => new RequestError({
|
||||
logLevel: error?.logLevel ?? LogLevel.ERROR,
|
||||
statusCode: error?.statusCode ?? 404,
|
||||
type: error?.type ?? "service_account_key_not_found_error",
|
||||
message: error?.message ?? "The requested service account key was not found",
|
||||
context: error?.context,
|
||||
stack: error?.stack,
|
||||
})
|
||||
|
||||
export const BotNotFoundError = (error?: Partial<RequestErrorContext>) => new RequestError({
|
||||
logLevel: error?.logLevel ?? LogLevel.ERROR,
|
||||
statusCode: error?.statusCode ?? 404,
|
||||
|
||||
@@ -7,7 +7,6 @@ export * from "./membership";
|
||||
export * from "./membershipOrg";
|
||||
export * from "./organization";
|
||||
export * from "./secrets";
|
||||
export * from "./serviceAccount";
|
||||
export * from "./serviceTokenData";
|
||||
export * from "./serviceTokenDataV3";
|
||||
export * from "./apiKeyDataV3";
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
import _ from "lodash";
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
IOrganization,
|
||||
ISecret,
|
||||
IServiceAccount,
|
||||
IUser,
|
||||
ServiceAccount,
|
||||
ServiceAccountWorkspacePermission,
|
||||
} from "../models";
|
||||
import { validateUserClientForServiceAccount } from "./user";
|
||||
import {
|
||||
BadRequestError,
|
||||
ServiceAccountNotFoundError,
|
||||
UnauthorizedRequestError,
|
||||
} from "../utils/errors";
|
||||
import {
|
||||
PERMISSION_READ_SECRETS,
|
||||
PERMISSION_WRITE_SECRETS,
|
||||
} from "../variables";
|
||||
import { AuthData } from "../interfaces/middleware";
|
||||
import { ActorType } from "../ee/models";
|
||||
|
||||
export const validateClientForServiceAccount = async ({
|
||||
authData,
|
||||
serviceAccountId,
|
||||
requiredPermissions,
|
||||
}: {
|
||||
authData: AuthData;
|
||||
serviceAccountId: Types.ObjectId;
|
||||
requiredPermissions?: string[];
|
||||
}) => {
|
||||
const serviceAccount = await ServiceAccount.findById(serviceAccountId);
|
||||
|
||||
if (!serviceAccount) {
|
||||
throw ServiceAccountNotFoundError({
|
||||
message: "Failed to find service account",
|
||||
});
|
||||
}
|
||||
|
||||
switch (authData.actor.type) {
|
||||
case ActorType.USER:
|
||||
await validateUserClientForServiceAccount({
|
||||
user: authData.authPayload as IUser,
|
||||
serviceAccount,
|
||||
requiredPermissions,
|
||||
});
|
||||
|
||||
return serviceAccount;
|
||||
case ActorType.SERVICE:
|
||||
throw UnauthorizedRequestError({
|
||||
message: "Failed service token authorization for service account resource",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that service account (client) can access workspace
|
||||
* with id [workspaceId] and its environment [environment] with required permissions
|
||||
* [requiredPermissions]
|
||||
* @param {Object} obj
|
||||
* @param {ServiceAccount} obj.serviceAccount - service account client
|
||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace to validate against
|
||||
* @param {String} environment - (optional) environment in workspace to validate against
|
||||
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
||||
*/
|
||||
export const validateServiceAccountClientForWorkspace = async ({
|
||||
serviceAccount,
|
||||
workspaceId,
|
||||
environment,
|
||||
requiredPermissions,
|
||||
}: {
|
||||
serviceAccount: IServiceAccount;
|
||||
workspaceId: Types.ObjectId;
|
||||
environment?: string;
|
||||
requiredPermissions?: string[];
|
||||
}) => {
|
||||
if (environment) {
|
||||
// case: environment specified ->
|
||||
// evaluate service account authorization for workspace
|
||||
// in the context of a specific environment [environment]
|
||||
const permission = await ServiceAccountWorkspacePermission.findOne({
|
||||
serviceAccount,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment,
|
||||
});
|
||||
|
||||
if (!permission) throw UnauthorizedRequestError({
|
||||
message: "Failed service account authorization for the given workspace environment",
|
||||
});
|
||||
|
||||
let runningIsDisallowed = false;
|
||||
requiredPermissions?.forEach((requiredPermission: string) => {
|
||||
switch (requiredPermission) {
|
||||
case PERMISSION_READ_SECRETS:
|
||||
if (!permission.read) runningIsDisallowed = true;
|
||||
break;
|
||||
case PERMISSION_WRITE_SECRETS:
|
||||
if (!permission.write) runningIsDisallowed = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (runningIsDisallowed) {
|
||||
throw UnauthorizedRequestError({
|
||||
message: `Failed permissions authorization for workspace environment action : ${requiredPermission}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// case: no environment specified ->
|
||||
// evaluate service account authorization for workspace
|
||||
// without need of environment [environment]
|
||||
|
||||
const permission = await ServiceAccountWorkspacePermission.findOne({
|
||||
serviceAccount,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
});
|
||||
|
||||
if (!permission) throw UnauthorizedRequestError({
|
||||
message: "Failed service account authorization for the given workspace",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that service account (client) can access secrets
|
||||
* with required permissions [requiredPermissions]
|
||||
* @param {Object} obj
|
||||
* @param {ServiceAccount} obj.serviceAccount - service account client
|
||||
* @param {Secret[]} secrets - secrets to validate against
|
||||
* @param {string[]} requiredPermissions - required permissions as part of the endpoint
|
||||
*/
|
||||
export const validateServiceAccountClientForSecrets = async ({
|
||||
serviceAccount,
|
||||
secrets,
|
||||
requiredPermissions,
|
||||
}: {
|
||||
serviceAccount: IServiceAccount;
|
||||
secrets: ISecret[];
|
||||
requiredPermissions?: string[];
|
||||
}) => {
|
||||
|
||||
const permissions = await ServiceAccountWorkspacePermission.find({
|
||||
serviceAccount: serviceAccount._id,
|
||||
});
|
||||
|
||||
const permissionsObj = _.keyBy(permissions, (p) => {
|
||||
return `${p.workspace.toString()}-${p.environment}`
|
||||
});
|
||||
|
||||
secrets.forEach((secret: ISecret) => {
|
||||
const permission = permissionsObj[`${secret.workspace.toString()}-${secret.environment}`];
|
||||
|
||||
if (!permission) throw BadRequestError({
|
||||
message: "Failed to find any permission for the secret workspace and environment",
|
||||
});
|
||||
|
||||
requiredPermissions?.forEach((requiredPermission: string) => {
|
||||
let runningIsDisallowed = false;
|
||||
requiredPermissions?.forEach((requiredPermission: string) => {
|
||||
switch (requiredPermission) {
|
||||
case PERMISSION_READ_SECRETS:
|
||||
if (!permission.read) runningIsDisallowed = true;
|
||||
break;
|
||||
case PERMISSION_WRITE_SECRETS:
|
||||
if (!permission.write) runningIsDisallowed = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (runningIsDisallowed) {
|
||||
throw UnauthorizedRequestError({
|
||||
message: `Failed permissions authorization for workspace environment action : ${requiredPermission}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that service account (client) can access target service
|
||||
* account [serviceAccount] with required permissions [requiredPermissions]
|
||||
* @param {Object} obj
|
||||
* @param {SerivceAccount} obj.serviceAccount - service account client
|
||||
* @param {ServiceAccount} targetServiceAccount - target service account to validate against
|
||||
* @param {string[]} requiredPermissions - required permissions as part of the endpoint
|
||||
*/
|
||||
export const validateServiceAccountClientForServiceAccount = ({
|
||||
serviceAccount,
|
||||
targetServiceAccount,
|
||||
requiredPermissions,
|
||||
}: {
|
||||
serviceAccount: IServiceAccount;
|
||||
targetServiceAccount: IServiceAccount;
|
||||
requiredPermissions?: string[];
|
||||
}) => {
|
||||
if (!serviceAccount.organization.equals(targetServiceAccount.organization)) {
|
||||
throw UnauthorizedRequestError({
|
||||
message: "Failed service account authorization for the given service account",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that service account (client) can access organization [organization]
|
||||
* @param {Object} obj
|
||||
* @param {User} obj.user - service account client
|
||||
* @param {Organization} obj.organization - organization to validate against
|
||||
*/
|
||||
export const validateServiceAccountClientForOrganization = async ({
|
||||
serviceAccount,
|
||||
organization,
|
||||
}: {
|
||||
serviceAccount: IServiceAccount;
|
||||
organization: IOrganization;
|
||||
}) => {
|
||||
if (!serviceAccount.organization.equals(organization._id)) {
|
||||
throw UnauthorizedRequestError({
|
||||
message: "Failed service account authorization for the given organization",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { Types } from "mongoose";
|
||||
import { IOrganization, ISecret, IServiceAccount, IUser, Membership } from "../models";
|
||||
import { IOrganization, ISecret, IUser, Membership } from "../models";
|
||||
import { validateMembership } from "../helpers/membership";
|
||||
import _ from "lodash";
|
||||
import { BadRequestError, UnauthorizedRequestError, ValidationError } from "../utils/errors";
|
||||
@@ -172,35 +172,6 @@ export const validateUserClientForSecrets = async ({
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate that user (client) can access service account [serviceAccount]
|
||||
* with required permissions [requiredPermissions]
|
||||
* @param {Object} obj
|
||||
* @param {User} obj.user - user client
|
||||
* @param {ServiceAccount} obj.serviceAccount - service account to validate against
|
||||
* @param {String[]} requiredPermissions - required permissions as part of the endpoint
|
||||
*/
|
||||
export const validateUserClientForServiceAccount = async ({
|
||||
user,
|
||||
serviceAccount,
|
||||
requiredPermissions
|
||||
}: {
|
||||
user: IUser;
|
||||
serviceAccount: IServiceAccount;
|
||||
requiredPermissions?: string[];
|
||||
}) => {
|
||||
if (!serviceAccount.user.equals(user._id)) {
|
||||
// case: user who created service account is not the
|
||||
// same user that is on the request
|
||||
await validateMembershipOrg({
|
||||
userId: user._id,
|
||||
organizationId: serviceAccount.organization,
|
||||
acceptedRoles: [],
|
||||
acceptedStatuses: []
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate that user (client) can access organization [organization]
|
||||
* @param {Object} obj
|
||||
|
||||
Reference in New Issue
Block a user