mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 06:26:27 +00:00
fix: seeding with a ghost user
This commit is contained in:
@@ -2,11 +2,27 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import { initEnvConfig } from "@app/lib/config/env";
|
import { initEnvConfig } from "@app/lib/config/env";
|
||||||
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
||||||
|
import { generateUserSrpKeys } from "@app/lib/crypto/srp";
|
||||||
import { initLogger, logger } from "@app/lib/logger";
|
import { initLogger, logger } from "@app/lib/logger";
|
||||||
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
import { AuthMethod } from "@app/services/auth/auth-type";
|
||||||
|
import { assignWorkspaceKeysToMembers, createProjectKey } from "@app/services/project/project-fns";
|
||||||
|
import { projectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
|
import { projectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||||
|
import { projectUserMembershipRoleDALFactory } from "@app/services/project-membership/project-user-membership-role-dal";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
import { userDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
import { ProjectMembershipRole, ProjectType, SecretEncryptionAlgo, SecretKeyEncoding, TableName } from "../schemas";
|
import {
|
||||||
import { buildUserProjectKey, getUserPrivateKey, seedData1 } from "../seed-data";
|
OrgMembershipRole,
|
||||||
|
OrgMembershipStatus,
|
||||||
|
ProjectMembershipRole,
|
||||||
|
ProjectType,
|
||||||
|
SecretEncryptionAlgo,
|
||||||
|
SecretKeyEncoding,
|
||||||
|
TableName
|
||||||
|
} from "../schemas";
|
||||||
|
import { seedData1 } from "../seed-data";
|
||||||
|
|
||||||
export const DEFAULT_PROJECT_ENVS = [
|
export const DEFAULT_PROJECT_ENVS = [
|
||||||
{ name: "Development", slug: "dev" },
|
{ name: "Development", slug: "dev" },
|
||||||
@@ -14,6 +30,148 @@ export const DEFAULT_PROJECT_ENVS = [
|
|||||||
{ name: "Production", slug: "prod" }
|
{ name: "Production", slug: "prod" }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const createGhostUser = async (
|
||||||
|
orgId: string,
|
||||||
|
projectId: string,
|
||||||
|
userId: string,
|
||||||
|
userOrgMembershipId: string,
|
||||||
|
knex: Knex
|
||||||
|
) => {
|
||||||
|
const projectKeyDAL = projectKeyDALFactory(knex);
|
||||||
|
const userDAL = userDALFactory(knex);
|
||||||
|
const projectMembershipDAL = projectMembershipDALFactory(knex);
|
||||||
|
const projectUserMembershipRoleDAL = projectUserMembershipRoleDALFactory(knex);
|
||||||
|
|
||||||
|
const email = `sudo-${alphaNumericNanoId(16)}-${orgId}@infisical.com`; // We add a nanoid because the email is unique. And we have to create a new ghost user each time, so we can have access to the private key.
|
||||||
|
|
||||||
|
const password = crypto.randomBytes(128).toString("hex");
|
||||||
|
|
||||||
|
const [ghostUser] = await knex(TableName.Users)
|
||||||
|
.insert({
|
||||||
|
isGhost: true,
|
||||||
|
authMethods: [AuthMethod.EMAIL],
|
||||||
|
username: email,
|
||||||
|
email,
|
||||||
|
isAccepted: true
|
||||||
|
})
|
||||||
|
.returning("*");
|
||||||
|
|
||||||
|
const encKeys = await generateUserSrpKeys(email, password);
|
||||||
|
|
||||||
|
await knex(TableName.UserEncryptionKey)
|
||||||
|
.insert({ userId: ghostUser.id, encryptionVersion: 2, publicKey: encKeys.publicKey })
|
||||||
|
.onConflict("userId")
|
||||||
|
.merge();
|
||||||
|
|
||||||
|
await knex(TableName.OrgMembership)
|
||||||
|
.insert({
|
||||||
|
orgId,
|
||||||
|
userId: ghostUser.id,
|
||||||
|
role: OrgMembershipRole.Admin,
|
||||||
|
status: OrgMembershipStatus.Accepted,
|
||||||
|
isActive: true
|
||||||
|
})
|
||||||
|
.returning("*");
|
||||||
|
|
||||||
|
const [projectMembership] = await knex(TableName.ProjectMembership)
|
||||||
|
.insert({
|
||||||
|
userId: ghostUser.id,
|
||||||
|
projectId
|
||||||
|
})
|
||||||
|
.returning("*");
|
||||||
|
|
||||||
|
await knex(TableName.ProjectUserMembershipRole).insert({
|
||||||
|
projectMembershipId: projectMembership.id,
|
||||||
|
role: ProjectMembershipRole.Admin
|
||||||
|
});
|
||||||
|
|
||||||
|
const { key: encryptedProjectKey, iv: encryptedProjectKeyIv } = createProjectKey({
|
||||||
|
publicKey: encKeys.publicKey,
|
||||||
|
privateKey: encKeys.plainPrivateKey
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex(TableName.ProjectKeys).insert({
|
||||||
|
projectId,
|
||||||
|
receiverId: ghostUser.id,
|
||||||
|
encryptedKey: encryptedProjectKey,
|
||||||
|
nonce: encryptedProjectKeyIv,
|
||||||
|
senderId: ghostUser.id
|
||||||
|
});
|
||||||
|
|
||||||
|
const { iv, tag, ciphertext, encoding, algorithm } = crypto
|
||||||
|
.encryption()
|
||||||
|
.symmetric()
|
||||||
|
.encryptWithRootEncryptionKey(encKeys.plainPrivateKey);
|
||||||
|
|
||||||
|
await knex(TableName.ProjectBot).insert({
|
||||||
|
name: "Infisical Bot (Ghost)",
|
||||||
|
projectId,
|
||||||
|
tag,
|
||||||
|
iv,
|
||||||
|
encryptedProjectKey,
|
||||||
|
encryptedProjectKeyNonce: encryptedProjectKeyIv,
|
||||||
|
encryptedPrivateKey: ciphertext,
|
||||||
|
isActive: true,
|
||||||
|
publicKey: encKeys.publicKey,
|
||||||
|
senderId: ghostUser.id,
|
||||||
|
algorithm,
|
||||||
|
keyEncoding: encoding
|
||||||
|
});
|
||||||
|
|
||||||
|
const latestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.id, projectId, knex);
|
||||||
|
|
||||||
|
if (!latestKey) {
|
||||||
|
throw new Error("Latest key not found for user");
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await userDAL.findUserEncKeyByUserId(userId, knex);
|
||||||
|
|
||||||
|
if (!user || !user.publicKey) {
|
||||||
|
throw new Error("User not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const [projectAdmin] = assignWorkspaceKeysToMembers({
|
||||||
|
decryptKey: latestKey,
|
||||||
|
userPrivateKey: encKeys.plainPrivateKey,
|
||||||
|
members: [
|
||||||
|
{
|
||||||
|
userPublicKey: user.publicKey,
|
||||||
|
orgMembershipId: userOrgMembershipId
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a membership for the user
|
||||||
|
const userProjectMembership = await projectMembershipDAL.create(
|
||||||
|
{
|
||||||
|
projectId,
|
||||||
|
userId: user.id
|
||||||
|
},
|
||||||
|
knex
|
||||||
|
);
|
||||||
|
await projectUserMembershipRoleDAL.create(
|
||||||
|
{ projectMembershipId: userProjectMembership.id, role: ProjectMembershipRole.Admin },
|
||||||
|
knex
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create a project key for the user
|
||||||
|
await projectKeyDAL.create(
|
||||||
|
{
|
||||||
|
encryptedKey: projectAdmin.workspaceEncryptedKey,
|
||||||
|
nonce: projectAdmin.workspaceEncryptedNonce,
|
||||||
|
senderId: ghostUser.id,
|
||||||
|
receiverId: user.id,
|
||||||
|
projectId
|
||||||
|
},
|
||||||
|
knex
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: ghostUser,
|
||||||
|
keys: encKeys
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export async function seed(knex: Knex): Promise<void> {
|
export async function seed(knex: Knex): Promise<void> {
|
||||||
// Deletes ALL existing entries
|
// Deletes ALL existing entries
|
||||||
await knex(TableName.Project).del();
|
await knex(TableName.Project).del();
|
||||||
@@ -37,7 +195,7 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
})
|
})
|
||||||
.returning("*");
|
.returning("*");
|
||||||
|
|
||||||
const projectMembership = await knex(TableName.ProjectMembership)
|
const [projectMembership] = await knex(TableName.ProjectMembership)
|
||||||
.insert({
|
.insert({
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
userId: seedData1.id
|
userId: seedData1.id
|
||||||
@@ -45,7 +203,7 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
.returning("*");
|
.returning("*");
|
||||||
await knex(TableName.ProjectUserMembershipRole).insert({
|
await knex(TableName.ProjectUserMembershipRole).insert({
|
||||||
role: ProjectMembershipRole.Admin,
|
role: ProjectMembershipRole.Admin,
|
||||||
projectMembershipId: projectMembership[0].id
|
projectMembershipId: projectMembership.id
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = await knex(TableName.UserEncryptionKey).where({ userId: seedData1.id }).first();
|
const user = await knex(TableName.UserEncryptionKey).where({ userId: seedData1.id }).first();
|
||||||
@@ -55,15 +213,7 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
throw new Error("User public key not found");
|
throw new Error("User public key not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const userPrivateKey = await getUserPrivateKey(seedData1.password, user);
|
await createGhostUser(seedData1.organization.id, project.id, seedData1.id, projectMembership.id, knex);
|
||||||
const projectKey = buildUserProjectKey(userPrivateKey, user.publicKey);
|
|
||||||
await knex(TableName.ProjectKeys).insert({
|
|
||||||
projectId: project.id,
|
|
||||||
nonce: projectKey.nonce,
|
|
||||||
encryptedKey: projectKey.ciphertext,
|
|
||||||
receiverId: seedData1.id,
|
|
||||||
senderId: seedData1.id
|
|
||||||
});
|
|
||||||
|
|
||||||
// create default environments and default folders
|
// create default environments and default folders
|
||||||
const envs = await knex(TableName.Environment)
|
const envs = await knex(TableName.Environment)
|
||||||
|
|||||||
Reference in New Issue
Block a user