From 20fb99f042cee2f89894bb0ec844c2e79694c593 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Fri, 12 Jan 2024 20:49:19 +0530 Subject: [PATCH] feat: added token based communications --- backend-pg/package.json | 2 +- backend-pg/scripts/generate-schema-types.ts | 3 +- backend-pg/src/@types/fastify.d.ts | 12 +- .../db/migrations/20231212110939_project.ts | 6 +- .../20231212110946_project-membership.ts | 4 +- .../migrations/20231218092517_secret-tag.ts | 2 +- .../db/migrations/20231218103423_secret.ts | 2 +- .../migrations/20231222092113_project-bot.ts | 2 +- .../migrations/20231222172455_integration.ts | 2 +- .../20231225072545_service-token.ts | 2 +- .../20231228075023_identity-membership.ts | 2 +- .../db/migrations/20240108134148_audit-log.ts | 2 +- backend-pg/src/db/schemas/audit-logs.ts | 2 +- .../schemas/identity-project-memberships.ts | 2 +- .../src/db/schemas/integration-auths.ts | 2 +- backend-pg/src/db/schemas/project-bots.ts | 2 +- .../src/db/schemas/project-environments.ts | 2 +- backend-pg/src/db/schemas/project-keys.ts | 2 +- .../src/db/schemas/project-memberships.ts | 2 +- backend-pg/src/db/schemas/project-roles.ts | 2 +- backend-pg/src/db/schemas/projects.ts | 2 +- .../src/db/schemas/sa-request-secrets.ts | 6 +- .../src/db/schemas/secret-blind-indexes.ts | 2 +- backend-pg/src/db/schemas/secret-tags.ts | 2 +- backend-pg/src/db/schemas/secret-versions.ts | 8 +- backend-pg/src/db/schemas/secrets.ts | 8 +- backend-pg/src/db/schemas/service-tokens.ts | 2 +- backend-pg/src/ee/routes/v1/project-router.ts | 6 +- .../src/ee/routes/v1/snapshot-router.ts | 4 +- .../src/ee/services/license/license-dal.ts | 6 + .../ee/services/license/license-service.ts | 34 ++++ .../src/ee/services/license/license-types.ts | 0 .../services/permission/permission-service.ts | 23 ++- .../services/permission/project-permission.ts | 27 ++++ backend-pg/src/lib/config/env.ts | 5 +- backend-pg/src/lib/errors/index.ts | 4 +- backend-pg/src/lib/ip/index.ts | 11 +- backend-pg/src/server/plugins/audit-log.ts | 16 ++ .../server/plugins/auth/inject-identity.ts | 97 +++++++---- .../server/plugins/auth/inject-permission.ts | 4 + backend-pg/src/server/routes/index.ts | 13 +- .../src/server/routes/v1/auth-router.ts | 18 ++- .../server/routes/v1/project-env-router.ts | 6 +- .../routes/v1/project-membership-router.ts | 10 +- .../src/server/routes/v1/project-router.ts | 1 + .../server/routes/v1/secret-folder-router.ts | 85 +++++++--- .../server/routes/v1/secret-import-router.ts | 64 ++++++-- .../server/routes/v2/organization-router.ts | 6 +- .../src/server/routes/v3/secret-router.ts | 56 ++++++- .../src/services/api-key/api-key-service.ts | 26 ++- .../services/auth-token/auth-token-service.ts | 34 +++- backend-pg/src/services/auth/auth-type.ts | 2 - .../identity-access-token-dal.ts | 41 ++++- .../identity-access-token-service.ts | 71 ++++++-- .../identity-access-token-types.ts | 7 + .../identity-ua/identity-ua-service.ts | 3 +- .../src/services/project/project-dal.ts | 77 ++++----- .../src/services/project/project-service.ts | 3 +- .../secret-folder/secret-folder-service.ts | 9 +- .../service-token/service-token-service.ts | 23 ++- backend/src/utils/authn/helpers/index.ts | 153 +++++------------- .../src/hooks/api/secretFolders/queries.tsx | 13 +- .../src/hooks/api/secretImports/mutation.tsx | 6 +- .../src/hooks/api/secretImports/queries.tsx | 4 +- frontend/src/hooks/api/workspace/index.tsx | 4 +- 65 files changed, 699 insertions(+), 360 deletions(-) create mode 100644 backend-pg/src/ee/services/license/license-dal.ts create mode 100644 backend-pg/src/ee/services/license/license-service.ts create mode 100644 backend-pg/src/ee/services/license/license-types.ts diff --git a/backend-pg/package.json b/backend-pg/package.json index 84821b0b2..73aa54cb8 100644 --- a/backend-pg/package.json +++ b/backend-pg/package.json @@ -31,7 +31,6 @@ "author": "", "license": "ISC", "devDependencies": { - "@octokit/webhooks-types": "^7.3.1", "@types/bcrypt": "^5.0.2", "@types/jmespath": "^0.15.2", "@types/jsonwebtoken": "^9.0.5", @@ -80,6 +79,7 @@ "@node-saml/passport-saml": "^4.0.4", "@octokit/rest": "^20.0.2", "@ucast/mongo2js": "^1.3.4", + "@octokit/webhooks-types": "^7.3.1", "ajv": "^8.12.0", "argon2": "^0.31.2", "aws-sdk": "^2.1532.0", diff --git a/backend-pg/scripts/generate-schema-types.ts b/backend-pg/scripts/generate-schema-types.ts index e9f69cf8f..125875a4e 100644 --- a/backend-pg/scripts/generate-schema-types.ts +++ b/backend-pg/scripts/generate-schema-types.ts @@ -48,6 +48,7 @@ const getZodDefaultValue = (type: unknown, value: string | number | boolean | Ob case "uuid": return; case "character varying": { + if (value === "gen_random_uuid()") return; if (typeof value === "string" && value.includes("::")) { return `.default(${value.split("::")[0]})`; } @@ -85,7 +86,7 @@ const main = async () => { .whereRaw("table_schema = current_schema()") .select<{ tableName: string }[]>("table_name as tableName") .orderBy("table_name") - ).filter((el) => el.tableName.includes("migration")); + ).filter((el) => !el.tableName.includes("_migrations")); console.log("Select a table to generate schema"); console.table(tables); diff --git a/backend-pg/src/@types/fastify.d.ts b/backend-pg/src/@types/fastify.d.ts index e48b65eab..6557f5079 100644 --- a/backend-pg/src/@types/fastify.d.ts +++ b/backend-pg/src/@types/fastify.d.ts @@ -10,12 +10,13 @@ import { TSecretApprovalRequestServiceFactory } from "@app/ee/services/secret-ap import { TSecretRotationServiceFactory } from "@app/ee/services/secret-rotation/secret-rotation-service"; import { TSecretScanningServiceFactory } from "@app/ee/services/secret-scanning/secret-scanning-service"; import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; +import { TAuthMode } from "@app/server/plugins/auth/inject-identity"; import { TApiKeyServiceFactory } from "@app/services/api-key/api-key-service"; import { TAuthLoginFactory } from "@app/services/auth/auth-login-service"; import { TAuthPasswordFactory } from "@app/services/auth/auth-password-service"; import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service"; -import { AuthMode } from "@app/services/auth/auth-signup-type"; import { ActorType } from "@app/services/auth/auth-type"; +import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service"; import { TIdentityServiceFactory } from "@app/services/identity/identity-service"; import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service"; import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service"; @@ -36,7 +37,6 @@ import { TSecretImportServiceFactory } from "@app/services/secret-import/secret- import { TSecretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service"; import { TServiceTokenServiceFactory } from "@app/services/service-token/service-token-service"; import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service"; -import { TAuthTokenServiceFactory } from "@app/services/token/token-service"; import { TUserDalFactory } from "@app/services/user/user-dal"; import { TUserServiceFactory } from "@app/services/user/user-service"; import { TWebhookServiceFactory } from "@app/services/webhook/webhook-service"; @@ -50,13 +50,7 @@ declare module "fastify" { user: TUsers; }; // identity injection. depending on which kinda of token the information is filled in auth - auth: { - authMode: AuthMode.JWT | AuthMode.API_KEY_V2 | AuthMode.API_KEY; - actor: ActorType.USER; - userId: string; - tokenVersionId: string; // the session id of token used - user: TUsers; - }; + auth: TAuthMode; permission: { type: ActorType; id: string; diff --git a/backend-pg/src/db/migrations/20231212110939_project.ts b/backend-pg/src/db/migrations/20231212110939_project.ts index ee4d32530..7591b5b9b 100644 --- a/backend-pg/src/db/migrations/20231212110939_project.ts +++ b/backend-pg/src/db/migrations/20231212110939_project.ts @@ -6,7 +6,7 @@ import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils"; export async function up(knex: Knex): Promise { if (!(await knex.schema.hasTable(TableName.Project))) { await knex.schema.createTable(TableName.Project, (t) => { - t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + t.string("id").primary().defaultTo(knex.fn.uuid()); t.string("name").notNullable(); t.boolean("autoCapitalization").defaultTo(true); t.uuid("orgId").notNullable(); @@ -22,7 +22,7 @@ export async function up(knex: Knex): Promise { t.string("name").notNullable(); t.string("slug").notNullable(); t.integer("position").notNullable(); - t.uuid("projectId").notNullable(); + t.string("projectId").notNullable(); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); // this will ensure ever env has its position t.unique(["projectId", "position"], { @@ -43,7 +43,7 @@ export async function up(knex: Knex): Promise { t.uuid("senderId"); // if sender is deleted just don't do anything to this record t.foreign("senderId").references("id").inTable(TableName.Users).onDelete("SET NULL"); - t.uuid("projectId").notNullable(); + t.string("projectId").notNullable(); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); t.timestamps(true, true, true); }); diff --git a/backend-pg/src/db/migrations/20231212110946_project-membership.ts b/backend-pg/src/db/migrations/20231212110946_project-membership.ts index a5d81953d..ad7cdb8a5 100644 --- a/backend-pg/src/db/migrations/20231212110946_project-membership.ts +++ b/backend-pg/src/db/migrations/20231212110946_project-membership.ts @@ -13,7 +13,7 @@ export async function up(knex: Knex): Promise { t.jsonb("permissions").notNullable(); // does not need update trigger we will do it manually t.timestamps(true, true, true); - t.uuid("projectId").notNullable(); + t.string("projectId").notNullable(); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); }); } @@ -26,7 +26,7 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); t.uuid("userId").notNullable(); t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE"); - t.uuid("projectId").notNullable(); + t.string("projectId").notNullable(); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); // until role is changed/removed the role should not deleted t.uuid("roleId"); diff --git a/backend-pg/src/db/migrations/20231218092517_secret-tag.ts b/backend-pg/src/db/migrations/20231218092517_secret-tag.ts index a1e000e6a..051f39d76 100644 --- a/backend-pg/src/db/migrations/20231218092517_secret-tag.ts +++ b/backend-pg/src/db/migrations/20231218092517_secret-tag.ts @@ -13,7 +13,7 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); t.uuid("createdBy"); t.foreign("createdBy").references("id").inTable(TableName.Users).onDelete("SET NULL"); - t.uuid("projectId").notNullable(); + t.string("projectId").notNullable(); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); }); } diff --git a/backend-pg/src/db/migrations/20231218103423_secret.ts b/backend-pg/src/db/migrations/20231218103423_secret.ts index 4334e2bf3..a29dccfc8 100644 --- a/backend-pg/src/db/migrations/20231218103423_secret.ts +++ b/backend-pg/src/db/migrations/20231218103423_secret.ts @@ -12,7 +12,7 @@ export async function up(knex: Knex): Promise { t.text("saltTag").notNullable(); t.string("algorithm").notNullable().defaultTo(SecretEncryptionAlgo.AES_256_GCM); t.string("keyEncoding").notNullable().defaultTo(SecretKeyEncoding.UTF8); - t.uuid("projectId").notNullable().unique(); + t.string("projectId").notNullable().unique(); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); t.timestamps(true, true, true); }); diff --git a/backend-pg/src/db/migrations/20231222092113_project-bot.ts b/backend-pg/src/db/migrations/20231222092113_project-bot.ts index 5a1e95682..98c67c8b6 100644 --- a/backend-pg/src/db/migrations/20231222092113_project-bot.ts +++ b/backend-pg/src/db/migrations/20231222092113_project-bot.ts @@ -18,7 +18,7 @@ export async function up(knex: Knex): Promise { t.text("encryptedProjectKey"); t.text("encryptedProjectKeyNonce"); // one to one relationship - t.uuid("projectId").notNullable().unique(); + t.string("projectId").notNullable().unique(); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); t.uuid("senderId"); t.foreign("senderId").references("id").inTable(TableName.Users).onDelete("SET NULL"); diff --git a/backend-pg/src/db/migrations/20231222172455_integration.ts b/backend-pg/src/db/migrations/20231222172455_integration.ts index 26e7c4a2d..cedbde23e 100644 --- a/backend-pg/src/db/migrations/20231222172455_integration.ts +++ b/backend-pg/src/db/migrations/20231222172455_integration.ts @@ -25,7 +25,7 @@ export async function up(knex: Knex): Promise { t.jsonb("metadata"); t.string("algorithm").notNullable(); t.string("keyEncoding").notNullable(); - t.uuid("projectId").notNullable(); + t.string("projectId").notNullable(); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); t.timestamps(true, true, true); }); diff --git a/backend-pg/src/db/migrations/20231225072545_service-token.ts b/backend-pg/src/db/migrations/20231225072545_service-token.ts index 7e416800f..9d385f245 100644 --- a/backend-pg/src/db/migrations/20231225072545_service-token.ts +++ b/backend-pg/src/db/migrations/20231225072545_service-token.ts @@ -19,7 +19,7 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); // user is old one t.string("createdBy").notNullable(); - t.uuid("projectId").notNullable(); + t.string("projectId").notNullable(); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); }); } diff --git a/backend-pg/src/db/migrations/20231228075023_identity-membership.ts b/backend-pg/src/db/migrations/20231228075023_identity-membership.ts index 3c0e56a2a..288c11be3 100644 --- a/backend-pg/src/db/migrations/20231228075023_identity-membership.ts +++ b/backend-pg/src/db/migrations/20231228075023_identity-membership.ts @@ -25,7 +25,7 @@ export async function up(knex: Knex): Promise { t.string("role").notNullable(); t.uuid("roleId"); t.foreign("roleId").references("id").inTable(TableName.ProjectRoles); - t.uuid("projectId").notNullable(); + t.string("projectId").notNullable(); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); t.uuid("identityId").notNullable(); t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE"); diff --git a/backend-pg/src/db/migrations/20240108134148_audit-log.ts b/backend-pg/src/db/migrations/20240108134148_audit-log.ts index b271f9ab3..9eb74f03d 100644 --- a/backend-pg/src/db/migrations/20240108134148_audit-log.ts +++ b/backend-pg/src/db/migrations/20240108134148_audit-log.ts @@ -18,7 +18,7 @@ export async function up(knex: Knex): Promise { // no trigger needed as this collection is append only t.uuid("orgId"); t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE"); - t.uuid("projectId"); + t.string("projectId"); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); }); } diff --git a/backend-pg/src/db/schemas/audit-logs.ts b/backend-pg/src/db/schemas/audit-logs.ts index 2f3c472b3..90c389b94 100644 --- a/backend-pg/src/db/schemas/audit-logs.ts +++ b/backend-pg/src/db/schemas/audit-logs.ts @@ -20,7 +20,7 @@ export const AuditLogsSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), orgId: z.string().uuid().nullable().optional(), - projectId: z.string().uuid().nullable().optional(), + projectId: z.string().nullable().optional(), }); export type TAuditLogs = z.infer; diff --git a/backend-pg/src/db/schemas/identity-project-memberships.ts b/backend-pg/src/db/schemas/identity-project-memberships.ts index f631a10e0..9a57952a4 100644 --- a/backend-pg/src/db/schemas/identity-project-memberships.ts +++ b/backend-pg/src/db/schemas/identity-project-memberships.ts @@ -11,7 +11,7 @@ export const IdentityProjectMembershipsSchema = z.object({ id: z.string().uuid(), role: z.string(), roleId: z.string().uuid().nullable().optional(), - projectId: z.string().uuid(), + projectId: z.string(), identityId: z.string().uuid(), createdAt: z.date(), updatedAt: z.date(), diff --git a/backend-pg/src/db/schemas/integration-auths.ts b/backend-pg/src/db/schemas/integration-auths.ts index 8f930e83f..d2983658c 100644 --- a/backend-pg/src/db/schemas/integration-auths.ts +++ b/backend-pg/src/db/schemas/integration-auths.ts @@ -27,7 +27,7 @@ export const IntegrationAuthsSchema = z.object({ metadata: z.unknown().nullable().optional(), algorithm: z.string(), keyEncoding: z.string(), - projectId: z.string().uuid(), + projectId: z.string(), createdAt: z.date(), updatedAt: z.date(), }); diff --git a/backend-pg/src/db/schemas/project-bots.ts b/backend-pg/src/db/schemas/project-bots.ts index b898be329..90ced9b3e 100644 --- a/backend-pg/src/db/schemas/project-bots.ts +++ b/backend-pg/src/db/schemas/project-bots.ts @@ -19,7 +19,7 @@ export const ProjectBotsSchema = z.object({ keyEncoding: z.string(), encryptedProjectKey: z.string().nullable().optional(), encryptedProjectKeyNonce: z.string().nullable().optional(), - projectId: z.string().uuid(), + projectId: z.string(), senderId: z.string().uuid().nullable().optional(), createdAt: z.date(), updatedAt: z.date(), diff --git a/backend-pg/src/db/schemas/project-environments.ts b/backend-pg/src/db/schemas/project-environments.ts index 53f3f9713..aa3e392c7 100644 --- a/backend-pg/src/db/schemas/project-environments.ts +++ b/backend-pg/src/db/schemas/project-environments.ts @@ -12,7 +12,7 @@ export const ProjectEnvironmentsSchema = z.object({ name: z.string(), slug: z.string(), position: z.number(), - projectId: z.string().uuid(), + projectId: z.string(), createdAt: z.date(), updatedAt: z.date(), }); diff --git a/backend-pg/src/db/schemas/project-keys.ts b/backend-pg/src/db/schemas/project-keys.ts index 30ff5ce98..64e33d574 100644 --- a/backend-pg/src/db/schemas/project-keys.ts +++ b/backend-pg/src/db/schemas/project-keys.ts @@ -13,7 +13,7 @@ export const ProjectKeysSchema = z.object({ nonce: z.string(), receiverId: z.string().uuid(), senderId: z.string().uuid().nullable().optional(), - projectId: z.string().uuid(), + projectId: z.string(), createdAt: z.date(), updatedAt: z.date(), }); diff --git a/backend-pg/src/db/schemas/project-memberships.ts b/backend-pg/src/db/schemas/project-memberships.ts index b8156ab9d..c98befb38 100644 --- a/backend-pg/src/db/schemas/project-memberships.ts +++ b/backend-pg/src/db/schemas/project-memberships.ts @@ -13,7 +13,7 @@ export const ProjectMembershipsSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), userId: z.string().uuid(), - projectId: z.string().uuid(), + projectId: z.string(), roleId: z.string().uuid().nullable().optional(), }); diff --git a/backend-pg/src/db/schemas/project-roles.ts b/backend-pg/src/db/schemas/project-roles.ts index 1cd763173..190dd1cec 100644 --- a/backend-pg/src/db/schemas/project-roles.ts +++ b/backend-pg/src/db/schemas/project-roles.ts @@ -15,7 +15,7 @@ export const ProjectRolesSchema = z.object({ permissions: z.unknown(), createdAt: z.date(), updatedAt: z.date(), - projectId: z.string().uuid(), + projectId: z.string(), }); export type TProjectRoles = z.infer; diff --git a/backend-pg/src/db/schemas/projects.ts b/backend-pg/src/db/schemas/projects.ts index 007e7d018..f44010674 100644 --- a/backend-pg/src/db/schemas/projects.ts +++ b/backend-pg/src/db/schemas/projects.ts @@ -8,7 +8,7 @@ import { z } from "zod"; import { TImmutableDBKeys } from "./models"; export const ProjectsSchema = z.object({ - id: z.string().uuid(), + id: z.string(), name: z.string(), autoCapitalization: z.boolean().default(true).nullable().optional(), orgId: z.string().uuid(), diff --git a/backend-pg/src/db/schemas/sa-request-secrets.ts b/backend-pg/src/db/schemas/sa-request-secrets.ts index 7419eefe2..b8135a785 100644 --- a/backend-pg/src/db/schemas/sa-request-secrets.ts +++ b/backend-pg/src/db/schemas/sa-request-secrets.ts @@ -23,15 +23,15 @@ export const SaRequestSecretsSchema = z.object({ secretReminderNote: z.string().nullable().optional(), secretReminderRepeatDays: z.number().nullable().optional(), skipMultilineEncoding: z.boolean().default(false).nullable().optional(), - algorithm: z.string().default("aes-256-gcm"), - keyEncoding: z.string().default("utf8"), + algorithm: z.string().default('aes-256-gcm'), + keyEncoding: z.string().default('utf8'), metadata: z.unknown().nullable().optional(), createdAt: z.date(), updatedAt: z.date(), requestId: z.string().uuid(), op: z.string(), secretId: z.string().uuid().nullable().optional(), - secretVersion: z.string().uuid().nullable().optional() + secretVersion: z.string().uuid().nullable().optional(), }); export type TSaRequestSecrets = z.infer; diff --git a/backend-pg/src/db/schemas/secret-blind-indexes.ts b/backend-pg/src/db/schemas/secret-blind-indexes.ts index 695566d08..17eacb473 100644 --- a/backend-pg/src/db/schemas/secret-blind-indexes.ts +++ b/backend-pg/src/db/schemas/secret-blind-indexes.ts @@ -14,7 +14,7 @@ export const SecretBlindIndexesSchema = z.object({ saltTag: z.string(), algorithm: z.string().default('aes-256-gcm'), keyEncoding: z.string().default('utf8'), - projectId: z.string().uuid(), + projectId: z.string(), createdAt: z.date(), updatedAt: z.date(), }); diff --git a/backend-pg/src/db/schemas/secret-tags.ts b/backend-pg/src/db/schemas/secret-tags.ts index 73a72ba42..78f03dedd 100644 --- a/backend-pg/src/db/schemas/secret-tags.ts +++ b/backend-pg/src/db/schemas/secret-tags.ts @@ -15,7 +15,7 @@ export const SecretTagsSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), createdBy: z.string().uuid().nullable().optional(), - projectId: z.string().uuid(), + projectId: z.string(), }); export type TSecretTags = z.infer; diff --git a/backend-pg/src/db/schemas/secret-versions.ts b/backend-pg/src/db/schemas/secret-versions.ts index 7f3cc40f6..3a04a8cd9 100644 --- a/backend-pg/src/db/schemas/secret-versions.ts +++ b/backend-pg/src/db/schemas/secret-versions.ts @@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models"; export const SecretVersionsSchema = z.object({ id: z.string().uuid(), version: z.number().default(1), - type: z.string().default("shared"), + type: z.string().default('shared'), secretBlindIndex: z.string(), secretKeyCiphertext: z.string(), secretKeyIV: z.string(), @@ -24,15 +24,15 @@ export const SecretVersionsSchema = z.object({ secretReminderNote: z.string().nullable().optional(), secretReminderRepeatDays: z.number().nullable().optional(), skipMultilineEncoding: z.boolean().default(false).nullable().optional(), - algorithm: z.string().default("aes-256-gcm"), - keyEncoding: z.string().default("utf8"), + algorithm: z.string().default('aes-256-gcm'), + keyEncoding: z.string().default('utf8'), metadata: z.unknown().nullable().optional(), envId: z.string().uuid().nullable().optional(), secretId: z.string().uuid(), folderId: z.string().uuid(), userId: z.string().uuid().nullable().optional(), createdAt: z.date(), - updatedAt: z.date() + updatedAt: z.date(), }); export type TSecretVersions = z.infer; diff --git a/backend-pg/src/db/schemas/secrets.ts b/backend-pg/src/db/schemas/secrets.ts index e08ae8592..a284ae770 100644 --- a/backend-pg/src/db/schemas/secrets.ts +++ b/backend-pg/src/db/schemas/secrets.ts @@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models"; export const SecretsSchema = z.object({ id: z.string().uuid(), version: z.number().default(1), - type: z.string().default("shared"), + type: z.string().default('shared'), secretBlindIndex: z.string(), secretKeyCiphertext: z.string(), secretKeyIV: z.string(), @@ -24,13 +24,13 @@ export const SecretsSchema = z.object({ secretReminderNote: z.string().nullable().optional(), secretReminderRepeatDays: z.number().nullable().optional(), skipMultilineEncoding: z.boolean().default(false).nullable().optional(), - algorithm: z.string().default("aes-256-gcm"), - keyEncoding: z.string().default("utf8"), + algorithm: z.string().default('aes-256-gcm'), + keyEncoding: z.string().default('utf8'), metadata: z.unknown().nullable().optional(), userId: z.string().uuid().nullable().optional(), folderId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date() + updatedAt: z.date(), }); export type TSecrets = z.infer; diff --git a/backend-pg/src/db/schemas/service-tokens.ts b/backend-pg/src/db/schemas/service-tokens.ts index d559939c9..29b0cd49f 100644 --- a/backend-pg/src/db/schemas/service-tokens.ts +++ b/backend-pg/src/db/schemas/service-tokens.ts @@ -21,7 +21,7 @@ export const ServiceTokensSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), createdBy: z.string(), - projectId: z.string().uuid(), + projectId: z.string(), }); export type TServiceTokens = z.infer; diff --git a/backend-pg/src/ee/routes/v1/project-router.ts b/backend-pg/src/ee/routes/v1/project-router.ts index f5b67042f..294150c83 100644 --- a/backend-pg/src/ee/routes/v1/project-router.ts +++ b/backend-pg/src/ee/routes/v1/project-router.ts @@ -25,7 +25,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secretSnapshots = await server.services.snapshot.listSnapshots({ actor: req.permission.type, @@ -54,7 +54,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const count = await server.services.snapshot.projectSecretSnapshotCount({ actor: req.permission.type, @@ -107,7 +107,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const auditLogs = await server.services.auditLog.listProjectAuditLogs({ actorId: req.permission.id, diff --git a/backend-pg/src/ee/routes/v1/snapshot-router.ts b/backend-pg/src/ee/routes/v1/snapshot-router.ts index d905c7a4a..f78f515e7 100644 --- a/backend-pg/src/ee/routes/v1/snapshot-router.ts +++ b/backend-pg/src/ee/routes/v1/snapshot-router.ts @@ -30,7 +30,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secretSnapshot = await server.services.snapshot.getSnapshotData({ actor: req.permission.type, @@ -54,7 +54,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secretSnapshot = await server.services.snapshot.rollbackSnapshot({ actor: req.permission.type, diff --git a/backend-pg/src/ee/services/license/license-dal.ts b/backend-pg/src/ee/services/license/license-dal.ts new file mode 100644 index 000000000..07d478aa3 --- /dev/null +++ b/backend-pg/src/ee/services/license/license-dal.ts @@ -0,0 +1,6 @@ +import { TDbClient } from "@app/db"; +import { TableName } from "@app/db/schemas"; + +export type TLicenseDalFactory = ReturnType; + +export const licenseDalFactory = (db: TDbClient) => ({ }); diff --git a/backend-pg/src/ee/services/license/license-service.ts b/backend-pg/src/ee/services/license/license-service.ts new file mode 100644 index 000000000..e1362a9fa --- /dev/null +++ b/backend-pg/src/ee/services/license/license-service.ts @@ -0,0 +1,34 @@ +import axios from "axios"; + +import { getConfig } from "@app/lib/config/env"; + +import { TLicenseDalFactory } from "./license-dal"; + +type TLicenseServiceFactoryDep = { + licenseDal: TLicenseDalFactory; +}; + +export type TLicenseServiceFactory = ReturnType; + +export const licenseServiceFactory = ({ licenseDal }: TLicenseServiceFactoryDep) => { + const appCfg = getConfig(); + const licenceApi = axios.create({ + baseURL: appCfg.LICENCE_SERVER_URL + }); + + const generateOrgCustomerId = async (orgName: string, email: string) => { + const { + data: { customerId } + } = await licenceApi.post("/api/license-server/v1/customers", { email, name: orgName }); + return customerId; + }; + + const removeOrgCustomer = async (customerId: string) => { + await licenceApi.delete(`/api/license-server/v1/customers/${customerId}`); + }; + + return { + generateOrgCustomerId, + removeOrgCustomer + }; +}; diff --git a/backend-pg/src/ee/services/license/license-types.ts b/backend-pg/src/ee/services/license/license-types.ts new file mode 100644 index 000000000..e69de29bb diff --git a/backend-pg/src/ee/services/permission/permission-service.ts b/backend-pg/src/ee/services/permission/permission-service.ts index a025f450f..795320c23 100644 --- a/backend-pg/src/ee/services/permission/permission-service.ts +++ b/backend-pg/src/ee/services/permission/permission-service.ts @@ -1,12 +1,13 @@ import { createMongoAbility, MongoAbility, RawRuleOf } from "@casl/ability"; import { PackRule, unpackRules } from "@casl/ability/extra"; -import { OrgMembershipRole, ProjectMembershipRole } from "@app/db/schemas"; +import { OrgMembershipRole, ProjectMembershipRole, ServiceTokenScopes } from "@app/db/schemas"; import { conditionsMatcher } from "@app/lib/casl"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { ActorType } from "@app/services/auth/auth-type"; import { TOrgRoleDalFactory } from "@app/services/org/org-role-dal"; import { TProjectRoleDalFactory } from "@app/services/project-role/project-role-dal"; +import { TServiceTokenDalFactory } from "@app/services/service-token/service-token-dal"; import { orgAdminPermissions, @@ -16,6 +17,7 @@ import { } from "./org-permission"; import { TPermissionDalFactory } from "./permission-dal"; import { + buildServiceTokenProjectPermission, projectAdminPermissions, projectMemberPermissions, projectNoAccessPermissions, @@ -25,6 +27,7 @@ import { type TPermissionServiceFactoryDep = { orgRoleDal: Pick; projectRoleDal: Pick; + serviceTokenDal: Pick; permissionDal: TPermissionDalFactory; }; @@ -33,7 +36,8 @@ export type TPermissionServiceFactory = ReturnType { const buildOrgPermission = (role: string, permission?: unknown) => { switch (role) { @@ -157,10 +161,25 @@ export const permissionServiceFactory = ({ }; }; + const getServiceTokenProjectPermission = async (serviceTokenId: string, projectId: string) => { + const serviceToken = await serviceTokenDal.findById(serviceTokenId); + if (serviceToken.projectId !== projectId) + throw new UnauthorizedError({ + message: "Failed to find service authorization for given project" + }); + const scopes = ServiceTokenScopes.parse(serviceToken.scopes || []); + return { + permission: buildServiceTokenProjectPermission(scopes, serviceToken.permissions), + member: undefined + }; + }; + const getProjectPermission = async (type: ActorType, id: string, projectId: string) => { switch (type) { case ActorType.USER: return getUserProjectPermission(id, projectId); + case ActorType.SERVICE: + return getServiceTokenProjectPermission(id, projectId); case ActorType.IDENTITY: return getIdentityProjectPermission(id, projectId); default: diff --git a/backend-pg/src/ee/services/permission/project-permission.ts b/backend-pg/src/ee/services/permission/project-permission.ts index 5b92c8f81..3459c73c7 100644 --- a/backend-pg/src/ee/services/permission/project-permission.ts +++ b/backend-pg/src/ee/services/permission/project-permission.ts @@ -230,6 +230,33 @@ const buildNoAccessProjectPermission = () => { return build({ conditionsMatcher }); }; +export const buildServiceTokenProjectPermission = ( + scopes: Array<{ secretPath: string; environment: string }>, + permission: string[] +) => { + const canWrite = permission.includes("write"); + const canRead = permission.includes("read"); + const { can, build } = new AbilityBuilder>(createMongoAbility); + scopes.forEach(({ secretPath, environment }) => { + if (canWrite) { + can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets, { secretPath, environment }); + can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets, { + secretPath, + environment + }); + can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets, { + secretPath, + environment + }); + } + if (canRead) { + can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets, { secretPath, environment }); + } + }); + + return build({ conditionsMatcher }); +}; + export const projectNoAccessPermissions = buildNoAccessProjectPermission(); /** diff --git a/backend-pg/src/lib/config/env.ts b/backend-pg/src/lib/config/env.ts index ee1b9efc5..2787cea69 100644 --- a/backend-pg/src/lib/config/env.ts +++ b/backend-pg/src/lib/config/env.ts @@ -81,7 +81,10 @@ const envSchema = z SECRET_SCANNING_WEBHOOK_PROXY: zpStr(z.string().optional()), SECRET_SCANNING_WEBHOOK_SECRET: zpStr(z.string().optional()), SECRET_SCANNING_GIT_APP_ID: zpStr(z.string().optional()), - SECRET_SCANNING_PRIVATE_KEY: zpStr(z.string().optional()) + SECRET_SCANNING_PRIVATE_KEY: zpStr(z.string().optional()), + // LICENCE + LICENCE_SERVER_URL: zpStr(z.string().optional()), + LICENCE_SERVER_KEY: zpStr(z.string().optional()) }) .transform((data) => ({ ...data, diff --git a/backend-pg/src/lib/errors/index.ts b/backend-pg/src/lib/errors/index.ts index 5699b7627..b78dc40a7 100644 --- a/backend-pg/src/lib/errors/index.ts +++ b/backend-pg/src/lib/errors/index.ts @@ -4,9 +4,9 @@ export class DatabaseError extends Error { error: unknown; - constructor({ name, error, message }: { message?: string; name: string; error: unknown }) { + constructor({ name, error, message }: { message?: string; name?: string; error: unknown }) { super(message || "Failed to execute db ops"); - this.name = name; + this.name = name || "DatabaseError"; this.error = error; } } diff --git a/backend-pg/src/lib/ip/index.ts b/backend-pg/src/lib/ip/index.ts index 6c9326118..f14ed4f41 100644 --- a/backend-pg/src/lib/ip/index.ts +++ b/backend-pg/src/lib/ip/index.ts @@ -103,6 +103,11 @@ export const isValidIpOrCidr = (ip: string): boolean => { return false; }; +export type TIp = { + ipAddress: string; + type: IPType; + prefix: number; +}; /** * Validates the IP address [ipAddress] against the trusted IPs [trustedIps]. */ @@ -111,11 +116,7 @@ export const checkIPAgainstBlocklist = ({ trustedIps }: { ipAddress: string; - trustedIps: { - ipAddress: string; - type: IPType; - prefix: number; - }[]; + trustedIps: TIp[]; }) => { const blockList = new net.BlockList(); diff --git a/backend-pg/src/server/plugins/audit-log.ts b/backend-pg/src/server/plugins/audit-log.ts index 1456e5376..e5b37517f 100644 --- a/backend-pg/src/server/plugins/audit-log.ts +++ b/backend-pg/src/server/plugins/audit-log.ts @@ -47,6 +47,22 @@ export const injectAuditLogInfo = fp(async (server: FastifyZodProvider) => { userId: req.auth.userId } }; + } else if (req.auth.actor === ActorType.SERVICE) { + payload.actor = { + type: ActorType.SERVICE, + metadata: { + name: req.auth.serviceToken.name, + serviceId: req.auth.serviceTokenId + } + }; + } else if (req.auth.actor === ActorType.IDENTITY) { + payload.actor = { + type: ActorType.IDENTITY, + metadata: { + name: req.auth.identityName, + identityId: req.auth.identityId + } + }; } else { throw new BadRequestError({ message: "Missing logic for other actor" }); } diff --git a/backend-pg/src/server/plugins/auth/inject-identity.ts b/backend-pg/src/server/plugins/auth/inject-identity.ts index ae9f9c9b0..98e6e4124 100644 --- a/backend-pg/src/server/plugins/auth/inject-identity.ts +++ b/backend-pg/src/server/plugins/auth/inject-identity.ts @@ -2,6 +2,7 @@ import { FastifyRequest } from "fastify"; import fp from "fastify-plugin"; import jwt, { JwtPayload } from "jsonwebtoken"; +import { TServiceTokens, TUsers } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; import { UnauthorizedError } from "@app/lib/errors"; import { @@ -10,6 +11,34 @@ import { AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type"; +import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types"; + +export type TAuthMode = + | { + authMode: AuthMode.JWT; + actor: ActorType.USER; + userId: string; + tokenVersionId: string; // the session id of token used + user: TUsers; + } + | { + authMode: AuthMode.API_KEY; + actor: ActorType.USER; + userId: string; + user: TUsers; + } + | { + authMode: AuthMode.SERVICE_TOKEN; + serviceToken: TServiceTokens; + actor: ActorType.SERVICE; + serviceTokenId: string; + } + | { + authMode: AuthMode.IDENTITY_ACCESS_TOKEN; + actor: ActorType.IDENTITY; + identityId: string; + identityName: string; + }; const extractAuth = async (req: FastifyRequest, jwtSecret: string) => { const apiKey = req.headers?.["x-api-key"]; @@ -24,7 +53,7 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => { return { authMode: AuthMode.SERVICE_TOKEN, token: authTokenValue, - actor: ActorType.USER + actor: ActorType.SERVICE } as const; } @@ -37,58 +66,62 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => { actor: ActorType.USER } as const; case AuthTokenType.API_KEY: - return { authMode: AuthMode.API_KEY_V2, token: decodedToken, actor: ActorType.USER } as const; - case AuthMode.SERVICE_ACCESS_TOKEN: + return { authMode: AuthMode.API_KEY, token: decodedToken, actor: ActorType.USER } as const; + case AuthTokenType.IDENTITY_ACCESS_TOKEN: return { - authMode: AuthMode.SERVICE_ACCESS_TOKEN, - token: decodedToken, - actor: ActorType.USER + authMode: AuthMode.IDENTITY_ACCESS_TOKEN, + token: decodedToken as TIdentityAccessTokenJwtPayload, + actor: ActorType.IDENTITY } as const; default: return { authMode: null, token: null } as const; } }; -const getJwtIdentity = async (server: FastifyZodProvider, token: AuthModeJwtTokenPayload) => { - const session = await server.services.authToken.getUserTokenSessionById( - token.tokenVersionId, - token.userId - ); - - if (!session) throw new UnauthorizedError({ name: "Session not found" }); - if (token.accessVersion !== session.accessVersion) - throw new UnauthorizedError({ name: "Stale session" }); - - const user = await server.store.user.findById(session.userId); - if (!user || !user.isAccepted) throw new UnauthorizedError({ name: "Token user not found" }); - - return { user, tokenVersionId: token.tokenVersionId }; -}; - export const injectIdentity = fp(async (server: FastifyZodProvider) => { server.decorateRequest("auth", null); server.addHook("onRequest", async (req) => { const appCfg = getConfig(); const { authMode, token, actor } = await extractAuth(req, appCfg.JWT_AUTH_SECRET); if (!authMode) return; - // TODO(akhilmhdh-pg): fill in rest of auth mode logic + switch (authMode) { case AuthMode.JWT: { - const { user, tokenVersionId } = await getJwtIdentity( - server, - token as AuthModeJwtTokenPayload - ); + const { user, tokenVersionId } = + await server.services.authToken.fnValidateJwtIdentity(token); req.auth = { authMode: AuthMode.JWT, user, userId: user.id, tokenVersionId, actor }; break; } - case AuthMode.SERVICE_TOKEN: + case AuthMode.IDENTITY_ACCESS_TOKEN: { + const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken( + token, + req.realIp + ); + req.auth = { + authMode: AuthMode.IDENTITY_ACCESS_TOKEN, + actor, + identityId: identity.identityId, + identityName: identity.name + }; break; - case AuthMode.SERVICE_ACCESS_TOKEN: + } + case AuthMode.SERVICE_TOKEN: { + const serviceToken = await server.services.serviceToken.fnValidateServiceToken( + token as string + ); + req.auth = { + authMode: AuthMode.SERVICE_TOKEN as const, + serviceToken, + serviceTokenId: serviceToken.id, + actor + }; break; - case AuthMode.API_KEY: - break; - case AuthMode.API_KEY_V2: + } + case AuthMode.API_KEY: { + const user = await server.services.apiKey.fnValidateApiKey(token as string); + req.auth = { authMode: AuthMode.API_KEY as const, userId: user.id, actor, user }; break; + } default: throw new UnauthorizedError({ name: "Unknown token strategy" }); } diff --git a/backend-pg/src/server/plugins/auth/inject-permission.ts b/backend-pg/src/server/plugins/auth/inject-permission.ts index 90919dd35..410621611 100644 --- a/backend-pg/src/server/plugins/auth/inject-permission.ts +++ b/backend-pg/src/server/plugins/auth/inject-permission.ts @@ -10,6 +10,10 @@ export const injectPermission = fp(async (server) => { if (req.auth.actor === ActorType.USER) { req.permission = { type: ActorType.USER, id: req.auth.userId }; + } else if (req.auth.actor === ActorType.IDENTITY) { + req.permission = { type: ActorType.IDENTITY, id: req.auth.identityId }; + } else if (req.auth.actor === ActorType.SERVICE) { + req.permission = { type: ActorType.SERVICE, id: req.auth.serviceTokenId }; } }); }); diff --git a/backend-pg/src/server/routes/index.ts b/backend-pg/src/server/routes/index.ts index ec6fbf9bf..f15f49863 100644 --- a/backend-pg/src/server/routes/index.ts +++ b/backend-pg/src/server/routes/index.ts @@ -169,7 +169,12 @@ export const registerRoutes = async ( const gitAppOrgDal = gitAppDalFactory(db); const secretScanningDal = secretScanningDalFactory(db); - const permissionService = permissionServiceFactory({ permissionDal, orgRoleDal, projectRoleDal }); + const permissionService = permissionServiceFactory({ + permissionDal, + orgRoleDal, + projectRoleDal, + serviceTokenDal + }); const auditLogQueue = auditLogQueueServiceFactory({ auditLogDal, queueService }); const auditLogService = auditLogServiceFactory({ auditLogDal, permissionService, auditLogQueue }); const sapService = secretApprovalPolicyServiceFactory({ @@ -187,7 +192,7 @@ export const registerRoutes = async ( samlConfigDal }); - const tokenService = tokenServiceFactory({ tokenDal: authTokenDal }); + const tokenService = tokenServiceFactory({ tokenDal: authTokenDal, userDal }); const userService = userServiceFactory({ userDal }); const loginService = authLoginServiceFactory({ userDal, smtpService, tokenService }); const passwordService = authPaswordServiceFactory({ @@ -220,7 +225,7 @@ export const registerRoutes = async ( authService: loginService, serverCfgDal: superAdminDal }); - const apiKeyService = apiKeyServiceFactory({ apiKeyDal }); + const apiKeyService = apiKeyServiceFactory({ apiKeyDal, userDal }); const secretScanningQueue = secretScanningQueueFactory({ userDal, @@ -425,7 +430,7 @@ export const registerRoutes = async ( user: userDal }); - await server.register(injectIdentity); + await server.register(injectIdentity, { userDal, serviceTokenDal }); await server.register(injectPermission); await server.register(injectAuditLogInfo); diff --git a/backend-pg/src/server/routes/v1/auth-router.ts b/backend-pg/src/server/routes/v1/auth-router.ts index e40e015c6..29f2e5bc5 100644 --- a/backend-pg/src/server/routes/v1/auth-router.ts +++ b/backend-pg/src/server/routes/v1/auth-router.ts @@ -24,7 +24,9 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => { onRequest: verifyAuth([AuthMode.JWT]), handler: async (req, res) => { const appCfg = getConfig(); - await server.services.login.logout(req.auth.userId, req.auth.tokenVersionId); + if (req.auth.authMode === AuthMode.JWT) { + await server.services.login.logout(req.auth.userId, req.auth.tokenVersionId); + } res.cookie("jid", "", { httpOnly: true, path: "/", @@ -35,6 +37,20 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => { } }); + server.route({ + url: "/checkAuth", + method: "POST", + schema: { + response: { + 200: z.object({ + message: z.literal("Authenticated") + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: () => ({ message: "Authenticated" as const }) + }); + server.route({ url: "/token", method: "POST", diff --git a/backend-pg/src/server/routes/v1/project-env-router.ts b/backend-pg/src/server/routes/v1/project-env-router.ts index 59c314118..44be1a3d6 100644 --- a/backend-pg/src/server/routes/v1/project-env-router.ts +++ b/backend-pg/src/server/routes/v1/project-env-router.ts @@ -25,7 +25,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const environment = await server.services.projectEnv.createEnvironment({ actorId: req.permission.id, @@ -74,7 +74,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { environment, old } = await server.services.projectEnv.updateEnvironment({ actorId: req.permission.id, @@ -124,7 +124,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const environment = await server.services.projectEnv.deleteEnvironment({ actorId: req.permission.id, diff --git a/backend-pg/src/server/routes/v1/project-membership-router.ts b/backend-pg/src/server/routes/v1/project-membership-router.ts index 99d680c2c..bacfcb1bb 100644 --- a/backend-pg/src/server/routes/v1/project-membership-router.ts +++ b/backend-pg/src/server/routes/v1/project-membership-router.ts @@ -11,8 +11,6 @@ import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; export const registerProjectMembershipRouter = async (server: FastifyZodProvider) => { - // TODO(akhilmhdh-pg): missing adding multiple user workspace refer v2/membership - server.route({ url: "/:workspaceId/memberships", method: "GET", @@ -37,7 +35,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const memberships = await server.services.projectMembership.getProjectMemberships({ actorId: req.permission.id, @@ -72,7 +70,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const data = await server.services.projectMembership.addUsersToProject({ actorId: req.permission.id, @@ -114,7 +112,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const membership = await server.services.projectMembership.updateProjectMembership({ actorId: req.permission.id, @@ -155,7 +153,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider }) } }, - onRequest: verifyAuth([AuthMode.JWT]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const membership = await server.services.projectMembership.deleteProjectMembership({ actorId: req.permission.id, diff --git a/backend-pg/src/server/routes/v1/project-router.ts b/backend-pg/src/server/routes/v1/project-router.ts index 120428610..6c226ba3b 100644 --- a/backend-pg/src/server/routes/v1/project-router.ts +++ b/backend-pg/src/server/routes/v1/project-router.ts @@ -17,6 +17,7 @@ import { sanitizedServiceTokenSchema } from "../v2/service-token-router"; const projectWithEnv = ProjectsSchema.merge( z.object({ + _id: z.string(), environments: z.object({ name: z.string(), slug: z.string(), id: z.string() }).array() }) ); diff --git a/backend-pg/src/server/routes/v1/secret-folder-router.ts b/backend-pg/src/server/routes/v1/secret-folder-router.ts index d267b8d65..fd9798dac 100644 --- a/backend-pg/src/server/routes/v1/secret-folder-router.ts +++ b/backend-pg/src/server/routes/v1/secret-folder-router.ts @@ -11,10 +11,12 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => method: "POST", schema: { body: z.object({ - projectId: z.string().trim(), + workspaceId: z.string().trim(), environment: z.string().trim(), name: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/"), + // backward compatiability with cli + directory: z.string().trim().default("/") }), response: { 200: z.object({ @@ -22,23 +24,31 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { + const path = req.body.path || req.body.directory; const folder = await server.services.folder.createFolder({ actorId: req.permission.id, actor: req.permission.type, - ...req.body + ...req.body, + projectId: req.body.workspaceId, + path }); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - projectId: req.body.projectId, + projectId: req.body.workspaceId, event: { type: EventType.CREATE_FOLDER, metadata: { environment: req.body.environment, folderId: folder.id, folderName: folder.name, - folderPath: req.body.path + folderPath: path } } }); @@ -51,13 +61,16 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => method: "PATCH", schema: { params: z.object({ + // old way this was name folderId: z.string() }), body: z.object({ - projectId: z.string().trim(), + workspaceId: z.string().trim(), environment: z.string().trim(), name: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/"), + // backward compatiability with cli + directory: z.string().trim().default("/") }), response: { 200: z.object({ @@ -65,23 +78,31 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { + const path = req.body.path || req.body.directory; const { folder, old } = await server.services.folder.updateFolder({ actorId: req.permission.id, actor: req.permission.type, ...req.body, - id: req.params.folderId + projectId: req.body.workspaceId, + id: req.params.folderId, + path }); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - projectId: req.body.projectId, + projectId: req.body.workspaceId, event: { type: EventType.UPDATE_FOLDER, metadata: { environment: req.body.environment, folderId: folder.id, - folderPath: req.body.path, + folderPath: path, newFolderName: folder.name, oldFolderName: old.name } @@ -99,9 +120,11 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => folderId: z.string() }), body: z.object({ - projectId: z.string().trim(), + workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/"), + // keep this here as cli need directory + directory: z.string().trim().default("/") }), response: { 200: z.object({ @@ -109,23 +132,31 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { + const path = req.body.path || req.body.directory; const folder = await server.services.folder.deleteFolder({ actorId: req.permission.id, actor: req.permission.type, ...req.body, - id: req.params.folderId + projectId: req.body.workspaceId, + id: req.params.folderId, + path }); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - projectId: req.body.projectId, + projectId: req.body.workspaceId, event: { type: EventType.DELETE_FOLDER, metadata: { environment: req.body.environment, folderId: folder.id, - folderPath: req.body.path, + folderPath: path, folderName: folder.name } } @@ -139,9 +170,11 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => method: "GET", schema: { querystring: z.object({ - projectId: z.string().trim(), + workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/"), + // backward compatiability with cli + directory: z.string().trim().default("/") }), response: { 200: z.object({ @@ -149,12 +182,20 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { + const path = req.query.path || req.query.directory; const folders = await server.services.folder.getFolders({ actorId: req.permission.id, actor: req.permission.type, - ...req.query + ...req.query, + projectId: req.query.workspaceId, + path }); return { folders }; } diff --git a/backend-pg/src/server/routes/v1/secret-import-router.ts b/backend-pg/src/server/routes/v1/secret-import-router.ts index d816251ee..50b0ede3b 100644 --- a/backend-pg/src/server/routes/v1/secret-import-router.ts +++ b/backend-pg/src/server/routes/v1/secret-import-router.ts @@ -11,7 +11,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => method: "POST", schema: { body: z.object({ - projectId: z.string().trim(), + workspaceId: z.string().trim(), environment: z.string().trim(), path: z.string().trim().default("/"), import: z.object({ @@ -30,18 +30,24 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const secretImport = await server.services.secretImport.createImport({ actorId: req.permission.id, actor: req.permission.type, ...req.body, + projectId: req.body.workspaceId, data: req.body.import }); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - projectId: req.body.projectId, + projectId: req.body.workspaceId, event: { type: EventType.CREATE_SECRET_IMPORT, metadata: { @@ -66,7 +72,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => secretImportId: z.string().trim() }), body: z.object({ - projectId: z.string().trim(), + workspaceId: z.string().trim(), environment: z.string().trim(), path: z.string().trim().default("/"), import: z.object({ @@ -86,19 +92,25 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const secretImport = await server.services.secretImport.updateImport({ actorId: req.permission.id, actor: req.permission.type, id: req.params.secretImportId, ...req.body, + projectId: req.body.workspaceId, data: req.body.import }); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - projectId: req.body.projectId, + projectId: req.body.workspaceId, event: { type: EventType.UPDATE_SECRET_IMPORT, metadata: { @@ -123,7 +135,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => secretImportId: z.string().trim() }), body: z.object({ - projectId: z.string().trim(), + workspaceId: z.string().trim(), environment: z.string().trim(), path: z.string().trim().default("/") }), @@ -138,18 +150,24 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const secretImport = await server.services.secretImport.deleteImport({ actorId: req.permission.id, actor: req.permission.type, id: req.params.secretImportId, - ...req.body + ...req.body, + projectId: req.body.workspaceId }); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - projectId: req.body.projectId, + projectId: req.body.workspaceId, event: { type: EventType.DELETE_SECRET_IMPORT, metadata: { @@ -171,7 +189,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => method: "GET", schema: { querystring: z.object({ - projectId: z.string().trim(), + workspaceId: z.string().trim(), environment: z.string().trim(), path: z.string().trim().default("/") }), @@ -188,17 +206,23 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const secretImports = await server.services.secretImport.getImports({ actorId: req.permission.id, actor: req.permission.type, - ...req.query + ...req.query, + projectId: req.query.workspaceId }); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, - projectId: req.query.projectId, + projectId: req.query.workspaceId, event: { type: EventType.GET_SECRET_IMPORTS, metadata: { @@ -217,7 +241,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => method: "GET", schema: { querystring: z.object({ - projectId: z.string().trim(), + workspaceId: z.string().trim(), environment: z.string().trim(), path: z.string().trim().default("/") }), @@ -238,12 +262,18 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const importedSecrets = await server.services.secretImport.getSecretsFromImports({ actorId: req.permission.id, actor: req.permission.type, - ...req.query + ...req.query, + projectId: req.query.workspaceId }); return { secrets: importedSecrets }; } diff --git a/backend-pg/src/server/routes/v2/organization-router.ts b/backend-pg/src/server/routes/v2/organization-router.ts index 7343216df..4bf6dd8a5 100644 --- a/backend-pg/src/server/routes/v2/organization-router.ts +++ b/backend-pg/src/server/routes/v2/organization-router.ts @@ -34,7 +34,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const users = await server.services.org.findAllOrgMembers( req.auth.userId, @@ -58,7 +58,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const membership = await server.services.org.updateOrgMembership({ userId: req.auth.userId, @@ -81,7 +81,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const membership = await server.services.org.deleteOrgMembership({ userId: req.auth.userId, diff --git a/backend-pg/src/server/routes/v3/secret-router.ts b/backend-pg/src/server/routes/v3/secret-router.ts index 03925b338..0cbc683e3 100644 --- a/backend-pg/src/server/routes/v3/secret-router.ts +++ b/backend-pg/src/server/routes/v3/secret-router.ts @@ -42,7 +42,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const secrets = await server.services.secret.getSecrets({ actorId: req.permission.id, @@ -92,7 +97,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const secret = await server.services.secret.getASecret({ actorId: req.permission.id, @@ -157,7 +167,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { ]) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const { workspaceId: projectId, @@ -307,7 +322,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { ]) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const { secretValueCiphertext, @@ -452,7 +472,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { ]) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const { secretPath, type, workspaceId: projectId, secretId, environment } = req.body; if (req.body.type !== SecretType.Personal && req.permission.type === ActorType.USER) { @@ -564,7 +589,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { ]) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const { environment, workspaceId: projectId, secretPath, secrets: inputSecrets } = req.body; if (req.permission.type === ActorType.USER) { @@ -672,7 +702,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { ]) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const { environment, workspaceId: projectId, secretPath, secrets: inputSecrets } = req.body; if (req.permission.type === ActorType.USER) { @@ -768,7 +803,12 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { ]) } }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + onRequest: verifyAuth([ + AuthMode.JWT, + AuthMode.API_KEY, + AuthMode.SERVICE_TOKEN, + AuthMode.IDENTITY_ACCESS_TOKEN + ]), handler: async (req) => { const { environment, workspaceId: projectId, secretPath, secrets: inputSecrets } = req.body; if (req.permission.type === ActorType.USER) { diff --git a/backend-pg/src/services/api-key/api-key-service.ts b/backend-pg/src/services/api-key/api-key-service.ts index 2cafd6508..5a8300ca1 100644 --- a/backend-pg/src/services/api-key/api-key-service.ts +++ b/backend-pg/src/services/api-key/api-key-service.ts @@ -4,19 +4,21 @@ import bcrypt from "bcrypt"; import { TApiKeys } from "@app/db/schemas/api-keys"; import { getConfig } from "@app/lib/config/env"; -import { BadRequestError } from "@app/lib/errors"; +import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; +import { TUserDalFactory } from "../user/user-dal"; import { TApiKeyDalFactory } from "./api-key-dal"; type TApiKeyServiceFactoryDep = { apiKeyDal: TApiKeyDalFactory; + userDal: Pick; }; export type TApiKeyServiceFactory = ReturnType; const formatApiKey = ({ secretHash, ...data }: TApiKeys) => data; -export const apiKeyServiceFactory = ({ apiKeyDal }: TApiKeyServiceFactoryDep) => { +export const apiKeyServiceFactory = ({ apiKeyDal, userDal }: TApiKeyServiceFactoryDep) => { const getMyApiKeys = async (userId: string) => { const apiKeys = await apiKeyDal.find({ userId }); return apiKeys.map((key) => formatApiKey(key)); @@ -48,9 +50,27 @@ export const apiKeyServiceFactory = ({ apiKeyDal }: TApiKeyServiceFactoryDep) => return formatApiKey(apiKeyData); }; + const fnValidateApiKey = async (token: string) => { + const [, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>token.split(".", 3); + const apiKey = await apiKeyDal.findById(TOKEN_IDENTIFIER); + if (!apiKey) throw new UnauthorizedError(); + + if (apiKey.expiresAt && new Date(apiKey.expiresAt) < new Date()) { + await apiKeyDal.deleteById(apiKey.id); + throw new UnauthorizedError(); + } + + const isMatch = await bcrypt.compare(TOKEN_SECRET, apiKey.secretHash); + if (!isMatch) throw new UnauthorizedError(); + await apiKeyDal.updateById(apiKey.id, { lastUsed: new Date() }); + const user = await userDal.findById(apiKey.userId); + return user; + }; + return { getMyApiKeys, createApiKey, - deleteApiKey + deleteApiKey, + fnValidateApiKey }; }; diff --git a/backend-pg/src/services/auth-token/auth-token-service.ts b/backend-pg/src/services/auth-token/auth-token-service.ts index 42940feb7..fb69ffad3 100644 --- a/backend-pg/src/services/auth-token/auth-token-service.ts +++ b/backend-pg/src/services/auth-token/auth-token-service.ts @@ -4,7 +4,10 @@ import bcrypt from "bcrypt"; import { TAuthTokens, TAuthTokenSessions } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; +import { UnauthorizedError } from "@app/lib/errors"; +import { AuthModeJwtTokenPayload } from "../auth/auth-type"; +import { TUserDalFactory } from "../user/user-dal"; import { TTokenDalFactory } from "./auth-token-dal"; import { TCreateTokenForUserDTO, @@ -15,7 +18,7 @@ import { type TAuthTokenServiceFactoryDep = { tokenDal: TTokenDalFactory; - // adjust the expiry from env through here + userDal: Pick; }; export type TAuthTokenServiceFactory = ReturnType; @@ -56,7 +59,7 @@ export const getTokenConfig = (tokenType: TokenType) => { } }; -export const tokenServiceFactory = ({ tokenDal }: TAuthTokenServiceFactoryDep) => { +export const tokenServiceFactory = ({ tokenDal, userDal }: TAuthTokenServiceFactoryDep) => { const createTokenForUser = async ({ type, userId, orgId }: TCreateTokenForUserDTO) => { const { token, ...tkCfg } = getTokenConfig(type); const appCfg = getConfig(); @@ -122,26 +125,43 @@ export const tokenServiceFactory = ({ tokenDal }: TAuthTokenServiceFactoryDep) = return session; }; - const getUserTokenSessionById = async (id: string, userId: string) => - tokenDal.findOneTokenSession({ id, userId }); - const clearTokenSessionById = async ( userId: string, sessionId: string ): Promise => tokenDal.incrementTokenSessionVersion(userId, sessionId); + const getUserTokenSessionById = async (id: string, userId: string) => + tokenDal.findOneTokenSession({ id, userId }); + const getTokenSessionByUser = async (userId: string) => tokenDal.findTokenSessions({ userId }); const revokeAllMySessions = async (userId: string) => tokenDal.deleteTokenSession({ userId }); + // to parse jwt identity in inject identity plugin + const fnValidateJwtIdentity = async (token: AuthModeJwtTokenPayload) => { + const session = await tokenDal.findOneTokenSession({ + id: token.tokenVersionId, + userId: token.userId + }); + if (!session) throw new UnauthorizedError({ name: "Session not found" }); + if (token.accessVersion !== session.accessVersion) + throw new UnauthorizedError({ name: "Stale session" }); + + const user = await userDal.findById(session.userId); + if (!user || !user.isAccepted) throw new UnauthorizedError({ name: "Token user not found" }); + + return { user, tokenVersionId: token.tokenVersionId }; + }; + return { createTokenForUser, validateTokenForUser, getUserTokenSession, clearTokenSessionById, - getUserTokenSessionById, getTokenSessionByUser, - revokeAllMySessions + revokeAllMySessions, + fnValidateJwtIdentity, + getUserTokenSessionById }; }; diff --git a/backend-pg/src/services/auth/auth-type.ts b/backend-pg/src/services/auth/auth-type.ts index a23bf911f..f1dfa993c 100644 --- a/backend-pg/src/services/auth/auth-type.ts +++ b/backend-pg/src/services/auth/auth-type.ts @@ -23,9 +23,7 @@ export enum AuthTokenType { export enum AuthMode { JWT = "jwt", SERVICE_TOKEN = "serviceToken", - SERVICE_ACCESS_TOKEN = "serviceAccessToken", API_KEY = "apiKey", - API_KEY_V2 = "apiKeyV2", IDENTITY_ACCESS_TOKEN = "identityAccessToken" } diff --git a/backend-pg/src/services/identity-access-token/identity-access-token-dal.ts b/backend-pg/src/services/identity-access-token/identity-access-token-dal.ts index dd1bf511e..4f9b938b8 100644 --- a/backend-pg/src/services/identity-access-token/identity-access-token-dal.ts +++ b/backend-pg/src/services/identity-access-token/identity-access-token-dal.ts @@ -1,10 +1,45 @@ +import { Knex } from "knex"; + import { TDbClient } from "@app/db"; -import { TableName } from "@app/db/schemas"; -import { ormify } from "@app/lib/knex"; +import { TableName,TIdentityAccessTokens } from "@app/db/schemas"; +import { DatabaseError } from "@app/lib/errors"; +import { ormify, selectAllTableCols } from "@app/lib/knex"; export type TIdentityAccessTokenDalFactory = ReturnType; export const identityAccessTokenDalFactory = (db: TDbClient) => { const identityAccessTokenOrm = ormify(db, TableName.IdentityAccessToken); - return identityAccessTokenOrm; + + const findOne = async (filter: Partial, tx?: Knex) => { + try { + const doc = await (tx || db)(TableName.IdentityAccessToken) + .where(filter) + .join( + TableName.Identity, + `${TableName.Identity}.id`, + `${TableName.IdentityAccessToken}.identityId` + ) + .leftJoin( + TableName.IdentityUaClientSecret, + `${TableName.IdentityAccessToken}.identityUAClientSecretId`, + `${TableName.IdentityUaClientSecret}.id` + ) + .leftJoin( + TableName.IdentityUniversalAuth, + `${TableName.IdentityUaClientSecret}.identityUAId`, + `${TableName.IdentityUniversalAuth}.id` + ) + .select(selectAllTableCols(TableName.IdentityAccessToken)) + .select( + db.ref("accessTokenTrustedIps").withSchema(TableName.IdentityUniversalAuth), + db.ref("name").withSchema(TableName.Identity) + ) + .first(); + return doc; + } catch (error) { + throw new DatabaseError({ error, name: "IdAccessTokenFindOne" }); + } + }; + + return { ...identityAccessTokenOrm, findOne }; }; diff --git a/backend-pg/src/services/identity-access-token/identity-access-token-service.ts b/backend-pg/src/services/identity-access-token/identity-access-token-service.ts index 4e4d910f3..3802ac28d 100644 --- a/backend-pg/src/services/identity-access-token/identity-access-token-service.ts +++ b/backend-pg/src/services/identity-access-token/identity-access-token-service.ts @@ -1,11 +1,16 @@ import jwt, { JwtPayload } from "jsonwebtoken"; +import { TableName, TIdentityAccessTokens } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; -import { UnauthorizedError } from "@app/lib/errors"; +import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; +import { checkIPAgainstBlocklist, TIp } from "@app/lib/ip"; import { AuthTokenType } from "../auth/auth-type"; import { TIdentityAccessTokenDalFactory } from "./identity-access-token-dal"; -import { TRenewAccessTokenDTO } from "./identity-access-token-types"; +import { + TIdentityAccessTokenJwtPayload, + TRenewAccessTokenDTO +} from "./identity-access-token-types"; type TIdentityAccessTokenServiceFactoryDep = { identityAccessTokenDal: TIdentityAccessTokenDalFactory; @@ -18,25 +23,22 @@ export type TIdentityAccessTokenServiceFactory = ReturnType< export const identityAccessTokenServiceFactory = ({ identityAccessTokenDal }: TIdentityAccessTokenServiceFactoryDep) => { - const renewAccessToken = async ({ accessToken }: TRenewAccessTokenDTO) => { - const appCfg = getConfig(); - - const decodedToken = jwt.verify(accessToken, appCfg.JWT_AUTH_SECRET) as JwtPayload; - if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) - throw new UnauthorizedError(); - - const identityAccessToken = await identityAccessTokenDal.findOne({ - id: decodedToken.identityAccessTokenId, - isAccessTokenRevoked: false - }); - if (!identityAccessToken) throw new UnauthorizedError(); - + const validateAccessTokenExp = async (identityAccessToken: TIdentityAccessTokens) => { const { accessTokenTTL, + accessTokenNumUses, + accessTokenNumUsesLimit, accessTokenLastRenewedAt, accessTokenMaxTTL, createdAt: accessTokenCreatedAt } = identityAccessToken; + + if (accessTokenNumUses > 0 && accessTokenNumUses >= accessTokenNumUsesLimit) { + throw new BadRequestError({ + message: "Unable to renew because access token number of uses limit reached" + }); + } + // ttl check if (accessTokenTTL > 0) { const currentDate = new Date(); @@ -81,6 +83,22 @@ export const identityAccessTokenServiceFactory = ({ message: "Failed to renew MI access token past its Max TTL expiration" }); } + }; + + const renewAccessToken = async ({ accessToken }: TRenewAccessTokenDTO) => { + const appCfg = getConfig(); + + const decodedToken = jwt.verify(accessToken, appCfg.JWT_AUTH_SECRET) as JwtPayload; + if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) + throw new UnauthorizedError(); + + const identityAccessToken = await identityAccessTokenDal.findOne({ + [`${TableName.IdentityAccessToken}.id` as "id"]: decodedToken.identityAccessTokenId, + isAccessTokenRevoked: false + }); + if (!identityAccessToken) throw new UnauthorizedError(); + + validateAccessTokenExp(identityAccessToken); const updatedIdentityAccessToken = await identityAccessTokenDal.updateById( identityAccessToken.id, @@ -92,5 +110,26 @@ export const identityAccessTokenServiceFactory = ({ return { accessToken, identityAccessToken: updatedIdentityAccessToken }; }; - return { renewAccessToken }; + const fnValidateIdentityAccessToken = async ( + token: TIdentityAccessTokenJwtPayload, + ipAddress?: string + ) => { + const identityAccessToken = await identityAccessTokenDal.findOne({ + [`${TableName.IdentityAccessToken}.id` as "id"]: token.identityAccessTokenId, + isAccessTokenRevoked: false + }); + if (!identityAccessToken) throw new UnauthorizedError(); + + if (ipAddress) { + checkIPAgainstBlocklist({ + ipAddress, + trustedIps: identityAccessToken?.accessTokenTrustedIps as TIp[] + }); + } + + validateAccessTokenExp(identityAccessToken); + return identityAccessToken; + }; + + return { renewAccessToken, fnValidateIdentityAccessToken }; }; diff --git a/backend-pg/src/services/identity-access-token/identity-access-token-types.ts b/backend-pg/src/services/identity-access-token/identity-access-token-types.ts index 8b80c37c7..86967df76 100644 --- a/backend-pg/src/services/identity-access-token/identity-access-token-types.ts +++ b/backend-pg/src/services/identity-access-token/identity-access-token-types.ts @@ -1,3 +1,10 @@ export type TRenewAccessTokenDTO = { accessToken: string; }; + +export type TIdentityAccessTokenJwtPayload = { + identityId: string; + clientSecretId: string; + identityAccessTokenId: string; + authTokenType: string; +}; diff --git a/backend-pg/src/services/identity-ua/identity-ua-service.ts b/backend-pg/src/services/identity-ua/identity-ua-service.ts index 26427c709..fa878a12a 100644 --- a/backend-pg/src/services/identity-ua/identity-ua-service.ts +++ b/backend-pg/src/services/identity-ua/identity-ua-service.ts @@ -19,6 +19,7 @@ import { ActorType, AuthTokenType } from "../auth/auth-type"; import { TIdentityDalFactory } from "../identity/identity-dal"; import { TIdentityOrgDalFactory } from "../identity/identity-org-dal"; import { TIdentityAccessTokenDalFactory } from "../identity-access-token/identity-access-token-dal"; +import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TIdentityUaClientSecretDalFactory } from "./identity-ua-client-secret-dal"; import { TIdentityUaDalFactory } from "./identity-ua-dal"; import { @@ -123,7 +124,7 @@ export const identityUaServiceFactory = ({ clientSecretId: validClientSecretInfo.id, identityAccessTokenId: identityAccessToken.id, authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN - }, + } as TIdentityAccessTokenJwtPayload, appCfg.JWT_AUTH_SECRET, { expiresIn: diff --git a/backend-pg/src/services/project/project-dal.ts b/backend-pg/src/services/project/project-dal.ts index 43c94012b..888eae582 100644 --- a/backend-pg/src/services/project/project-dal.ts +++ b/backend-pg/src/services/project/project-dal.ts @@ -1,16 +1,14 @@ import { TDbClient } from "@app/db"; -import { TableName, TProjects } from "@app/db/schemas"; +import { ProjectsSchema, TableName } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { mergeOneToManyRelation, ormify } from "@app/lib/knex"; +import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex"; export type TProjectDalFactory = ReturnType; export const projectDalFactory = (db: TDbClient) => { const projectOrm = ormify(db, TableName.Project); - const findAllProjects = async ( - userId: string - ): Promise<(TProjects & { environments: { id: string; slug: string; name: string }[] })[]> => { + const findAllProjects = async (userId: string) => { try { const workspaces = await db(TableName.ProjectMembership) .where({ userId }) @@ -25,34 +23,35 @@ export const projectDalFactory = (db: TDbClient) => { `${TableName.Project}.id` ) .select( - db.ref("id").withSchema(TableName.Project), - db.ref("name").withSchema(TableName.Project), - db.ref("autoCapitalization").withSchema(TableName.Project), - db.ref("orgId").withSchema(TableName.Project), - db.ref("createdAt").withSchema(TableName.Project), - db.ref("updatedAt").withSchema(TableName.Project), + selectAllTableCols(TableName.Project), + db.ref("id").withSchema(TableName.Project).as("_id"), db.ref("id").withSchema(TableName.Environment).as("envId"), db.ref("slug").withSchema(TableName.Environment).as("envSlug"), db.ref("name").withSchema(TableName.Environment).as("envName") ) .orderBy("createdAt", "asc", "last"); - return mergeOneToManyRelation( - workspaces, - "id", - ({ envId, envSlug, envName, ...data }) => data, - ({ envName, envSlug, envId }) => ({ id: envId, slug: envSlug, name: envName }), - "environments" - ); + return sqlNestRelationships({ + data: workspaces, + key: "id", + parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }), + childrenMapper: [ + { + key: "envId", + label: "environments" as const, + mapper: ({ envId: id, envSlug: slug, envName: name }) => ({ + id, + slug, + name + }) + } + ] + }); } catch (error) { throw new DatabaseError({ error, name: "Find all projects" }); } }; - const findProjectById = async ( - id: string - ): Promise< - (TProjects & { environments: { id: string; slug: string; name: string }[] }) | undefined - > => { + const findProjectById = async (id: string) => { try { const workspaces = await db(TableName.ProjectMembership) .where(`${TableName.Project}.id`, id) @@ -67,24 +66,28 @@ export const projectDalFactory = (db: TDbClient) => { `${TableName.Project}.id` ) .select( - db.ref("id").withSchema(TableName.Project), - db.ref("name").withSchema(TableName.Project), - db.ref("autoCapitalization").withSchema(TableName.Project), - db.ref("orgId").withSchema(TableName.Project), - db.ref("createdAt").withSchema(TableName.Project), - db.ref("updatedAt").withSchema(TableName.Project), + selectAllTableCols(TableName.Project), + db.ref("id").withSchema(TableName.Project).as("_id"), db.ref("id").withSchema(TableName.Environment).as("envId"), db.ref("slug").withSchema(TableName.Environment).as("envSlug"), db.ref("name").withSchema(TableName.Environment).as("envName") ); - const [doc] = mergeOneToManyRelation( - workspaces, - "id", - ({ envId, envSlug, envName, ...data }) => data, - ({ envName, envSlug, envId }) => ({ id: envId, slug: envSlug, name: envName }), - "environments" - ); - return doc; + return sqlNestRelationships({ + data: workspaces, + key: "id", + parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }), + childrenMapper: [ + { + key: "envId", + label: "environments" as const, + mapper: ({ envId, envSlug: slug, envName: name }) => ({ + id: envId, + slug, + name + }) + } + ] + })?.[0]; } catch (error) { throw new DatabaseError({ error, name: "Find all projects" }); } diff --git a/backend-pg/src/services/project/project-service.ts b/backend-pg/src/services/project/project-service.ts index febf29502..886612ecb 100644 --- a/backend-pg/src/services/project/project-service.ts +++ b/backend-pg/src/services/project/project-service.ts @@ -90,7 +90,8 @@ export const projectServiceFactory = ({ envs.map(({ id }) => ({ name: ROOT_FOLDER_NAME, envId: id, version: 1 })), tx ); - return { ...project, environments: envs }; + // _id for backward compat + return { ...project, environments: envs, _id: project.id }; }); return newProject; diff --git a/backend-pg/src/services/secret-folder/secret-folder-service.ts b/backend-pg/src/services/secret-folder/secret-folder-service.ts index 690e3da87..080a5303f 100644 --- a/backend-pg/src/services/secret-folder/secret-folder-service.ts +++ b/backend-pg/src/services/secret-folder/secret-folder-service.ts @@ -97,12 +97,17 @@ export const secretFolderServiceFactory = ({ const env = await projectEnvDal.findOne({ projectId, slug: environment }); if (!env) throw new BadRequestError({ message: "Environment not found", name: "Update folder" }); - const folder = await folderDal.findOne({ envId: env.id, id, parentId: parentFolder.id }); + let folder = await folderDal.findOne({ envId: env.id, id, parentId: parentFolder.id }); + // now folder api accepts id based change + // this is for cli and when cli removes this will remove this logic + if (!folder) { + folder = await folderDal.findOne({ envId: env.id, name: id, parentId: parentFolder.id }); + } if (!folder) throw new BadRequestError({ message: "Folder not found" }); const newFolder = await folderDal.transaction(async (tx) => { const [doc] = await folderDal.update( - { envId: env.id, id, parentId: parentFolder.id }, + { envId: env.id, id: folder.id, parentId: parentFolder.id }, { name }, tx ); diff --git a/backend-pg/src/services/service-token/service-token-service.ts b/backend-pg/src/services/service-token/service-token-service.ts index 4e1337d22..fd5aeb0bd 100644 --- a/backend-pg/src/services/service-token/service-token-service.ts +++ b/backend-pg/src/services/service-token/service-token-service.ts @@ -9,7 +9,7 @@ import { ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; -import { BadRequestError } from "@app/lib/errors"; +import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { ActorType } from "../auth/auth-type"; import { TProjectEnvDalFactory } from "../project-env/project-env-dal"; @@ -130,10 +130,29 @@ export const serviceTokenServiceFactory = ({ return tokens; }; + const fnValidateServiceToken = async (token: string) => { + const [, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>token.split(".", 3); + const serviceToken = await serviceTokenDal.findById(TOKEN_IDENTIFIER); + if (!serviceToken) throw new UnauthorizedError(); + + if (serviceToken.expiresAt && new Date(serviceToken.expiresAt) < new Date()) { + await serviceTokenDal.deleteById(serviceToken.id); + throw new UnauthorizedError({ message: "failed to authenticate expired service token" }); + } + + const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceToken.secretHash); + if (!isMatch) throw new UnauthorizedError(); + const updatedToken = await serviceTokenDal.updateById(serviceToken.id, { + lastUsed: new Date() + }); + return updatedToken; + }; + return { createServiceToken, deleteServiceToken, getServiceToken, - getProjectServiceTokens + getProjectServiceTokens, + fnValidateServiceToken }; }; diff --git a/backend/src/utils/authn/helpers/index.ts b/backend/src/utils/authn/helpers/index.ts index 45849c989..d3df5ffa5 100644 --- a/backend/src/utils/authn/helpers/index.ts +++ b/backend/src/utils/authn/helpers/index.ts @@ -69,19 +69,21 @@ export const extractAuthMode = async ({ return { authMode: AuthMode.SERVICE_TOKEN, authTokenValue }; } - switch (decodedToken.authTokenType) { - case AuthTokenType.ACCESS_TOKEN: - return { authMode: AuthMode.JWT, authTokenValue }; - case AuthTokenType.API_KEY: - return { authMode: AuthMode.API_KEY_V2, authTokenValue }; - case AuthTokenType.IDENTITY_ACCESS_TOKEN: - return { authMode: AuthMode.IDENTITY_ACCESS_TOKEN, authTokenValue }; - default: - throw UnauthorizedRequestError({ - message: "Failed to authenticate unknown authentication method" - }); - } -} + const decodedToken = jwt.verify(authTokenValue, await getAuthSecret()); + + switch (decodedToken.authTokenType) { + case AuthTokenType.ACCESS_TOKEN: + return { authMode: AuthMode.JWT, authTokenValue }; + case AuthTokenType.API_KEY: + return { authMode: AuthMode.API_KEY_V2, authTokenValue }; + case AuthTokenType.IDENTITY_ACCESS_TOKEN: + return { authMode: AuthMode.IDENTITY_ACCESS_TOKEN, authTokenValue }; + default: + throw UnauthorizedRequestError({ + message: "Failed to authenticate unknown authentication method" + }); + } +}; export const getAuthData = async ({ authMode, @@ -97,112 +99,9 @@ export const getAuthData = async ({ authTokenValue }); - switch (authMode) { - case AuthMode.SERVICE_TOKEN: { - const serviceTokenData = await validateServiceTokenV2({ - authTokenValue - }); - - return { - actor: { - type: ActorType.SERVICE, - metadata: { - serviceId: serviceTokenData._id.toString(), - name: serviceTokenData.name - } - }, - authPayload: serviceTokenData, - ipAddress, - userAgent, - userAgentType - } - } - case AuthMode.IDENTITY_ACCESS_TOKEN: { - const identity = await validateIdentity({ - authTokenValue, - ipAddress - }); - - return { - actor: { - type: ActorType.IDENTITY, - metadata: { - identityId: identity._id.toString(), - name: identity.name - } - }, - authPayload: identity, - ipAddress, - userAgent, - userAgentType - } - } - case AuthMode.API_KEY: { - const user = await validateAPIKey({ - authTokenValue - }); - - return { - actor: { - type: ActorType.USER, - metadata: { - userId: user._id.toString(), - email: user.email - } - }, - authPayload: user, - ipAddress, - userAgent, - userAgentType - } - } - case AuthMode.API_KEY_V2: { - const user = await validateAPIKeyV2({ - authTokenValue - }); - - return { - actor: { - type: ActorType.USER, - metadata: { - userId: user._id.toString(), - email: user.email - } - }, - authPayload: user, - ipAddress, - userAgent, - userAgentType - } - } - case AuthMode.JWT: { - const user = await validateJWT({ - authTokenValue - }); - - return { - actor: { - type: ActorType.USER, - metadata: { - userId: user._id.toString(), - email: user.email - } - }, - authPayload: user, - ipAddress, - userAgent, - userAgentType - } - } - } - case AuthMode.SERVICE_ACCESS_TOKEN: { - const serviceTokenData = await validateServiceTokenV3({ - authTokenValue - }); - return { actor: { - type: ActorType.SERVICE_V3, + type: ActorType.SERVICE, metadata: { serviceId: serviceTokenData._id.toString(), name: serviceTokenData.name @@ -214,6 +113,26 @@ export const getAuthData = async ({ userAgentType }; } + case AuthMode.IDENTITY_ACCESS_TOKEN: { + const identity = await validateIdentity({ + authTokenValue, + ipAddress + }); + + return { + actor: { + type: ActorType.IDENTITY, + metadata: { + identityId: identity._id.toString(), + name: identity.name + } + }, + authPayload: identity, + ipAddress, + userAgent, + userAgentType + }; + } case AuthMode.API_KEY: { const user = await validateAPIKey({ authTokenValue diff --git a/frontend/src/hooks/api/secretFolders/queries.tsx b/frontend/src/hooks/api/secretFolders/queries.tsx index 6abf6cc30..bcda2b0a4 100644 --- a/frontend/src/hooks/api/secretFolders/queries.tsx +++ b/frontend/src/hooks/api/secretFolders/queries.tsx @@ -24,10 +24,10 @@ export const folderQueryKeys = { ["secret-folders", { projectId, environment, path }] as const }; -const fetchProjectFolders = async (projectId: string, environment: string, path = "/") => { +const fetchProjectFolders = async (workspaceId: string, environment: string, path = "/") => { const { data } = await apiRequest.get<{ folders: TSecretFolder[] }>("/api/v1/folders", { params: { - projectId, + workspaceId, environment, path } @@ -102,7 +102,10 @@ export const useCreateFolder = () => { return useMutation<{}, {}, TCreateFolderDTO>({ mutationFn: async (dto) => { - const { data } = await apiRequest.post("/api/v1/folders", dto); + const { data } = await apiRequest.post("/api/v1/folders", { + ...dto, + workspaceId: dto.projectId + }); return data; }, onSuccess: (_, { projectId, environment, path }) => { @@ -127,7 +130,7 @@ export const useUpdateFolder = () => { const { data } = await apiRequest.patch(`/api/v1/folders/${folderId}`, { name, environment, - projectId, + workspaceId: projectId, path }); return data; @@ -154,7 +157,7 @@ export const useDeleteFolder = () => { const { data } = await apiRequest.delete(`/api/v1/folders/${folderId}`, { data: { environment, - projectId, + workspaceId: projectId, path } }); diff --git a/frontend/src/hooks/api/secretImports/mutation.tsx b/frontend/src/hooks/api/secretImports/mutation.tsx index ccde4dcc0..928322a3c 100644 --- a/frontend/src/hooks/api/secretImports/mutation.tsx +++ b/frontend/src/hooks/api/secretImports/mutation.tsx @@ -13,7 +13,7 @@ export const useCreateSecretImport = () => { const { data } = await apiRequest.post("/api/v1/secret-imports", { import: secretImport, environment, - projectId, + workspaceId: projectId, path }); return data; @@ -38,7 +38,7 @@ export const useUpdateSecretImport = () => { import: secretImports, environment, path, - projectId + workspaceId: projectId }); return data; }, @@ -60,7 +60,7 @@ export const useDeleteSecretImport = () => { mutationFn: async ({ id, projectId, path, environment }) => { const { data } = await apiRequest.delete(`/api/v1/secret-imports/${id}`, { data: { - projectId, + workspaceId: projectId, path, environment } diff --git a/frontend/src/hooks/api/secretImports/queries.tsx b/frontend/src/hooks/api/secretImports/queries.tsx index f64fbd47d..ee55e20f3 100644 --- a/frontend/src/hooks/api/secretImports/queries.tsx +++ b/frontend/src/hooks/api/secretImports/queries.tsx @@ -25,7 +25,7 @@ const fetchSecretImport = async ({ projectId, environment, path = "/" }: TGetSec "/api/v1/secret-imports", { params: { - projectId, + workspaceId: projectId, environment, path } @@ -66,7 +66,7 @@ const fetchImportedSecrets = async ( "/api/v1/secret-imports/secrets", { params: { - projectId: workspaceId, + workspaceId, environment, path: directory } diff --git a/frontend/src/hooks/api/workspace/index.tsx b/frontend/src/hooks/api/workspace/index.tsx index 6a678a432..63be0f7ad 100644 --- a/frontend/src/hooks/api/workspace/index.tsx +++ b/frontend/src/hooks/api/workspace/index.tsx @@ -18,8 +18,8 @@ export { useGetWorkspaceUsers, useNameWorkspaceSecrets, useRenameWorkspace, - useReorderWsEnvironment, useToggleAutoCapitalization, useUpdateIdentityWorkspaceRole, useUpdateUserWorkspaceRole, - useUpdateWsEnvironment} from "./queries"; + useUpdateWsEnvironment +} from "./queries";