mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Ghost user!
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { registerLoginRouter } from "./login-router";
|
import { registerLoginRouter } from "./login-router";
|
||||||
|
import { registerProjectRouter } from "./project-router";
|
||||||
import { registerSecretBlindIndexRouter } from "./secret-blind-index-router";
|
import { registerSecretBlindIndexRouter } from "./secret-blind-index-router";
|
||||||
import { registerSecretRouter } from "./secret-router";
|
import { registerSecretRouter } from "./secret-router";
|
||||||
import { registerSignupRouter } from "./signup-router";
|
import { registerSignupRouter } from "./signup-router";
|
||||||
@@ -10,4 +11,5 @@ export const registerV3Routes = async (server: FastifyZodProvider) => {
|
|||||||
await server.register(registerUserRouter, { prefix: "/users" });
|
await server.register(registerUserRouter, { prefix: "/users" });
|
||||||
await server.register(registerSecretRouter, { prefix: "/secrets" });
|
await server.register(registerSecretRouter, { prefix: "/secrets" });
|
||||||
await server.register(registerSecretBlindIndexRouter, { prefix: "/workspaces" });
|
await server.register(registerSecretBlindIndexRouter, { prefix: "/workspaces" });
|
||||||
|
await server.register(registerProjectRouter, { prefix: "/projects" });
|
||||||
};
|
};
|
||||||
|
|||||||
126
backend/src/server/routes/v3/project-router.ts
Normal file
126
backend/src/server/routes/v3/project-router.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import crypto from "crypto";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { ProjectMembershipRole, ProjectsSchema } from "@app/db/schemas";
|
||||||
|
import { encryptAsymmetric } from "@app/lib/crypto";
|
||||||
|
import { createWsMembers } from "@app/lib/project";
|
||||||
|
import { authRateLimit } from "@app/server/config/rateLimiter";
|
||||||
|
|
||||||
|
const projectWithEnv = ProjectsSchema.merge(
|
||||||
|
z.object({
|
||||||
|
_id: z.string(),
|
||||||
|
environments: z.object({ name: z.string(), slug: z.string(), id: z.string() }).array()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||||
|
/* Create new project */
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: authRateLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
projectName: z.string().trim(),
|
||||||
|
inviteAllOrgMembers: z.boolean(),
|
||||||
|
organizationId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
workspace: projectWithEnv
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
// 1. create the ghost user and add it to the org as admin
|
||||||
|
const ghost = await server.services.org.addGhostUser(req.body.organizationId);
|
||||||
|
|
||||||
|
// 2. create the workspace
|
||||||
|
const workspace = await server.services.project.createProject({
|
||||||
|
actorId: ghost.user.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
orgId: req.body.organizationId,
|
||||||
|
workspaceName: req.body.projectName
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. create a random key that we'll use as the project key
|
||||||
|
const randomBytes = crypto.randomBytes(16).toString("hex");
|
||||||
|
|
||||||
|
const ghostPrivateKey = ghost.keys.plainPrivateKey;
|
||||||
|
|
||||||
|
const { ciphertext: encryptedProjectKey, nonce: encryptedProjectKeyIv } = encryptAsymmetric(
|
||||||
|
randomBytes,
|
||||||
|
ghost.keys.publicKey,
|
||||||
|
ghostPrivateKey
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. create workspace keys for the ghost user
|
||||||
|
await server.services.projectKey.uploadProjectKeys({
|
||||||
|
projectId: workspace.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: ghost.user.id,
|
||||||
|
nonce: encryptedProjectKeyIv,
|
||||||
|
receiverId: ghost.user.id,
|
||||||
|
encryptedKey: encryptedProjectKey
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. create a project bot
|
||||||
|
const bot = await server.services.projectBot.findBotByProjectId({
|
||||||
|
actorId: ghost.user.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
projectId: workspace.id
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5. activate the bot
|
||||||
|
await server.services.projectBot.setBotActiveState({
|
||||||
|
botKey: {
|
||||||
|
encryptedKey: encryptedProjectKey,
|
||||||
|
nonce: encryptedProjectKeyIv
|
||||||
|
},
|
||||||
|
actorId: ghost.user.id,
|
||||||
|
isActive: true,
|
||||||
|
actor: req.permission.type,
|
||||||
|
botId: bot.id
|
||||||
|
});
|
||||||
|
|
||||||
|
// 6. get the current user & org membership
|
||||||
|
const user = await server.services.user.getMe(req.permission.id);
|
||||||
|
const userOrgMembership = await server.services.permission.getUserOrgPermission(user.id, req.body.organizationId);
|
||||||
|
|
||||||
|
// 7. Get the latest key from the ghost!
|
||||||
|
const latestKey = await server.services.projectKey.getLatestProjectKey({
|
||||||
|
actorId: ghost.user.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
projectId: workspace.id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!latestKey) throw new Error("Failed to get latest key");
|
||||||
|
|
||||||
|
// 8. Create workspace members for the current user
|
||||||
|
|
||||||
|
const projectAdmin = await createWsMembers({
|
||||||
|
decryptKey: latestKey,
|
||||||
|
members: [
|
||||||
|
{
|
||||||
|
userPublicKey: user.publicKey,
|
||||||
|
orgMembershipId: userOrgMembership.membership.id,
|
||||||
|
projectMembershipRole: ProjectMembershipRole.Admin // <-- Make the first user an admin
|
||||||
|
}
|
||||||
|
],
|
||||||
|
userPrivateKey: ghostPrivateKey
|
||||||
|
});
|
||||||
|
|
||||||
|
// 9. Add the current user to the workspace
|
||||||
|
await server.services.projectMembership.addUsersToProject({
|
||||||
|
projectId: workspace.id,
|
||||||
|
actorId: ghost.user.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
members: projectAdmin
|
||||||
|
});
|
||||||
|
|
||||||
|
return { workspace };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
@@ -43,7 +44,7 @@ export const projectKeyServiceFactory = ({
|
|||||||
name: "Upload project keys"
|
name: "Upload project keys"
|
||||||
});
|
});
|
||||||
|
|
||||||
await projectKeyDAL.create({ projectId, receiverId, encryptedKey, nonce, senderId: actorId });
|
await projectKeyDAL.create({ projectId, receiverId, encryptedKey, nonce, senderId: actorId }, tx);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLatestProjectKey = async ({ actorId, projectId, actor, actorOrgId }: TGetLatestProjectKeyDTO) => {
|
const getLatestProjectKey = async ({ actorId, projectId, actor, actorOrgId }: TGetLatestProjectKeyDTO) => {
|
||||||
|
|||||||
@@ -509,6 +509,7 @@ const OrganizationPage = withPermission(
|
|||||||
projectName: name
|
projectName: name
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
if (addMembers) {
|
if (addMembers) {
|
||||||
const orgUsers = await fetchOrgUsers(currentOrg);
|
const orgUsers = await fetchOrgUsers(currentOrg);
|
||||||
|
|
||||||
@@ -523,6 +524,24 @@ const OrganizationPage = withPermission(
|
|||||||
// eslint-disable-next-line no-promise-executor-return -- We do this because the function returns too fast, which sometimes causes an error when the user is redirected.
|
// eslint-disable-next-line no-promise-executor-return -- We do this because the function returns too fast, which sometimes causes an error when the user is redirected.
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
||||||
|
|
||||||
|
const decryptKey = await fetchUserWsKey(newWorkspaceId);
|
||||||
|
|
||||||
|
await addWsUser.mutateAsync({
|
||||||
|
workspaceId: newWorkspaceId,
|
||||||
|
decryptKey,
|
||||||
|
userPrivateKey: PRIVATE_KEY,
|
||||||
|
members: orgUsers
|
||||||
|
.filter(
|
||||||
|
({ status, user: orgUser }) => status === "accepted" && user.email !== orgUser.email
|
||||||
|
)
|
||||||
|
.map(({ user: orgUser, id: orgMembershipId }) => ({
|
||||||
|
userPublicKey: orgUser.publicKey,
|
||||||
|
orgMembershipId
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
createNotification({ text: "Workspace created", type: "success" });
|
||||||
handlePopUpClose("addNewWs");
|
handlePopUpClose("addNewWs");
|
||||||
createNotification({ text: "Workspace created", type: "success" });
|
createNotification({ text: "Workspace created", type: "success" });
|
||||||
router.push(`/project/${newProjectId}/secrets/overview`);
|
router.push(`/project/${newProjectId}/secrets/overview`);
|
||||||
|
|||||||
Reference in New Issue
Block a user