diff --git a/backend-pg/e2e-test/routes/v1/login.spec.ts b/backend-pg/e2e-test/routes/v1/login.spec.ts index d27934ccc..c95e1e016 100644 --- a/backend-pg/e2e-test/routes/v1/login.spec.ts +++ b/backend-pg/e2e-test/routes/v1/login.spec.ts @@ -1,11 +1,11 @@ -import { testUser } from "@app/db/seeds/1-user"; +import { seedData1 } from "@app/db/seed-data"; import jsrp from "jsrp"; describe("Login V1 Router", async () => { // eslint-disable-next-line const client = new jsrp.client(); await new Promise((resolve) => { - client.init({ username: testUser.email, password: testUser.password }, () => resolve(null)); + client.init({ username: seedData1.email, password: seedData1.password }, () => resolve(null)); }); let clientProof: string; @@ -32,7 +32,7 @@ describe("Login V1 Router", async () => { method: "POST", url: "/api/v3/auth/login2", body: { - email: testUser.email, + email: seedData1.email, clientProof } }); diff --git a/backend-pg/e2e-test/routes/v1/org.spec.ts b/backend-pg/e2e-test/routes/v1/org.spec.ts new file mode 100644 index 000000000..7a92ac4bc --- /dev/null +++ b/backend-pg/e2e-test/routes/v1/org.spec.ts @@ -0,0 +1,19 @@ +import { seedData1 } from "@app/db/seed-data"; + +describe("Org V1 Router", async () => { + test("GET Org list", async () => { + const res = await testServer.inject({ + method: "GET", + url: "/api/v1/organization", + headers: { + authorization: `Bearer ${jwtAuthToken}` + } + }); + expect(res.statusCode).toBe(200); + const payload = JSON.parse(res.payload); + expect(payload).toHaveProperty("organizations"); + expect(payload).toEqual({ + organizations: [expect.objectContaining({ name: seedData1.organization.name })] + }); + }); +}); diff --git a/backend-pg/e2e-test/routes/v1/test.spec.ts b/backend-pg/e2e-test/routes/v1/test.spec.ts deleted file mode 100644 index fefb51ad3..000000000 --- a/backend-pg/e2e-test/routes/v1/test.spec.ts +++ /dev/null @@ -1,9 +0,0 @@ -describe("Status V1 Router", async () => { - test("Simple check", async () => { - const res = await testServer.inject({ - method: "GET", - url: "/api/status" - }); - expect(res.statusCode).toBe(200); - }); -}); diff --git a/backend-pg/e2e-test/vitest-environment-knex.ts b/backend-pg/e2e-test/vitest-environment-knex.ts index f20a09404..80154e05a 100644 --- a/backend-pg/e2e-test/vitest-environment-knex.ts +++ b/backend-pg/e2e-test/vitest-environment-knex.ts @@ -5,10 +5,13 @@ import knex from "knex"; import path from "path"; import { mockSmtpServer } from "./mocks/smtp"; import { initLogger } from "@app/lib/logger"; +import jwt from "jsonwebtoken"; import "ts-node/register"; import { main } from "@app/server/app"; import { mockQueue } from "./mocks/queue"; +import { AuthTokenType } from "@app/services/auth/auth-type"; +import { seedData1 } from "@app/db/seed-data"; dotenv.config({ path: path.join(__dirname, "../.env.test") }); export default { @@ -35,10 +38,21 @@ export default { const smtp = mockSmtpServer(); const queue = mockQueue(); const logger = await initLogger(); - initEnvConfig(logger); + const cfg = initEnvConfig(logger); const server = await main({ db, smtp, logger, queue }); // @ts-expect-error type globalThis.testServer = server; + // @ts-expect-error type + globalThis.jwtAuthToken = jwt.sign( + { + authTokenType: AuthTokenType.ACCESS_TOKEN, + userId: seedData1.id, + tokenVersionId: seedData1.token.id, + accessVersion: 1 + }, + cfg.JWT_AUTH_SECRET, + { expiresIn: cfg.JWT_AUTH_LIFETIME } + ); } catch (error) { await db.destroy(); throw error; @@ -50,6 +64,8 @@ export default { await globalThis.testServer.close(); // @ts-expect-error type delete globalThis.testServer; + // @ts-expect-error type + delete globalThis.jwtToken; // called after all tests with this env have been run await db.migrate.rollback({}, true); await db.destroy(); diff --git a/backend-pg/src/@types/fastify-zod.d.ts b/backend-pg/src/@types/fastify-zod.d.ts index 6db9f6c2e..cff119c2f 100644 --- a/backend-pg/src/@types/fastify-zod.d.ts +++ b/backend-pg/src/@types/fastify-zod.d.ts @@ -19,4 +19,5 @@ declare global { // used only for testing const testServer: FastifyZodProvider; + const jwtAuthToken: string; } diff --git a/backend-pg/src/db/seed-data.ts b/backend-pg/src/db/seed-data.ts new file mode 100644 index 000000000..2f7da6d1a --- /dev/null +++ b/backend-pg/src/db/seed-data.ts @@ -0,0 +1,16 @@ +export const seedData1 = { + id: "3dafd81d-4388-432b-a4c5-f735616868c1", + email: "test@localhost.local", + password: process.env.TEST_USER_PASSWORD || "testInfisical@1", + organization: { + id: "180870b7-f464-4740-8ffe-9d11c9245ea7", + name: "infisical" + }, + project: { + id: "77fa7aed-9288-401e-a4c9-3a9430be62a0", + name: "first project" + }, + token: { + id: "a9dfafba-a3b7-42e3-8618-91abb702fd36" + } +}; diff --git a/backend-pg/src/db/seeds/1-user.ts b/backend-pg/src/db/seeds/1-user.ts index 696eca198..03f77ca06 100644 --- a/backend-pg/src/db/seeds/1-user.ts +++ b/backend-pg/src/db/seeds/1-user.ts @@ -1,17 +1,16 @@ -import { Knex } from "knex"; +import crypto from "node:crypto"; + +import argon2, { argon2id } from "argon2"; import jsrp from "jsrp"; +import { Knex } from "knex"; import nacl from "tweetnacl"; import { encodeBase64 } from "tweetnacl-util"; -import argon2, { argon2id } from "argon2"; -import { AuthMethod } from "../../services/auth/auth-type"; -import { TableName } from "../schemas"; -import crypto from "node:crypto"; + import { encryptSymmetric } from "@app/lib/crypto"; -export const testUser = { - email: "test@localhost.local", - password: process.env.TEST_USER_PASSWORD || "testInfisical@1" -}; +import { AuthMethod } from "../../services/auth/auth-type"; +import { TableName } from "../schemas"; +import { seedData1 } from "../seed-data"; export const generateUserSrpKeys = async (password: string) => { const pair = nacl.box.keyPair(); @@ -23,7 +22,7 @@ export const generateUserSrpKeys = async (password: string) => { // eslint-disable-next-line const client = new jsrp.client(); await new Promise((resolve) => { - client.init({ username: testUser.email, password: testUser.password }, () => resolve(null)); + client.init({ username: seedData1.email, password: seedData1.password }, () => resolve(null)); }); const { salt, verifier } = await new Promise<{ salt: string; verifier: string }>( (resolve, reject) => { @@ -85,7 +84,9 @@ export async function seed(knex: Knex): Promise { const [user] = await knex(TableName.Users) .insert([ { - email: testUser.email, + // @ts-ignore to calculate predefined + id: seedData1.id, + email: seedData1.email, superAdmin: true, firstName: "test", lastName: "", @@ -98,7 +99,7 @@ export async function seed(knex: Knex): Promise { ]) .returning("*"); - const encKeys = await generateUserSrpKeys(testUser.password); + const encKeys = await generateUserSrpKeys(seedData1.password); // password: testInfisical@1 await knex(TableName.UserEncryptionKey).insert([ { @@ -115,4 +116,16 @@ export async function seed(knex: Knex): Promise { userId: user.id } ]); + + await knex(TableName.AuthTokenSession).insert({ + // @ts-ignore + id: seedData1.token.id, + userId: seedData1.id, + ip: "151.196.220.213", + userAgent: + "Mozilla/5.0 (X11; Ubuntu; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36 RuxitSynthetic/1.0 v3690753611340580436 t8052286838287810618", + accessVersion: 1, + refreshVersion: 1, + lastUsed: new Date() + }); } diff --git a/backend-pg/src/db/seeds/2-org.ts b/backend-pg/src/db/seeds/2-org.ts index 07f00ed7c..ccc236909 100644 --- a/backend-pg/src/db/seeds/2-org.ts +++ b/backend-pg/src/db/seeds/2-org.ts @@ -1,19 +1,21 @@ import { Knex } from "knex"; import { OrgMembershipRole, OrgMembershipStatus, TableName } from "../schemas"; -import { testUser } from "./1-user"; +import { seedData1 } from "../seed-data"; export async function seed(knex: Knex): Promise { // Deletes ALL existing entries await knex(TableName.Organization).del(); await knex(TableName.OrgMembership).del(); - const user = await knex(TableName.Users).where({ email: testUser.email }).first(); + const user = await knex(TableName.Users).where({ email: seedData1.email }).first(); if (!user) throw new Error("User not found"); // Inserts seed entries const [org] = await knex(TableName.Organization) .insert([ { + // @ts-ignore because we need that id for api calls + id: seedData1.organization.id, name: "infisical", customerId: null } diff --git a/backend-pg/src/db/seeds/3-project.ts b/backend-pg/src/db/seeds/3-project.ts new file mode 100644 index 000000000..4b79312b8 --- /dev/null +++ b/backend-pg/src/db/seeds/3-project.ts @@ -0,0 +1,39 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; +import { seedData1 } from "../seed-data"; + +export const DEFAULT_PROJECT_ENVS = [ + { name: "Development", slug: "dev" }, + { name: "Staging", slug: "staging" }, + { name: "Production", slug: "prod" } +]; + +export async function seed(knex: Knex): Promise { + // Deletes ALL existing entries + await knex(TableName.Project).del(); + await knex(TableName.Environment).del(); + await knex(TableName.SecretFolder).del(); + + const [project] = await knex(TableName.Project) + .insert({ + name: seedData1.project.name, + orgId: seedData1.organization.id, + // @ts-ignore pre calc id + id: seedData1.project.id + }) + .returning("*"); + const envs = await knex(TableName.Environment) + .insert( + DEFAULT_PROJECT_ENVS.map(({ name, slug }, index) => ({ + name, + slug, + projectId: project.id, + position: index + 1 + })) + ) + .returning("*"); + await knex(TableName.SecretFolder).insert( + envs.map(({ id }) => ({ name: "root", envId: id, parentId: null })) + ); +} diff --git a/backend-pg/src/services/project/project-service.ts b/backend-pg/src/services/project/project-service.ts index 3bb947a84..24e6202c7 100644 --- a/backend-pg/src/services/project/project-service.ts +++ b/backend-pg/src/services/project/project-service.ts @@ -22,7 +22,7 @@ import { ROOT_FOLDER_NAME, TSecretFolderDalFactory } from "../secret-folder/secr import { TProjectDalFactory } from "./project-dal"; import { TCreateProjectDTO, TDeleteProjectDTO, TGetProjectDTO } from "./project-types"; -const DEFAULT_PROJECT_ENVS = [ +export const DEFAULT_PROJECT_ENVS = [ { name: "Development", slug: "dev" }, { name: "Staging", slug: "staging" }, { name: "Production", slug: "prod" }