mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 14:34:09 +00:00
Merge pull request #1152 from akhilmhdh/fix/key-rogue
fix: changed 2 fold operation of member workspace to one api call
This commit is contained in:
@@ -9,17 +9,19 @@ 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";
|
||||
|
||||
export {
|
||||
authController,
|
||||
signupController,
|
||||
usersController,
|
||||
organizationsController,
|
||||
workspaceController,
|
||||
serviceTokenDataController,
|
||||
secretController,
|
||||
secretsController,
|
||||
serviceAccountsController,
|
||||
environmentController,
|
||||
tagController,
|
||||
}
|
||||
authController,
|
||||
signupController,
|
||||
usersController,
|
||||
organizationsController,
|
||||
workspaceController,
|
||||
serviceTokenDataController,
|
||||
secretController,
|
||||
secretsController,
|
||||
serviceAccountsController,
|
||||
environmentController,
|
||||
tagController,
|
||||
membershipController
|
||||
};
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
import { getSiteURL } from "../../config";
|
||||
import { EventType } from "../../ee/models";
|
||||
import { EEAuditLogService } from "../../ee/services";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionSub,
|
||||
getUserProjectPermissions
|
||||
} from "../../ee/services/ProjectRoleService";
|
||||
import { sendMail } from "../../helpers";
|
||||
import { validateRequest } from "../../helpers/validation";
|
||||
import { IUser, Key, Membership, MembershipOrg, Workspace } from "../../models";
|
||||
import { BadRequestError } from "../../utils/errors";
|
||||
import * as reqValidator from "../../validation/membership";
|
||||
import { ACCEPTED, MEMBER } from "../../variables";
|
||||
|
||||
export const addUserToWorkspace = async (req: Request, res: Response) => {
|
||||
const {
|
||||
params: { workspaceId },
|
||||
body: { members }
|
||||
} = await validateRequest(reqValidator.AddUserToWorkspaceV2, req);
|
||||
// check workspace
|
||||
const workspace = await Workspace.findById(workspaceId);
|
||||
if (!workspace) throw new Error("Failed to find workspace");
|
||||
|
||||
// check permission
|
||||
const { permission } = await getUserProjectPermissions(req.user._id, workspaceId);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.Member
|
||||
);
|
||||
|
||||
// validate members are part of the organization
|
||||
const orgMembers = await MembershipOrg.find({
|
||||
status: ACCEPTED,
|
||||
_id: { $in: members.map(({ orgMembershipId }) => orgMembershipId) },
|
||||
organization: workspace.organization
|
||||
})
|
||||
.populate<{ user: IUser }>("user")
|
||||
.select({ _id: 1, user: 1 })
|
||||
.lean();
|
||||
if (orgMembers.length !== members.length)
|
||||
throw BadRequestError({ message: "Org member not found" });
|
||||
|
||||
const existingMember = await Membership.find({
|
||||
workspace: workspaceId,
|
||||
user: { $in: orgMembers.map(({ user }) => user) }
|
||||
});
|
||||
if (existingMember?.length)
|
||||
throw BadRequestError({ message: "Some users are already part of workspace" });
|
||||
|
||||
await Membership.insertMany(
|
||||
orgMembers.map(({ user }) => ({ user: user._id, workspace: workspaceId, role: MEMBER }))
|
||||
);
|
||||
|
||||
const encKeyGroupedByOrgMemberId = members.reduce<Record<string, (typeof members)[number]>>(
|
||||
(prev, curr) => ({ ...prev, [curr.orgMembershipId]: curr }),
|
||||
{}
|
||||
);
|
||||
await Key.insertMany(
|
||||
orgMembers.map(({ user, _id: id }) => ({
|
||||
encryptedKey: encKeyGroupedByOrgMemberId[id.toString()].workspaceEncryptedKey,
|
||||
nonce: encKeyGroupedByOrgMemberId[id.toString()].workspaceEncryptedNonce,
|
||||
sender: req.user._id,
|
||||
receiver: user._id,
|
||||
workspace: workspaceId
|
||||
}))
|
||||
);
|
||||
|
||||
await sendMail({
|
||||
template: "workspaceInvitation.handlebars",
|
||||
subjectLine: "Infisical workspace invitation",
|
||||
recipients: orgMembers.map(({ user }) => user.email),
|
||||
substitutions: {
|
||||
inviterFirstName: req.user.firstName,
|
||||
inviterEmail: req.user.email,
|
||||
workspaceName: workspace.name,
|
||||
callback_url: (await getSiteURL()) + "/login"
|
||||
}
|
||||
});
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
req.authData,
|
||||
{
|
||||
type: EventType.ADD_BATCH_WORKSPACE_MEMBER,
|
||||
metadata: orgMembers.map(({ user }) => ({
|
||||
userId: user._id.toString(),
|
||||
email: user.email
|
||||
}))
|
||||
},
|
||||
{
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
success: true,
|
||||
data: orgMembers
|
||||
});
|
||||
};
|
||||
@@ -39,6 +39,7 @@ export enum EventType {
|
||||
UPDATE_ENVIRONMENT = "update-environment",
|
||||
DELETE_ENVIRONMENT = "delete-environment",
|
||||
ADD_WORKSPACE_MEMBER = "add-workspace-member",
|
||||
ADD_BATCH_WORKSPACE_MEMBER = "add-workspace-members",
|
||||
REMOVE_WORKSPACE_MEMBER = "remove-workspace-member",
|
||||
CREATE_FOLDER = "create-folder",
|
||||
UPDATE_FOLDER = "update-folder",
|
||||
|
||||
@@ -287,6 +287,14 @@ interface AddWorkspaceMemberEvent {
|
||||
};
|
||||
}
|
||||
|
||||
interface AddBatchWorkspaceMemberEvent {
|
||||
type: EventType.ADD_BATCH_WORKSPACE_MEMBER;
|
||||
metadata: Array<{
|
||||
userId: string;
|
||||
email: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface RemoveWorkspaceMemberEvent {
|
||||
type: EventType.REMOVE_WORKSPACE_MEMBER;
|
||||
metadata: {
|
||||
@@ -494,6 +502,7 @@ export type Event =
|
||||
| UpdateEnvironmentEvent
|
||||
| DeleteEnvironmentEvent
|
||||
| AddWorkspaceMemberEvent
|
||||
| AddBatchWorkspaceMemberEvent
|
||||
| RemoveWorkspaceMemberEvent
|
||||
| CreateFolderEvent
|
||||
| UpdateFolderEvent
|
||||
|
||||
+11
-7
@@ -67,7 +67,8 @@ import {
|
||||
signup as v2SignupRouter,
|
||||
tags as v2TagsRouter,
|
||||
users as v2UsersRouter,
|
||||
workspace as v2WorkspaceRouter
|
||||
workspace as v2WorkspaceRouter,
|
||||
membership as v2MembershipController
|
||||
} from "./routes/v2";
|
||||
import {
|
||||
auth as v3AuthRouter,
|
||||
@@ -87,7 +88,7 @@ import {
|
||||
getSecretScanningPrivateKey,
|
||||
getSecretScanningWebhookProxy,
|
||||
getSecretScanningWebhookSecret,
|
||||
getSiteURL,
|
||||
getSiteURL
|
||||
} from "./config";
|
||||
import { setup } from "./utils/setup";
|
||||
import { syncSecretsToThirdPartyServices } from "./queues/integrations/syncSecretsToThirdPartyServices";
|
||||
@@ -106,11 +107,13 @@ const main = async () => {
|
||||
|
||||
const app = express();
|
||||
app.enable("trust proxy");
|
||||
|
||||
app.use(httpLogger({
|
||||
logger,
|
||||
autoLogging: false
|
||||
}));
|
||||
|
||||
app.use(
|
||||
httpLogger({
|
||||
logger,
|
||||
autoLogging: false
|
||||
})
|
||||
);
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
@@ -230,6 +233,7 @@ const main = async () => {
|
||||
app.use("/api/v2/auth", v2AuthRouter);
|
||||
app.use("/api/v2/users", v2UsersRouter);
|
||||
app.use("/api/v2/organizations", v2OrganizationsRouter);
|
||||
app.use("/api/v2/workspace", v2MembershipController);
|
||||
app.use("/api/v2/workspace", v2EnvironmentRouter);
|
||||
app.use("/api/v2/workspace", v2TagsRouter);
|
||||
app.use("/api/v2/workspace", v2WorkspaceRouter);
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
import auth from "./auth";
|
||||
import signup from "./signup";
|
||||
import users from "./users";
|
||||
import environment from "./environment";
|
||||
import membership from "./membership";
|
||||
import organizations from "./organizations";
|
||||
import workspace from "./workspace";
|
||||
import secret from "./secret"; // deprecated
|
||||
import secrets from "./secrets";
|
||||
import serviceTokenData from "./serviceTokenData";
|
||||
import serviceAccounts from "./serviceAccounts";
|
||||
import environment from "./environment"
|
||||
import tags from "./tags"
|
||||
import serviceTokenData from "./serviceTokenData";
|
||||
import signup from "./signup";
|
||||
import tags from "./tags";
|
||||
import users from "./users";
|
||||
import workspace from "./workspace";
|
||||
|
||||
export {
|
||||
auth,
|
||||
signup,
|
||||
users,
|
||||
organizations,
|
||||
workspace,
|
||||
secret,
|
||||
secrets,
|
||||
serviceTokenData,
|
||||
serviceAccounts,
|
||||
environment,
|
||||
tags,
|
||||
}
|
||||
auth,
|
||||
signup,
|
||||
users,
|
||||
organizations,
|
||||
workspace,
|
||||
secret,
|
||||
secrets,
|
||||
serviceTokenData,
|
||||
serviceAccounts,
|
||||
environment,
|
||||
tags,
|
||||
membership
|
||||
};
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import { membershipController } from "../../controllers/v2";
|
||||
import { requireAuth } from "../../middleware";
|
||||
import { AuthMode } from "../../variables";
|
||||
|
||||
router.post(
|
||||
"/:workspaceId/memberships",
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY]
|
||||
}),
|
||||
membershipController.addUserToWorkspace
|
||||
);
|
||||
|
||||
export default router;
|
||||
@@ -78,3 +78,19 @@ export const DenyMembershipPermissionV1 = z.object({
|
||||
permissions: z.object({}).array()
|
||||
})
|
||||
});
|
||||
|
||||
export const AddUserToWorkspaceV2 = z.object({
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
members: z
|
||||
.object({
|
||||
orgMembershipId: z.string().trim(),
|
||||
workspaceEncryptedKey: z.string().trim(),
|
||||
workspaceEncryptedNonce: z.string().trim()
|
||||
})
|
||||
.array()
|
||||
.min(1)
|
||||
})
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user