diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index fd27d42c1..8ec62ef24 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,6 @@ # Description 📣 - + ## Type ✨ @@ -19,4 +19,6 @@ --- -- [ ] I have read the [contributing guide](https://infisical.com/docs/contributing/overview), agreed and acknowledged the [code of conduct](https://infisical.com/docs/contributing/code-of-conduct). 📝 \ No newline at end of file +- [ ] I have read the [contributing guide](https://infisical.com/docs/contributing/getting-started/overview), agreed and acknowledged the [code of conduct](https://infisical.com/docs/contributing/getting-started/code-of-conduct). 📝 + + \ No newline at end of file diff --git a/.github/values.yaml b/.github/values.yaml index 7416f1ba4..be55bfad4 100644 --- a/.github/values.yaml +++ b/.github/values.yaml @@ -19,14 +19,14 @@ infisical: ## @param backend.name Backend name ## name: infisical - replicaCount: 2 + replicaCount: 3 image: - repository: infisical/infisical - tag: "latest-postgres" + repository: infisical/staging_infisical + tag: "latest" pullPolicy: IfNotPresent deploymentAnnotations: - secrets.infisical.com/auto-reload: "true" + secrets.infisical.com/auto-reload: "false" kubeSecretRef: "infisical-gamma-secrets" diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js index 7a3a8d802..e99c48c09 100644 --- a/backend/.eslintrc.js +++ b/backend/.eslintrc.js @@ -1,27 +1,39 @@ +/* eslint-env node */ module.exports = { - root: true, env: { - browser: true, - es2021: true + es6: true, + node: true }, - extends: ["airbnb-base", "airbnb-typescript/base", "prettier"], - plugins: ["prettier", "simple-import-sort", "import"], + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-type-checked", + "airbnb-base", + "airbnb-typescript/base", + "plugin:prettier/recommended", + "prettier" + ], + plugins: ["@typescript-eslint", "simple-import-sort", "import"], + parser: "@typescript-eslint/parser", parserOptions: { - ecmaVersion: "latest", + project: true, sourceType: "module", - project: "./tsconfig.json", tsconfigRootDir: __dirname }, + root: true, rules: { "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unsafe-enum-comparison": "off", + "no-void": "off", "consistent-return": "off", // my style "import/order": "off", // for simple-import-order "import/prefer-default-export": "off", // why "no-restricted-syntax": "off", + // importing rules + "simple-import-sort/exports": "error", "import/first": "error", "import/newline-after-import": "error", "import/no-duplicates": "error", - "simple-import-sort/exports": "error", "simple-import-sort/imports": [ "warn", { @@ -45,12 +57,5 @@ module.exports = { ] } ] - }, - settings: { - "import/resolver": { - typescript: { - project: ["./tsconfig.json"] - } - } } }; diff --git a/backend/.prettierrc.json b/backend/.prettierrc.json index f9058accf..987f567d4 100644 --- a/backend/.prettierrc.json +++ b/backend/.prettierrc.json @@ -1,7 +1,7 @@ { "singleQuote": false, - "printWidth": 100, + "printWidth": 120, "trailingComma": "none", "tabWidth": 2, "semi": true -} \ No newline at end of file +} diff --git a/backend/e2e-test/mocks/queue.ts b/backend/e2e-test/mocks/queue.ts index f39e98738..c694979db 100644 --- a/backend/e2e-test/mocks/queue.ts +++ b/backend/e2e-test/mocks/queue.ts @@ -16,9 +16,11 @@ export const mockQueue = (): TQueueServiceFactory => { queues[name] = jobFn; workers[name] = jobFn; }, - listen: async (name, event) => { + listen: (name, event) => { events[name] = event; }, + clearQueue: async () => {}, + stopJobById: async () => {}, stopRepeatableJobByJobId: async () => true }; }; diff --git a/backend/package-lock.json b/backend/package-lock.json index 6f8a6d33e..0b471d2a8 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,6 +13,7 @@ "@casl/ability": "^6.5.0", "@fastify/cookie": "^9.2.0", "@fastify/cors": "^8.4.1", + "@fastify/etag": "^5.1.0", "@fastify/formbody": "^7.4.0", "@fastify/helmet": "^11.1.1", "@fastify/passport": "^2.4.0", @@ -25,6 +26,8 @@ "@octokit/webhooks-types": "^7.3.1", "@serdnam/pino-cloudwatch-transport": "^1.0.4", "@sindresorhus/slugify": "^2.2.1", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", "@ucast/mongo2js": "^1.3.4", "ajv": "^8.12.0", "argon2": "^0.31.2", @@ -34,6 +37,8 @@ "bcrypt": "^5.1.1", "bullmq": "^5.1.1", "dotenv": "^16.3.1", + "eslint": "^8.56.0", + "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^17.1.0", "fastify": "^4.24.3", "fastify-plugin": "^4.5.1", @@ -80,10 +85,6 @@ "@types/picomatch": "^2.3.3", "@types/prompt-sync": "^4.2.3", "@types/uuid": "^9.0.7", - "@typescript-eslint/eslint-plugin": "^6.13.2", - "@typescript-eslint/parser": "^6.13.2", - "eslint": "^8.56.0", - "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.1", @@ -1671,6 +1672,14 @@ "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" }, + "node_modules/@fastify/etag": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fastify/etag/-/etag-5.1.0.tgz", + "integrity": "sha512-j/huE8baxgF22idzY35a579b6uP+9ykE9Jt02xY4ZApELNr2KGZmQOKTQsZS94TfKMLfPHwkoM8FfZRq8OZDXg==", + "dependencies": { + "fastify-plugin": "^4.0.0" + } + }, "node_modules/@fastify/fast-json-stringify-compiler": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", @@ -4530,15 +4539,15 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz", - "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.20.0.tgz", + "integrity": "sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg==", "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/type-utils": "6.13.2", - "@typescript-eslint/utils": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/type-utils": "6.20.0", + "@typescript-eslint/utils": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -4585,14 +4594,14 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@typescript-eslint/parser": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz", - "integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz", + "integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==", "dependencies": { - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/typescript-estree": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", "debug": "^4.3.4" }, "engines": { @@ -4633,12 +4642,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz", - "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", + "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2" + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4649,12 +4658,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz", - "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.20.0.tgz", + "integrity": "sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g==", "dependencies": { - "@typescript-eslint/typescript-estree": "6.13.2", - "@typescript-eslint/utils": "6.13.2", + "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/utils": "6.20.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -4696,9 +4705,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@typescript-eslint/types": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz", - "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", + "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -4708,15 +4717,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz", - "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", + "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/visitor-keys": "6.20.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", + "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -4733,6 +4743,14 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4749,22 +4767,36 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@typescript-eslint/utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz", - "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", + "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/typescript-estree": "6.13.2", + "@typescript-eslint/scope-manager": "6.20.0", + "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/typescript-estree": "6.20.0", "semver": "^7.5.4" }, "engines": { @@ -4779,11 +4811,11 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz", - "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", + "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", "dependencies": { - "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/types": "6.20.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { diff --git a/backend/package.json b/backend/package.json index d379d54a8..7562612ec 100644 --- a/backend/package.json +++ b/backend/package.json @@ -45,10 +45,6 @@ "@types/picomatch": "^2.3.3", "@types/prompt-sync": "^4.2.3", "@types/uuid": "^9.0.7", - "@typescript-eslint/eslint-plugin": "^6.13.2", - "@typescript-eslint/parser": "^6.13.2", - "eslint": "^8.56.0", - "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.1", @@ -71,6 +67,7 @@ "@casl/ability": "^6.5.0", "@fastify/cookie": "^9.2.0", "@fastify/cors": "^8.4.1", + "@fastify/etag": "^5.1.0", "@fastify/formbody": "^7.4.0", "@fastify/helmet": "^11.1.1", "@fastify/passport": "^2.4.0", @@ -83,6 +80,8 @@ "@octokit/webhooks-types": "^7.3.1", "@serdnam/pino-cloudwatch-transport": "^1.0.4", "@sindresorhus/slugify": "^2.2.1", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", "@ucast/mongo2js": "^1.3.4", "ajv": "^8.12.0", "argon2": "^0.31.2", @@ -92,6 +91,8 @@ "bcrypt": "^5.1.1", "bullmq": "^5.1.1", "dotenv": "^16.3.1", + "eslint": "^8.56.0", + "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^17.1.0", "fastify": "^4.24.3", "fastify-plugin": "^4.5.1", diff --git a/backend/src/@types/fastify-zod.d.ts b/backend/src/@types/fastify-zod.d.ts index cff119c2f..393579391 100644 --- a/backend/src/@types/fastify-zod.d.ts +++ b/backend/src/@types/fastify-zod.d.ts @@ -1,9 +1,4 @@ -import { - FastifyInstance, - RawReplyDefaultExpression, - RawRequestDefaultExpression, - RawServerDefault -} from "fastify"; +import { FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from "fastify"; import { Logger } from "pino"; import { ZodTypeProvider } from "@app/server/plugins/fastify-zod"; diff --git a/backend/src/@types/knex.d.ts b/backend/src/@types/knex.d.ts index 05ce1e59d..5bf563810 100644 --- a/backend/src/@types/knex.d.ts +++ b/backend/src/@types/knex.d.ts @@ -177,11 +177,7 @@ declare module "knex/types/tables" { TUserEncryptionKeysInsert, TUserEncryptionKeysUpdate >; - [TableName.AuthTokens]: Knex.CompositeTableType< - TAuthTokens, - TAuthTokensInsert, - TAuthTokensUpdate - >; + [TableName.AuthTokens]: Knex.CompositeTableType; [TableName.AuthTokenSession]: Knex.CompositeTableType< TAuthTokenSessions, TAuthTokenSessionsInsert, @@ -192,32 +188,16 @@ declare module "knex/types/tables" { TBackupPrivateKeyInsert, TBackupPrivateKeyUpdate >; - [TableName.Organization]: Knex.CompositeTableType< - TOrganizations, - TOrganizationsInsert, - TOrganizationsUpdate - >; - [TableName.OrgMembership]: Knex.CompositeTableType< - TOrgMemberships, - TOrgMembershipsInsert, - TOrgMembershipsUpdate - >; + [TableName.Organization]: Knex.CompositeTableType; + [TableName.OrgMembership]: Knex.CompositeTableType; [TableName.OrgRoles]: Knex.CompositeTableType; [TableName.IncidentContact]: Knex.CompositeTableType< TIncidentContacts, TIncidentContactsInsert, TIncidentContactsUpdate >; - [TableName.UserAction]: Knex.CompositeTableType< - TUserActions, - TUserActionsInsert, - TUserActionsUpdate - >; - [TableName.SuperAdmin]: Knex.CompositeTableType< - TSuperAdmin, - TSuperAdminInsert, - TSuperAdminUpdate - >; + [TableName.UserAction]: Knex.CompositeTableType; + [TableName.SuperAdmin]: Knex.CompositeTableType; [TableName.ApiKey]: Knex.CompositeTableType; [TableName.Project]: Knex.CompositeTableType; [TableName.ProjectMembership]: Knex.CompositeTableType< @@ -230,73 +210,33 @@ declare module "knex/types/tables" { TProjectEnvironmentsInsert, TProjectEnvironmentsUpdate >; - [TableName.ProjectBot]: Knex.CompositeTableType< - TProjectBots, - TProjectBotsInsert, - TProjectBotsUpdate - >; - [TableName.ProjectRoles]: Knex.CompositeTableType< - TProjectRoles, - TProjectRolesInsert, - TProjectRolesUpdate - >; - [TableName.ProjectKeys]: Knex.CompositeTableType< - TProjectKeys, - TProjectKeysInsert, - TProjectKeysUpdate - >; + [TableName.ProjectBot]: Knex.CompositeTableType; + [TableName.ProjectRoles]: Knex.CompositeTableType; + [TableName.ProjectKeys]: Knex.CompositeTableType; [TableName.Secret]: Knex.CompositeTableType; [TableName.SecretBlindIndex]: Knex.CompositeTableType< TSecretBlindIndexes, TSecretBlindIndexesInsert, TSecretBlindIndexesUpdate >; - [TableName.SecretVersion]: Knex.CompositeTableType< - TSecretVersions, - TSecretVersionsInsert, - TSecretVersionsUpdate - >; - [TableName.SecretFolder]: Knex.CompositeTableType< - TSecretFolders, - TSecretFoldersInsert, - TSecretFoldersUpdate - >; + [TableName.SecretVersion]: Knex.CompositeTableType; + [TableName.SecretFolder]: Knex.CompositeTableType; [TableName.SecretFolderVersion]: Knex.CompositeTableType< TSecretFolderVersions, TSecretFolderVersionsInsert, TSecretFolderVersionsUpdate >; - [TableName.SecretTag]: Knex.CompositeTableType< - TSecretTags, - TSecretTagsInsert, - TSecretTagsUpdate - >; - [TableName.SecretImport]: Knex.CompositeTableType< - TSecretImports, - TSecretImportsInsert, - TSecretImportsUpdate - >; - [TableName.Integration]: Knex.CompositeTableType< - TIntegrations, - TIntegrationsInsert, - TIntegrationsUpdate - >; + [TableName.SecretTag]: Knex.CompositeTableType; + [TableName.SecretImport]: Knex.CompositeTableType; + [TableName.Integration]: Knex.CompositeTableType; [TableName.Webhook]: Knex.CompositeTableType; - [TableName.ServiceToken]: Knex.CompositeTableType< - TServiceTokens, - TServiceTokensInsert, - TServiceTokensUpdate - >; + [TableName.ServiceToken]: Knex.CompositeTableType; [TableName.IntegrationAuth]: Knex.CompositeTableType< TIntegrationAuths, TIntegrationAuthsInsert, TIntegrationAuthsUpdate >; - [TableName.Identity]: Knex.CompositeTableType< - TIdentities, - TIdentitiesInsert, - TIdentitiesUpdate - >; + [TableName.Identity]: Knex.CompositeTableType; [TableName.IdentityUniversalAuth]: Knex.CompositeTableType< TIdentityUniversalAuths, TIdentityUniversalAuthsInsert, @@ -362,11 +302,7 @@ declare module "knex/types/tables" { TSecretRotationOutputsInsert, TSecretRotationOutputsUpdate >; - [TableName.Snapshot]: Knex.CompositeTableType< - TSecretSnapshots, - TSecretSnapshotsInsert, - TSecretSnapshotsUpdate - >; + [TableName.Snapshot]: Knex.CompositeTableType; [TableName.SnapshotSecret]: Knex.CompositeTableType< TSecretSnapshotSecrets, TSecretSnapshotSecretsInsert, @@ -377,11 +313,7 @@ declare module "knex/types/tables" { TSecretSnapshotFoldersInsert, TSecretSnapshotFoldersUpdate >; - [TableName.SamlConfig]: Knex.CompositeTableType< - TSamlConfigs, - TSamlConfigsInsert, - TSamlConfigsUpdate - >; + [TableName.SamlConfig]: Knex.CompositeTableType; [TableName.OrgBot]: Knex.CompositeTableType; [TableName.AuditLog]: Knex.CompositeTableType; [TableName.GitAppInstallSession]: Knex.CompositeTableType< @@ -395,11 +327,7 @@ declare module "knex/types/tables" { TSecretScanningGitRisksInsert, TSecretScanningGitRisksUpdate >; - [TableName.TrustedIps]: Knex.CompositeTableType< - TTrustedIps, - TTrustedIpsInsert, - TTrustedIpsUpdate - >; + [TableName.TrustedIps]: Knex.CompositeTableType; // Junction tables [TableName.JnSecretTag]: Knex.CompositeTableType< TSecretTagJunction, diff --git a/backend/src/db/migrations/20231220052508_secret-version.ts b/backend/src/db/migrations/20231220052508_secret-version.ts index 0bcee295b..46e3264bf 100644 --- a/backend/src/db/migrations/20231220052508_secret-version.ts +++ b/backend/src/db/migrations/20231220052508_secret-version.ts @@ -38,12 +38,7 @@ export async function up(knex: Knex): Promise { } await createOnUpdateTrigger(knex, TableName.SecretVersion); // many to many relation between tags - await createJunctionTable( - knex, - TableName.SecretVersionTag, - TableName.SecretVersion, - TableName.SecretTag - ); + await createJunctionTable(knex, TableName.SecretVersionTag, TableName.SecretVersion, TableName.SecretTag); } export async function down(knex: Knex): Promise { diff --git a/backend/src/db/migrations/20231222172455_integration.ts b/backend/src/db/migrations/20231222172455_integration.ts index 4ef34cab1..43fd9986a 100644 --- a/backend/src/db/migrations/20231222172455_integration.ts +++ b/backend/src/db/migrations/20231222172455_integration.ts @@ -50,10 +50,7 @@ export async function up(knex: Knex): Promise { t.string("integration").notNullable(); t.jsonb("metadata"); t.uuid("integrationAuthId").notNullable(); - t.foreign("integrationAuthId") - .references("id") - .inTable(TableName.IntegrationAuth) - .onDelete("CASCADE"); + t.foreign("integrationAuthId").references("id").inTable(TableName.IntegrationAuth).onDelete("CASCADE"); t.uuid("envId").notNullable(); t.string("secretPath").defaultTo("/").notNullable(); t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE"); diff --git a/backend/src/db/migrations/20231228074908_identity-universal-auth.ts b/backend/src/db/migrations/20231228074908_identity-universal-auth.ts index 76d05db91..48d58ccd4 100644 --- a/backend/src/db/migrations/20231228074908_identity-universal-auth.ts +++ b/backend/src/db/migrations/20231228074908_identity-universal-auth.ts @@ -31,10 +31,7 @@ export async function up(knex: Knex): Promise { t.boolean("isClientSecretRevoked").defaultTo(false).notNullable(); t.timestamps(true, true, true); t.uuid("identityUAId").notNullable(); - t.foreign("identityUAId") - .references("id") - .inTable(TableName.IdentityUniversalAuth) - .onDelete("CASCADE"); + t.foreign("identityUAId").references("id").inTable(TableName.IdentityUniversalAuth).onDelete("CASCADE"); }); } await createOnUpdateTrigger(knex, TableName.IdentityUniversalAuth); diff --git a/backend/src/db/migrations/20231228075011_identity-access-token.ts b/backend/src/db/migrations/20231228075011_identity-access-token.ts index 33c5f96ef..e8e26fc70 100644 --- a/backend/src/db/migrations/20231228075011_identity-access-token.ts +++ b/backend/src/db/migrations/20231228075011_identity-access-token.ts @@ -19,7 +19,7 @@ export async function up(knex: Knex): Promise { .references("id") .inTable(TableName.IdentityUaClientSecret) .onDelete("CASCADE"); - t.uuid("identityId").notNullable(); + t.uuid("identityId").notNullable(); t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE"); t.timestamps(true, true, true); }); diff --git a/backend/src/db/migrations/20240101054849_secret-approval-policy.ts b/backend/src/db/migrations/20240101054849_secret-approval-policy.ts index 7fde0d314..3de1ce280 100644 --- a/backend/src/db/migrations/20240101054849_secret-approval-policy.ts +++ b/backend/src/db/migrations/20240101054849_secret-approval-policy.ts @@ -21,15 +21,9 @@ export async function up(knex: Knex): Promise { await knex.schema.createTable(TableName.SecretApprovalPolicyApprover, (t) => { t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); t.uuid("approverId").notNullable(); - t.foreign("approverId") - .references("id") - .inTable(TableName.ProjectMembership) - .onDelete("CASCADE"); + t.foreign("approverId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE"); t.uuid("policyId").notNullable(); - t.foreign("policyId") - .references("id") - .inTable(TableName.SecretApprovalPolicy) - .onDelete("CASCADE"); + t.foreign("policyId").references("id").inTable(TableName.SecretApprovalPolicy).onDelete("CASCADE"); t.timestamps(true, true, true); }); } diff --git a/backend/src/db/migrations/20240101104907_secret-approval-request.ts b/backend/src/db/migrations/20240101104907_secret-approval-request.ts index 21a9e944e..f35f87819 100644 --- a/backend/src/db/migrations/20240101104907_secret-approval-request.ts +++ b/backend/src/db/migrations/20240101104907_secret-approval-request.ts @@ -11,23 +11,14 @@ export async function up(knex: Knex): Promise { t.boolean("hasMerged").defaultTo(false).notNullable(); t.string("status").defaultTo("open").notNullable(); t.jsonb("conflicts"); - t.foreign("policyId") - .references("id") - .inTable(TableName.SecretApprovalPolicy) - .onDelete("CASCADE"); + t.foreign("policyId").references("id").inTable(TableName.SecretApprovalPolicy).onDelete("CASCADE"); t.string("slug").notNullable(); t.uuid("folderId").notNullable(); t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE"); t.uuid("statusChangeBy"); - t.foreign("statusChangeBy") - .references("id") - .inTable(TableName.ProjectMembership) - .onDelete("SET NULL"); + t.foreign("statusChangeBy").references("id").inTable(TableName.ProjectMembership).onDelete("SET NULL"); t.uuid("committerId").notNullable(); - t.foreign("committerId") - .references("id") - .inTable(TableName.ProjectMembership) - .onDelete("CASCADE"); + t.foreign("committerId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE"); t.timestamps(true, true, true); }); } @@ -40,10 +31,7 @@ export async function up(knex: Knex): Promise { t.foreign("member").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE"); t.string("status").notNullable(); t.uuid("requestId").notNullable(); - t.foreign("requestId") - .references("id") - .inTable(TableName.SecretApprovalRequest) - .onDelete("CASCADE"); + t.foreign("requestId").references("id").inTable(TableName.SecretApprovalRequest).onDelete("CASCADE"); t.timestamps(true, true, true); }); } @@ -73,18 +61,12 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); // commit details t.uuid("requestId").notNullable(); - t.foreign("requestId") - .references("id") - .inTable(TableName.SecretApprovalRequest) - .onDelete("CASCADE"); + t.foreign("requestId").references("id").inTable(TableName.SecretApprovalRequest).onDelete("CASCADE"); t.string("op").notNullable(); t.uuid("secretId"); t.foreign("secretId").references("id").inTable(TableName.Secret).onDelete("SET NULL"); t.uuid("secretVersion"); - t.foreign("secretVersion") - .references("id") - .inTable(TableName.SecretVersion) - .onDelete("SET NULL"); + t.foreign("secretVersion").references("id").inTable(TableName.SecretVersion).onDelete("SET NULL"); }); } await createOnUpdateTrigger(knex, TableName.SecretApprovalRequestSecret); @@ -93,10 +75,7 @@ export async function up(knex: Knex): Promise { await knex.schema.createTable(TableName.SecretApprovalRequestSecretTag, (t) => { t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); t.uuid("secretId").notNullable(); - t.foreign("secretId") - .references("id") - .inTable(TableName.SecretApprovalRequestSecret) - .onDelete("CASCADE"); + t.foreign("secretId").references("id").inTable(TableName.SecretApprovalRequestSecret).onDelete("CASCADE"); t.uuid("tagId").notNullable(); t.foreign("tagId").references("id").inTable(TableName.SecretTag).onDelete("CASCADE"); t.timestamps(true, true, true); diff --git a/backend/src/db/migrations/20240102152111_secret-rotation.ts b/backend/src/db/migrations/20240102152111_secret-rotation.ts index f7009488a..ea962cc6e 100644 --- a/backend/src/db/migrations/20240102152111_secret-rotation.ts +++ b/backend/src/db/migrations/20240102152111_secret-rotation.ts @@ -32,10 +32,7 @@ export async function up(knex: Knex): Promise { t.uuid("secretId").notNullable(); t.foreign("secretId").references("id").inTable(TableName.Secret).onDelete("CASCADE"); t.uuid("rotationId").notNullable(); - t.foreign("rotationId") - .references("id") - .inTable(TableName.SecretRotation) - .onDelete("CASCADE"); + t.foreign("rotationId").references("id").inTable(TableName.SecretRotation).onDelete("CASCADE"); }); } } diff --git a/backend/src/db/migrations/20240104140641_secret-snapshot.ts b/backend/src/db/migrations/20240104140641_secret-snapshot.ts index 01b1e24fb..3dfb06772 100644 --- a/backend/src/db/migrations/20240104140641_secret-snapshot.ts +++ b/backend/src/db/migrations/20240104140641_secret-snapshot.ts @@ -25,10 +25,7 @@ export async function up(knex: Knex): Promise { t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE"); // not a relation kept like that to keep it when rolled back t.uuid("secretVersionId").notNullable(); - t.foreign("secretVersionId") - .references("id") - .inTable(TableName.SecretVersion) - .onDelete("CASCADE"); + t.foreign("secretVersionId").references("id").inTable(TableName.SecretVersion).onDelete("CASCADE"); t.uuid("snapshotId").notNullable(); t.foreign("snapshotId").references("id").inTable(TableName.Snapshot).onDelete("CASCADE"); t.timestamps(true, true, true); @@ -42,10 +39,7 @@ export async function up(knex: Knex): Promise { t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE"); // not a relation kept like that to keep it when rolled back t.uuid("folderVersionId").notNullable(); - t.foreign("folderVersionId") - .references("id") - .inTable(TableName.SecretFolderVersion) - .onDelete("CASCADE"); + t.foreign("folderVersionId").references("id").inTable(TableName.SecretFolderVersion).onDelete("CASCADE"); t.uuid("snapshotId").notNullable(); t.foreign("snapshotId").references("id").inTable(TableName.Snapshot).onDelete("CASCADE"); t.timestamps(true, true, true); diff --git a/backend/src/db/migrations/20240111051011_secret-scanning.ts b/backend/src/db/migrations/20240111051011_secret-scanning.ts index af011d558..28a57bf08 100644 --- a/backend/src/db/migrations/20240111051011_secret-scanning.ts +++ b/backend/src/db/migrations/20240111051011_secret-scanning.ts @@ -15,7 +15,7 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); }); } - createOnUpdateTrigger(knex, TableName.GitAppInstallSession); + await createOnUpdateTrigger(knex, TableName.GitAppInstallSession); if (!(await knex.schema.hasTable(TableName.GitAppOrg))) { await knex.schema.createTable(TableName.GitAppOrg, (t) => { @@ -28,7 +28,7 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); }); } - createOnUpdateTrigger(knex, TableName.GitAppOrg); + await createOnUpdateTrigger(knex, TableName.GitAppOrg); if (!(await knex.schema.hasTable(TableName.SecretScanningGitRisk))) { await knex.schema.createTable(TableName.SecretScanningGitRisk, (t) => { @@ -66,7 +66,7 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); }); } - createOnUpdateTrigger(knex, TableName.SecretScanningGitRisk); + await createOnUpdateTrigger(knex, TableName.SecretScanningGitRisk); } export async function down(knex: Knex): Promise { diff --git a/backend/src/db/schemas/api-keys.ts b/backend/src/db/schemas/api-keys.ts index c3f384a5a..ff29a54e1 100644 --- a/backend/src/db/schemas/api-keys.ts +++ b/backend/src/db/schemas/api-keys.ts @@ -15,7 +15,7 @@ export const ApiKeysSchema = z.object({ secretHash: z.string(), createdAt: z.date(), updatedAt: z.date(), - userId: z.string().uuid(), + userId: z.string().uuid() }); export type TApiKeys = z.infer; diff --git a/backend/src/db/schemas/audit-logs.ts b/backend/src/db/schemas/audit-logs.ts index 90c389b94..f7143bb57 100644 --- a/backend/src/db/schemas/audit-logs.ts +++ b/backend/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().nullable().optional(), + projectId: z.string().nullable().optional() }); export type TAuditLogs = z.infer; diff --git a/backend/src/db/schemas/auth-token-sessions.ts b/backend/src/db/schemas/auth-token-sessions.ts index 9dbf8e09f..46ed7c201 100644 --- a/backend/src/db/schemas/auth-token-sessions.ts +++ b/backend/src/db/schemas/auth-token-sessions.ts @@ -16,7 +16,7 @@ export const AuthTokenSessionsSchema = z.object({ lastUsed: z.date(), createdAt: z.date(), updatedAt: z.date(), - userId: z.string().uuid(), + userId: z.string().uuid() }); export type TAuthTokenSessions = z.infer; diff --git a/backend/src/db/schemas/auth-tokens.ts b/backend/src/db/schemas/auth-tokens.ts index 4a612b11d..9ae8eed44 100644 --- a/backend/src/db/schemas/auth-tokens.ts +++ b/backend/src/db/schemas/auth-tokens.ts @@ -17,7 +17,7 @@ export const AuthTokensSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), userId: z.string().uuid().nullable().optional(), - orgId: z.string().uuid().nullable().optional(), + orgId: z.string().uuid().nullable().optional() }); export type TAuthTokens = z.infer; diff --git a/backend/src/db/schemas/backup-private-key.ts b/backend/src/db/schemas/backup-private-key.ts index 9b6e787b1..5930bbd4a 100644 --- a/backend/src/db/schemas/backup-private-key.ts +++ b/backend/src/db/schemas/backup-private-key.ts @@ -18,7 +18,7 @@ export const BackupPrivateKeySchema = z.object({ verifier: z.string(), createdAt: z.date(), updatedAt: z.date(), - userId: z.string().uuid(), + userId: z.string().uuid() }); export type TBackupPrivateKey = z.infer; diff --git a/backend/src/db/schemas/git-app-install-sessions.ts b/backend/src/db/schemas/git-app-install-sessions.ts index 16f62eab7..6c6db40ea 100644 --- a/backend/src/db/schemas/git-app-install-sessions.ts +++ b/backend/src/db/schemas/git-app-install-sessions.ts @@ -13,7 +13,7 @@ export const GitAppInstallSessionsSchema = z.object({ userId: z.string().uuid().nullable().optional(), orgId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TGitAppInstallSessions = z.infer; diff --git a/backend/src/db/schemas/git-app-org.ts b/backend/src/db/schemas/git-app-org.ts index f5226811d..57e0d474a 100644 --- a/backend/src/db/schemas/git-app-org.ts +++ b/backend/src/db/schemas/git-app-org.ts @@ -13,7 +13,7 @@ export const GitAppOrgSchema = z.object({ userId: z.string().uuid(), orgId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TGitAppOrg = z.infer; diff --git a/backend/src/db/schemas/identities.ts b/backend/src/db/schemas/identities.ts index b8ff6c36f..005adf025 100644 --- a/backend/src/db/schemas/identities.ts +++ b/backend/src/db/schemas/identities.ts @@ -12,7 +12,7 @@ export const IdentitiesSchema = z.object({ name: z.string(), authMethod: z.string().nullable().optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TIdentities = z.infer; diff --git a/backend/src/db/schemas/identity-access-tokens.ts b/backend/src/db/schemas/identity-access-tokens.ts index d36291ab9..cbd71e5c5 100644 --- a/backend/src/db/schemas/identity-access-tokens.ts +++ b/backend/src/db/schemas/identity-access-tokens.ts @@ -19,7 +19,7 @@ export const IdentityAccessTokensSchema = z.object({ identityUAClientSecretId: z.string().nullable().optional(), identityId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TIdentityAccessTokens = z.infer; diff --git a/backend/src/db/schemas/identity-org-memberships.ts b/backend/src/db/schemas/identity-org-memberships.ts index c5c9a0f61..647ec7124 100644 --- a/backend/src/db/schemas/identity-org-memberships.ts +++ b/backend/src/db/schemas/identity-org-memberships.ts @@ -14,7 +14,7 @@ export const IdentityOrgMembershipsSchema = z.object({ orgId: z.string().uuid(), createdAt: z.date(), updatedAt: z.date(), - identityId: z.string().uuid(), + identityId: z.string().uuid() }); export type TIdentityOrgMemberships = z.infer; diff --git a/backend/src/db/schemas/identity-project-memberships.ts b/backend/src/db/schemas/identity-project-memberships.ts index 9a57952a4..866324c8b 100644 --- a/backend/src/db/schemas/identity-project-memberships.ts +++ b/backend/src/db/schemas/identity-project-memberships.ts @@ -14,7 +14,7 @@ export const IdentityProjectMembershipsSchema = z.object({ projectId: z.string(), identityId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TIdentityProjectMemberships = z.infer; diff --git a/backend/src/db/schemas/identity-ua-client-secrets.ts b/backend/src/db/schemas/identity-ua-client-secrets.ts index f17b1ed7f..60f8d862f 100644 --- a/backend/src/db/schemas/identity-ua-client-secrets.ts +++ b/backend/src/db/schemas/identity-ua-client-secrets.ts @@ -19,7 +19,7 @@ export const IdentityUaClientSecretsSchema = z.object({ isClientSecretRevoked: z.boolean().default(false), createdAt: z.date(), updatedAt: z.date(), - identityUAId: z.string().uuid(), + identityUAId: z.string().uuid() }); export type TIdentityUaClientSecrets = z.infer; diff --git a/backend/src/db/schemas/identity-universal-auths.ts b/backend/src/db/schemas/identity-universal-auths.ts index 1dbc61ea8..5a8f0c7ec 100644 --- a/backend/src/db/schemas/identity-universal-auths.ts +++ b/backend/src/db/schemas/identity-universal-auths.ts @@ -17,7 +17,7 @@ export const IdentityUniversalAuthsSchema = z.object({ accessTokenTrustedIps: z.unknown(), createdAt: z.date(), updatedAt: z.date(), - identityId: z.string().uuid(), + identityId: z.string().uuid() }); export type TIdentityUniversalAuths = z.infer; diff --git a/backend/src/db/schemas/incident-contacts.ts b/backend/src/db/schemas/incident-contacts.ts index c1492e0fa..431bf05ab 100644 --- a/backend/src/db/schemas/incident-contacts.ts +++ b/backend/src/db/schemas/incident-contacts.ts @@ -12,7 +12,7 @@ export const IncidentContactsSchema = z.object({ email: z.string(), createdAt: z.date(), updatedAt: z.date(), - orgId: z.string().uuid(), + orgId: z.string().uuid() }); export type TIncidentContacts = z.infer; diff --git a/backend/src/db/schemas/integration-auths.ts b/backend/src/db/schemas/integration-auths.ts index d2983658c..db602c0af 100644 --- a/backend/src/db/schemas/integration-auths.ts +++ b/backend/src/db/schemas/integration-auths.ts @@ -29,7 +29,7 @@ export const IntegrationAuthsSchema = z.object({ keyEncoding: z.string(), projectId: z.string(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TIntegrationAuths = z.infer; diff --git a/backend/src/db/schemas/integrations.ts b/backend/src/db/schemas/integrations.ts index b2163dc4d..62f73d190 100644 --- a/backend/src/db/schemas/integrations.ts +++ b/backend/src/db/schemas/integrations.ts @@ -25,9 +25,9 @@ export const IntegrationsSchema = z.object({ metadata: z.unknown().nullable().optional(), integrationAuthId: z.string().uuid(), envId: z.string().uuid(), - secretPath: z.string().default('/'), + secretPath: z.string().default("/"), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TIntegrations = z.infer; diff --git a/backend/src/db/schemas/org-bots.ts b/backend/src/db/schemas/org-bots.ts index 400ab19c8..b328f1aaf 100644 --- a/backend/src/db/schemas/org-bots.ts +++ b/backend/src/db/schemas/org-bots.ts @@ -23,7 +23,7 @@ export const OrgBotsSchema = z.object({ privateKeyKeyEncoding: z.string(), orgId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TOrgBots = z.infer; diff --git a/backend/src/db/schemas/org-memberships.ts b/backend/src/db/schemas/org-memberships.ts index 932c84d00..b2fffa117 100644 --- a/backend/src/db/schemas/org-memberships.ts +++ b/backend/src/db/schemas/org-memberships.ts @@ -10,13 +10,13 @@ import { TImmutableDBKeys } from "./models"; export const OrgMembershipsSchema = z.object({ id: z.string().uuid(), role: z.string(), - status: z.string().default('invited'), + status: z.string().default("invited"), inviteEmail: z.string().nullable().optional(), createdAt: z.date(), updatedAt: z.date(), userId: z.string().uuid().nullable().optional(), orgId: z.string().uuid(), - roleId: z.string().uuid().nullable().optional(), + roleId: z.string().uuid().nullable().optional() }); export type TOrgMemberships = z.infer; diff --git a/backend/src/db/schemas/org-roles.ts b/backend/src/db/schemas/org-roles.ts index 9718cdb26..72b582f96 100644 --- a/backend/src/db/schemas/org-roles.ts +++ b/backend/src/db/schemas/org-roles.ts @@ -15,7 +15,7 @@ export const OrgRolesSchema = z.object({ permissions: z.unknown(), createdAt: z.date(), updatedAt: z.date(), - orgId: z.string().uuid(), + orgId: z.string().uuid() }); export type TOrgRoles = z.infer; diff --git a/backend/src/db/schemas/organizations.ts b/backend/src/db/schemas/organizations.ts index 539217c00..e0f70d1c0 100644 --- a/backend/src/db/schemas/organizations.ts +++ b/backend/src/db/schemas/organizations.ts @@ -13,7 +13,7 @@ export const OrganizationsSchema = z.object({ customerId: z.string().nullable().optional(), slug: z.string(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TOrganizations = z.infer; diff --git a/backend/src/db/schemas/project-bots.ts b/backend/src/db/schemas/project-bots.ts index 90ced9b3e..c68576943 100644 --- a/backend/src/db/schemas/project-bots.ts +++ b/backend/src/db/schemas/project-bots.ts @@ -22,7 +22,7 @@ export const ProjectBotsSchema = z.object({ projectId: z.string(), senderId: z.string().uuid().nullable().optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TProjectBots = z.infer; diff --git a/backend/src/db/schemas/project-environments.ts b/backend/src/db/schemas/project-environments.ts index aa3e392c7..8b95dbba0 100644 --- a/backend/src/db/schemas/project-environments.ts +++ b/backend/src/db/schemas/project-environments.ts @@ -14,7 +14,7 @@ export const ProjectEnvironmentsSchema = z.object({ position: z.number(), projectId: z.string(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TProjectEnvironments = z.infer; diff --git a/backend/src/db/schemas/project-keys.ts b/backend/src/db/schemas/project-keys.ts index 64e33d574..720cd79bf 100644 --- a/backend/src/db/schemas/project-keys.ts +++ b/backend/src/db/schemas/project-keys.ts @@ -15,7 +15,7 @@ export const ProjectKeysSchema = z.object({ senderId: z.string().uuid().nullable().optional(), projectId: z.string(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TProjectKeys = z.infer; diff --git a/backend/src/db/schemas/project-memberships.ts b/backend/src/db/schemas/project-memberships.ts index c98befb38..b9f191a84 100644 --- a/backend/src/db/schemas/project-memberships.ts +++ b/backend/src/db/schemas/project-memberships.ts @@ -14,7 +14,7 @@ export const ProjectMembershipsSchema = z.object({ updatedAt: z.date(), userId: z.string().uuid(), projectId: z.string(), - roleId: z.string().uuid().nullable().optional(), + roleId: z.string().uuid().nullable().optional() }); export type TProjectMemberships = z.infer; diff --git a/backend/src/db/schemas/project-roles.ts b/backend/src/db/schemas/project-roles.ts index 190dd1cec..1946ab5e1 100644 --- a/backend/src/db/schemas/project-roles.ts +++ b/backend/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(), + projectId: z.string() }); export type TProjectRoles = z.infer; diff --git a/backend/src/db/schemas/projects.ts b/backend/src/db/schemas/projects.ts index 005e4bbde..296fa421e 100644 --- a/backend/src/db/schemas/projects.ts +++ b/backend/src/db/schemas/projects.ts @@ -14,7 +14,7 @@ export const ProjectsSchema = z.object({ autoCapitalization: z.boolean().default(true).nullable().optional(), orgId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TProjects = z.infer; diff --git a/backend/src/db/schemas/saml-configs.ts b/backend/src/db/schemas/saml-configs.ts index 392fb2bef..4633384b9 100644 --- a/backend/src/db/schemas/saml-configs.ts +++ b/backend/src/db/schemas/saml-configs.ts @@ -22,7 +22,7 @@ export const SamlConfigsSchema = z.object({ certTag: z.string().nullable().optional(), createdAt: z.date(), updatedAt: z.date(), - orgId: z.string().uuid(), + orgId: z.string().uuid() }); export type TSamlConfigs = z.infer; diff --git a/backend/src/db/schemas/secret-approval-policies-approvers.ts b/backend/src/db/schemas/secret-approval-policies-approvers.ts index c6fb75f06..503299d30 100644 --- a/backend/src/db/schemas/secret-approval-policies-approvers.ts +++ b/backend/src/db/schemas/secret-approval-policies-approvers.ts @@ -12,7 +12,7 @@ export const SecretApprovalPoliciesApproversSchema = z.object({ approverId: z.string().uuid(), policyId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretApprovalPoliciesApprovers = z.infer; diff --git a/backend/src/db/schemas/secret-approval-policies.ts b/backend/src/db/schemas/secret-approval-policies.ts index ec859bb4e..6c331f1b1 100644 --- a/backend/src/db/schemas/secret-approval-policies.ts +++ b/backend/src/db/schemas/secret-approval-policies.ts @@ -14,7 +14,7 @@ export const SecretApprovalPoliciesSchema = z.object({ approvals: z.number().default(1), envId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretApprovalPolicies = z.infer; diff --git a/backend/src/db/schemas/secret-approval-request-secret-tags.ts b/backend/src/db/schemas/secret-approval-request-secret-tags.ts index 47e11e6a9..f5e7ba632 100644 --- a/backend/src/db/schemas/secret-approval-request-secret-tags.ts +++ b/backend/src/db/schemas/secret-approval-request-secret-tags.ts @@ -12,7 +12,7 @@ export const SecretApprovalRequestSecretTagsSchema = z.object({ secretId: z.string().uuid(), tagId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretApprovalRequestSecretTags = z.infer; diff --git a/backend/src/db/schemas/secret-approval-requests-reviewers.ts b/backend/src/db/schemas/secret-approval-requests-reviewers.ts index 4ed9b50a1..a3657f1f9 100644 --- a/backend/src/db/schemas/secret-approval-requests-reviewers.ts +++ b/backend/src/db/schemas/secret-approval-requests-reviewers.ts @@ -13,7 +13,7 @@ export const SecretApprovalRequestsReviewersSchema = z.object({ status: z.string(), requestId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretApprovalRequestsReviewers = z.infer; diff --git a/backend/src/db/schemas/secret-approval-requests-secrets.ts b/backend/src/db/schemas/secret-approval-requests-secrets.ts index db98bda17..810a4f2cf 100644 --- a/backend/src/db/schemas/secret-approval-requests-secrets.ts +++ b/backend/src/db/schemas/secret-approval-requests-secrets.ts @@ -23,15 +23,15 @@ export const SecretApprovalRequestsSecretsSchema = 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 TSecretApprovalRequestsSecrets = z.infer; diff --git a/backend/src/db/schemas/secret-approval-requests.ts b/backend/src/db/schemas/secret-approval-requests.ts index 7d5f2f443..590c283f5 100644 --- a/backend/src/db/schemas/secret-approval-requests.ts +++ b/backend/src/db/schemas/secret-approval-requests.ts @@ -11,14 +11,14 @@ export const SecretApprovalRequestsSchema = z.object({ id: z.string().uuid(), policyId: z.string().uuid(), hasMerged: z.boolean().default(false), - status: z.string().default('open'), + status: z.string().default("open"), conflicts: z.unknown().nullable().optional(), slug: z.string(), folderId: z.string().uuid(), statusChangeBy: z.string().uuid().nullable().optional(), committerId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretApprovalRequests = z.infer; diff --git a/backend/src/db/schemas/secret-blind-indexes.ts b/backend/src/db/schemas/secret-blind-indexes.ts index 17eacb473..fa919babd 100644 --- a/backend/src/db/schemas/secret-blind-indexes.ts +++ b/backend/src/db/schemas/secret-blind-indexes.ts @@ -12,11 +12,11 @@ export const SecretBlindIndexesSchema = z.object({ encryptedSaltCipherText: z.string(), saltIV: z.string(), saltTag: z.string(), - algorithm: z.string().default('aes-256-gcm'), - keyEncoding: z.string().default('utf8'), + algorithm: z.string().default("aes-256-gcm"), + keyEncoding: z.string().default("utf8"), projectId: z.string(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretBlindIndexes = z.infer; diff --git a/backend/src/db/schemas/secret-folder-versions.ts b/backend/src/db/schemas/secret-folder-versions.ts index 895f81ebc..8c550d065 100644 --- a/backend/src/db/schemas/secret-folder-versions.ts +++ b/backend/src/db/schemas/secret-folder-versions.ts @@ -14,7 +14,7 @@ export const SecretFolderVersionsSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), envId: z.string().uuid(), - folderId: z.string().uuid(), + folderId: z.string().uuid() }); export type TSecretFolderVersions = z.infer; diff --git a/backend/src/db/schemas/secret-folders.ts b/backend/src/db/schemas/secret-folders.ts index aa437c753..648238b0a 100644 --- a/backend/src/db/schemas/secret-folders.ts +++ b/backend/src/db/schemas/secret-folders.ts @@ -14,7 +14,7 @@ export const SecretFoldersSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), envId: z.string().uuid(), - parentId: z.string().uuid().nullable().optional(), + parentId: z.string().uuid().nullable().optional() }); export type TSecretFolders = z.infer; diff --git a/backend/src/db/schemas/secret-imports.ts b/backend/src/db/schemas/secret-imports.ts index 109d41ec2..9c1ee905f 100644 --- a/backend/src/db/schemas/secret-imports.ts +++ b/backend/src/db/schemas/secret-imports.ts @@ -15,7 +15,7 @@ export const SecretImportsSchema = z.object({ position: z.number(), createdAt: z.date(), updatedAt: z.date(), - folderId: z.string().uuid(), + folderId: z.string().uuid() }); export type TSecretImports = z.infer; diff --git a/backend/src/db/schemas/secret-rotation-outputs.ts b/backend/src/db/schemas/secret-rotation-outputs.ts index b98338c75..3b594365a 100644 --- a/backend/src/db/schemas/secret-rotation-outputs.ts +++ b/backend/src/db/schemas/secret-rotation-outputs.ts @@ -11,7 +11,7 @@ export const SecretRotationOutputsSchema = z.object({ id: z.string().uuid(), key: z.string(), secretId: z.string().uuid(), - rotationId: z.string().uuid(), + rotationId: z.string().uuid() }); export type TSecretRotationOutputs = z.infer; diff --git a/backend/src/db/schemas/secret-rotations.ts b/backend/src/db/schemas/secret-rotations.ts index 6e2bf6547..4c65712fa 100644 --- a/backend/src/db/schemas/secret-rotations.ts +++ b/backend/src/db/schemas/secret-rotations.ts @@ -22,7 +22,7 @@ export const SecretRotationsSchema = z.object({ keyEncoding: z.string().nullable().optional(), envId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretRotations = z.infer; diff --git a/backend/src/db/schemas/secret-scanning-git-risks.ts b/backend/src/db/schemas/secret-scanning-git-risks.ts index 0951d3ba5..85cfcd376 100644 --- a/backend/src/db/schemas/secret-scanning-git-risks.ts +++ b/backend/src/db/schemas/secret-scanning-git-risks.ts @@ -38,7 +38,7 @@ export const SecretScanningGitRisksSchema = z.object({ status: z.string().nullable().optional(), orgId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretScanningGitRisks = z.infer; diff --git a/backend/src/db/schemas/secret-snapshot-folders.ts b/backend/src/db/schemas/secret-snapshot-folders.ts index 5f3b1a88c..acf11ab0a 100644 --- a/backend/src/db/schemas/secret-snapshot-folders.ts +++ b/backend/src/db/schemas/secret-snapshot-folders.ts @@ -13,7 +13,7 @@ export const SecretSnapshotFoldersSchema = z.object({ folderVersionId: z.string().uuid(), snapshotId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretSnapshotFolders = z.infer; diff --git a/backend/src/db/schemas/secret-snapshot-secrets.ts b/backend/src/db/schemas/secret-snapshot-secrets.ts index f8a69a695..6a83d1155 100644 --- a/backend/src/db/schemas/secret-snapshot-secrets.ts +++ b/backend/src/db/schemas/secret-snapshot-secrets.ts @@ -13,7 +13,7 @@ export const SecretSnapshotSecretsSchema = z.object({ secretVersionId: z.string().uuid(), snapshotId: z.string().uuid(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretSnapshotSecrets = z.infer; diff --git a/backend/src/db/schemas/secret-snapshots.ts b/backend/src/db/schemas/secret-snapshots.ts index ef9e0b7d0..ed255cb77 100644 --- a/backend/src/db/schemas/secret-snapshots.ts +++ b/backend/src/db/schemas/secret-snapshots.ts @@ -13,7 +13,7 @@ export const SecretSnapshotsSchema = z.object({ folderId: z.string().uuid(), parentFolderId: z.string().uuid().nullable().optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSecretSnapshots = z.infer; diff --git a/backend/src/db/schemas/secret-tag-junction.ts b/backend/src/db/schemas/secret-tag-junction.ts index 467ef7b15..1d25574c5 100644 --- a/backend/src/db/schemas/secret-tag-junction.ts +++ b/backend/src/db/schemas/secret-tag-junction.ts @@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models"; export const SecretTagJunctionSchema = z.object({ id: z.string().uuid(), secretsId: z.string().uuid(), - secret_tagsId: z.string().uuid(), + secret_tagsId: z.string().uuid() }); export type TSecretTagJunction = z.infer; diff --git a/backend/src/db/schemas/secret-tags.ts b/backend/src/db/schemas/secret-tags.ts index 78f03dedd..622c29bd3 100644 --- a/backend/src/db/schemas/secret-tags.ts +++ b/backend/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(), + projectId: z.string() }); export type TSecretTags = z.infer; diff --git a/backend/src/db/schemas/secret-version-tag-junction.ts b/backend/src/db/schemas/secret-version-tag-junction.ts index 11b3f0032..2c9a24fee 100644 --- a/backend/src/db/schemas/secret-version-tag-junction.ts +++ b/backend/src/db/schemas/secret-version-tag-junction.ts @@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models"; export const SecretVersionTagJunctionSchema = z.object({ id: z.string().uuid(), secret_versionsId: z.string().uuid(), - secret_tagsId: z.string().uuid(), + secret_tagsId: z.string().uuid() }); export type TSecretVersionTagJunction = z.infer; diff --git a/backend/src/db/schemas/service-tokens.ts b/backend/src/db/schemas/service-tokens.ts index c12e28432..24720f3e4 100644 --- a/backend/src/db/schemas/service-tokens.ts +++ b/backend/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(), + projectId: z.string() }); export type TServiceTokens = z.infer; diff --git a/backend/src/db/schemas/super-admin.ts b/backend/src/db/schemas/super-admin.ts index f998cf325..13bf45e7b 100644 --- a/backend/src/db/schemas/super-admin.ts +++ b/backend/src/db/schemas/super-admin.ts @@ -12,7 +12,7 @@ export const SuperAdminSchema = z.object({ initialized: z.boolean().default(false).nullable().optional(), allowSignUp: z.boolean().default(true).nullable().optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TSuperAdmin = z.infer; diff --git a/backend/src/db/schemas/trusted-ips.ts b/backend/src/db/schemas/trusted-ips.ts index c3311340d..6d9018db3 100644 --- a/backend/src/db/schemas/trusted-ips.ts +++ b/backend/src/db/schemas/trusted-ips.ts @@ -16,7 +16,7 @@ export const TrustedIpsSchema = z.object({ comment: z.string().nullable().optional(), projectId: z.string(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TTrustedIps = z.infer; diff --git a/backend/src/db/schemas/user-actions.ts b/backend/src/db/schemas/user-actions.ts index a3a07d699..eaa03ba98 100644 --- a/backend/src/db/schemas/user-actions.ts +++ b/backend/src/db/schemas/user-actions.ts @@ -12,7 +12,7 @@ export const UserActionsSchema = z.object({ action: z.string(), createdAt: z.date(), updatedAt: z.date(), - userId: z.string().uuid(), + userId: z.string().uuid() }); export type TUserActions = z.infer; diff --git a/backend/src/db/schemas/user-encryption-keys.ts b/backend/src/db/schemas/user-encryption-keys.ts index 938b93aaf..8f35b09fe 100644 --- a/backend/src/db/schemas/user-encryption-keys.ts +++ b/backend/src/db/schemas/user-encryption-keys.ts @@ -21,7 +21,7 @@ export const UserEncryptionKeysSchema = z.object({ tag: z.string(), salt: z.string(), verifier: z.string(), - userId: z.string().uuid(), + userId: z.string().uuid() }); export type TUserEncryptionKeys = z.infer; diff --git a/backend/src/db/schemas/users.ts b/backend/src/db/schemas/users.ts index b9689883d..4a29de510 100644 --- a/backend/src/db/schemas/users.ts +++ b/backend/src/db/schemas/users.ts @@ -19,7 +19,7 @@ export const UsersSchema = z.object({ mfaMethods: z.string().array().nullable().optional(), devices: z.unknown().nullable().optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date() }); export type TUsers = z.infer; diff --git a/backend/src/db/schemas/webhooks.ts b/backend/src/db/schemas/webhooks.ts index 2b7e36cab..7abfb3772 100644 --- a/backend/src/db/schemas/webhooks.ts +++ b/backend/src/db/schemas/webhooks.ts @@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models"; export const WebhooksSchema = z.object({ id: z.string().uuid(), - secretPath: z.string().default('/'), + secretPath: z.string().default("/"), url: z.string(), lastStatus: z.string().nullable().optional(), lastRunErrorMessage: z.string().nullable().optional(), @@ -21,7 +21,7 @@ export const WebhooksSchema = z.object({ keyEncoding: z.string().nullable().optional(), createdAt: z.date(), updatedAt: z.date(), - envId: z.string().uuid(), + envId: z.string().uuid() }); export type TWebhooks = z.infer; diff --git a/backend/src/db/seed-data.ts b/backend/src/db/seed-data.ts index a9dedbb11..bb57d5bb4 100644 --- a/backend/src/db/seed-data.ts +++ b/backend/src/db/seed-data.ts @@ -48,14 +48,12 @@ export const generateUserSrpKeys = async (password: string) => { await new Promise((resolve) => { client.init({ username: seedData1.email, password: seedData1.password }, () => resolve(null)); }); - const { salt, verifier } = await new Promise<{ salt: string; verifier: string }>( - (resolve, reject) => { - client.createVerifier((err, res) => { - if (err) return reject(err); - return resolve(res); - }); - } - ); + const { salt, verifier } = await new Promise<{ salt: string; verifier: string }>((resolve, reject) => { + client.createVerifier((err, res) => { + if (err) return reject(err); + return resolve(res); + }); + }); const derivedKey = await argon2.hash(password, { salt: Buffer.from(salt), memoryCost: 65536, diff --git a/backend/src/db/seeds/1-user.ts b/backend/src/db/seeds/1-user.ts index 7855ebc78..ca0042a98 100644 --- a/backend/src/db/seeds/1-user.ts +++ b/backend/src/db/seeds/1-user.ts @@ -14,7 +14,7 @@ export async function seed(knex: Knex): Promise { const [user] = await knex(TableName.Users) .insert([ { - // @ts-ignore to calculate predefined + // @ts-expect-error exluded type id needs to be inserted here to keep it testable id: seedData1.id, email: seedData1.email, superAdmin: true, @@ -48,7 +48,7 @@ export async function seed(knex: Knex): Promise { ]); await knex(TableName.AuthTokenSession).insert({ - // @ts-ignore + // @ts-expect-error exluded type id needs to be inserted here to keep it testable id: seedData1.token.id, userId: seedData1.id, ip: "151.196.220.213", diff --git a/backend/src/db/seeds/2-org.ts b/backend/src/db/seeds/2-org.ts index ec7e9dd43..a9c3ec3c7 100644 --- a/backend/src/db/seeds/2-org.ts +++ b/backend/src/db/seeds/2-org.ts @@ -14,7 +14,7 @@ export async function seed(knex: Knex): Promise { const [org] = await knex(TableName.Organization) .insert([ { - // @ts-ignore because we need that id for api calls + // @ts-expect-error exluded type id needs to be inserted here to keep it testable id: seedData1.organization.id, name: "infisical", slug: "infisical", diff --git a/backend/src/db/seeds/3-project.ts b/backend/src/db/seeds/3-project.ts index 48d4df5c1..7818d5831 100644 --- a/backend/src/db/seeds/3-project.ts +++ b/backend/src/db/seeds/3-project.ts @@ -20,7 +20,7 @@ export async function seed(knex: Knex): Promise { name: seedData1.project.name, orgId: seedData1.organization.id, slug: "first-project", - // @ts-ignore pre calc id + // @ts-expect-error exluded type id needs to be inserted here to keep it testable id: seedData1.project.id }) .returning("*"); @@ -45,7 +45,5 @@ export async function seed(knex: Knex): Promise { })) ) .returning("*"); - await knex(TableName.SecretFolder).insert( - envs.map(({ id }) => ({ name: "root", envId: id, parentId: null })) - ); + await knex(TableName.SecretFolder).insert(envs.map(({ id }) => ({ name: "root", envId: id, parentId: null }))); } diff --git a/backend/src/db/utils.ts b/backend/src/db/utils.ts index 51a097a4b..68c400596 100644 --- a/backend/src/db/utils.ts +++ b/backend/src/db/utils.ts @@ -2,12 +2,7 @@ import { Knex } from "knex"; import { TableName } from "./schemas"; -export const createJunctionTable = ( - knex: Knex, - tableName: TableName, - table1Name: TableName, - table2Name: TableName -) => +export const createJunctionTable = (knex: Knex, tableName: TableName, table1Name: TableName, table2Name: TableName) => knex.schema.createTable(tableName, (table) => { table.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); table.uuid(`${table1Name}Id`).unsigned().notNullable(); // Foreign key for table1 diff --git a/backend/src/ee/routes/v1/license-router.ts b/backend/src/ee/routes/v1/license-router.ts index 675dcdc00..e560fc842 100644 --- a/backend/src/ee/routes/v1/license-router.ts +++ b/backend/src/ee/routes/v1/license-router.ts @@ -1,3 +1,6 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +// TODO(akhilmhdh): Fix this when licence service gets it type import { z } from "zod"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; @@ -79,7 +82,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const data = await server.services.license.startOrgTrail({ + const data = await server.services.license.startOrgTrial({ actorId: req.permission.id, actor: req.permission.type, orgId: req.params.organizationId, @@ -89,6 +92,26 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => { } }); + server.route({ + url: "/:organizationId/customer-portal-session", + method: "POST", + schema: { + params: z.object({ organizationId: z.string().trim() }), + response: { + 200: z.any() + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const data = await server.services.license.createOrganizationPortalSession({ + actorId: req.permission.id, + actor: req.permission.type, + orgId: req.params.organizationId + }); + return data; + } + }); + server.route({ url: "/:organizationId/plan/billing", method: "GET", diff --git a/backend/src/ee/routes/v1/org-role-router.ts b/backend/src/ee/routes/v1/org-role-router.ts index 79e392880..4890c97a5 100644 --- a/backend/src/ee/routes/v1/org-role-router.ts +++ b/backend/src/ee/routes/v1/org-role-router.ts @@ -26,12 +26,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - - const role = await server.services.orgRole.createRole( - req.permission.id, - req.params.organizationId, - req.body - ); + const role = await server.services.orgRole.createRole(req.permission.id, req.params.organizationId, req.body); return { role }; } }); @@ -58,7 +53,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const role = await server.services.orgRole.updateRole( req.permission.id, req.params.organizationId, @@ -85,7 +79,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const role = await server.services.orgRole.deleteRole( req.permission.id, req.params.organizationId, @@ -114,11 +107,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - - const roles = await server.services.orgRole.listRoles( - req.permission.id, - req.params.organizationId - ); + const roles = await server.services.orgRole.listRoles(req.permission.id, req.params.organizationId); return { data: { roles } }; } }); @@ -139,7 +128,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const { permissions, membership } = await server.services.orgRole.getUserPermission( req.permission.id, req.params.organizationId diff --git a/backend/src/ee/routes/v1/project-role-router.ts b/backend/src/ee/routes/v1/project-role-router.ts index 622762bca..86f2242e2 100644 --- a/backend/src/ee/routes/v1/project-role-router.ts +++ b/backend/src/ee/routes/v1/project-role-router.ts @@ -141,7 +141,6 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const { permissions, membership } = await server.services.projectRole.getUserPermission( req.permission.id, req.params.projectId diff --git a/backend/src/ee/routes/v1/saml-router.ts b/backend/src/ee/routes/v1/saml-router.ts index a8e29d08c..6ddebb388 100644 --- a/backend/src/ee/routes/v1/saml-router.ts +++ b/backend/src/ee/routes/v1/saml-router.ts @@ -1,3 +1,11 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +// All the any rules are disabled because passport typesense with fastify is really poor + import { Authenticator } from "@fastify/passport"; import fastifySession from "@fastify/session"; import { MultiSamlStrategy } from "@node-saml/passport-saml"; @@ -33,6 +41,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { new MultiSamlStrategy( { passReqToCallback: true, + // eslint-disable-next-line getSamlOptions: async (req, done) => { try { const { ssoIdentifier } = req.params; @@ -67,13 +76,17 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { } } }, + // eslint-disable-next-line async (req, profile, cb) => { try { const serverCfg = getServerCfg(); if (!profile) throw new BadRequestError({ message: "Missing profile" }); - const { email, firstName } = profile; - if (!email || !firstName) + const { firstName } = profile; + const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved + + if (!email || !firstName) { throw new BadRequestError({ message: "Invalid request. Missing email or first name" }); + } const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({ email, @@ -137,15 +150,11 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { handler: (req, res) => { if (req.passportUser.isUserCompleted) { return res.redirect( - `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } return res.redirect( - `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } }); diff --git a/backend/src/ee/routes/v1/secret-approval-policy-router.ts b/backend/src/ee/routes/v1/secret-approval-policy-router.ts index 49d2b95ad..dda8dbe38 100644 --- a/backend/src/ee/routes/v1/secret-approval-policy-router.ts +++ b/backend/src/ee/routes/v1/secret-approval-policy-router.ts @@ -111,7 +111,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi }), response: { 200: z.object({ - approvals: sapPubSchema.merge(z.object({approvers:z.string().array()})).array() + approvals: sapPubSchema.merge(z.object({ approvers: z.string().array() })).array() }) } }, @@ -137,7 +137,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi }), response: { 200: z.object({ - policy: sapPubSchema.merge(z.object({approvers:z.string().array()})).optional() + policy: sapPubSchema.merge(z.object({ approvers: z.string().array() })).optional() }) } }, diff --git a/backend/src/ee/routes/v1/secret-approval-request-router.ts b/backend/src/ee/routes/v1/secret-approval-request-router.ts index a745a9b04..f33e8b0d0 100644 --- a/backend/src/ee/routes/v1/secret-approval-request-router.ts +++ b/backend/src/ee/routes/v1/secret-approval-request-router.ts @@ -9,10 +9,7 @@ import { SecretVersionsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; -import { - ApprovalStatus, - RequestState -} from "@app/ee/services/secret-approval-request/secret-approval-request-types"; +import { ApprovalStatus, RequestState } from "@app/ee/services/secret-approval-request/secret-approval-request-types"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -41,9 +38,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv approvers: z.string().array(), secretPath: z.string().optional().nullable() }), - commits: z - .object({ op: z.string(), secretId: z.string().nullable().optional() }) - .array(), + commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(), environment: z.string(), reviewers: z.object({ member: z.string(), status: z.string() }).array(), approvers: z.string().array() @@ -174,11 +169,12 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv ...req.auditLogInfo, event: { type: isClosing ? EventType.SECRET_APPROVAL_CLOSED : EventType.SECRET_APPROVAL_REOPENED, + // eslint-disable-next-line metadata: { - [isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: - approval.statusChangeBy as string, + [isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangeBy as string, secretApprovalRequestId: approval.id, secretApprovalRequestSlug: approval.slug + // eslint-disable-next-line } as any // akhilmhdh: had to apply any to avoid ts issue with this } diff --git a/backend/src/ee/routes/v1/secret-scanning-router.ts b/backend/src/ee/routes/v1/secret-scanning-router.ts index 48898eddb..2970a4308 100644 --- a/backend/src/ee/routes/v1/secret-scanning-router.ts +++ b/backend/src/ee/routes/v1/secret-scanning-router.ts @@ -62,12 +62,11 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) = }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const appInstallationCompleted = - await server.services.secretScanning.getOrgInstallationStatus({ - actor: req.permission.type, - actorId: req.permission.id, - orgId: req.params.organizationId - }); + const appInstallationCompleted = await server.services.secretScanning.getOrgInstallationStatus({ + actor: req.permission.type, + actorId: req.permission.id, + orgId: req.params.organizationId + }); return { appInstallationCompleted }; } }); diff --git a/backend/src/ee/services/audit-log/audit-log-dal.ts b/backend/src/ee/services/audit-log/audit-log-dal.ts index ec5f6ab3d..b3ad8c2b6 100644 --- a/backend/src/ee/services/audit-log/audit-log-dal.ts +++ b/backend/src/ee/services/audit-log/audit-log-dal.ts @@ -2,6 +2,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; +import { DatabaseError } from "@app/lib/errors"; import { ormify, stripUndefinedInWhere } from "@app/lib/knex"; export type TAuditLogDALFactory = ReturnType; @@ -22,40 +23,46 @@ export const auditLogDALFactory = (db: TDbClient) => { const auditLogOrm = ormify(db, TableName.AuditLog); const find = async ( - { - orgId, - projectId, - userAgentType, - startDate, - endDate, - limit = 20, - offset = 0, - actor, - eventType - }: TFindQuery, + { orgId, projectId, userAgentType, startDate, endDate, limit = 20, offset = 0, actor, eventType }: TFindQuery, tx?: Knex ) => { - const sqlQuery = (tx || db)(TableName.AuditLog) - .where( - stripUndefinedInWhere({ - projectId, - orgId, - eventType, - actor, - userAgentType - }) - ) - .limit(limit) - .offset(offset); - if (startDate) { - sqlQuery.where("createdAt", ">=", startDate); + try { + const sqlQuery = (tx || db)(TableName.AuditLog) + .where( + stripUndefinedInWhere({ + projectId, + orgId, + eventType, + actor, + userAgentType + }) + ) + .limit(limit) + .offset(offset) + .orderBy("createdAt", "desc"); + if (startDate) { + void sqlQuery.where("createdAt", ">=", startDate); + } + if (endDate) { + void sqlQuery.where("createdAt", "<=", endDate); + } + const docs = await sqlQuery; + return docs; + } catch (error) { + throw new DatabaseError({ error }); } - if (endDate) { - sqlQuery.where("createdAt", "<=", endDate); - } - const docs = await sqlQuery; - return docs; }; - return { ...auditLogOrm, find }; + // delete all audit log that have expired + const pruneAuditLog = async (tx?: Knex) => { + try { + const today = new Date(); + const docs = await (tx || db)(TableName.AuditLog).where("expiresAt", "<", today).del(); + return docs; + } catch (error) { + throw new DatabaseError({ error, name: "PruneAuditLog" }); + } + }; + + return { ...auditLogOrm, pruneAuditLog, find }; }; diff --git a/backend/src/ee/services/audit-log/audit-log-queue.ts b/backend/src/ee/services/audit-log/audit-log-queue.ts index bcdf92920..6f2c93221 100644 --- a/backend/src/ee/services/audit-log/audit-log-queue.ts +++ b/backend/src/ee/services/audit-log/audit-log-queue.ts @@ -1,3 +1,4 @@ +import { logger } from "@app/lib/logger"; import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue"; import { TProjectDALFactory } from "@app/services/project/project-dal"; @@ -43,6 +44,8 @@ export const auditLogQueueServiceFactory = ({ const plan = await licenseService.getPlan(orgId); const ttl = plan.auditLogsRetentionDays * MS_IN_DAY; + // skip inserting if audit log retention is 0 meaning its not supported + if (ttl === 0) return; await auditLogDAL.create({ actor: actor.type, actorMetadata: actor.metadata, @@ -57,7 +60,35 @@ export const auditLogQueueServiceFactory = ({ }); }); + queueService.start(QueueName.AuditLogPrune, async () => { + logger.info(`${QueueName.AuditLogPrune}: queue task started`); + await auditLogDAL.pruneAuditLog(); + logger.info(`${QueueName.AuditLogPrune}: queue task completed`); + }); + + // we do a repeat cron job in utc timezone at 12 Midnight each day + const startAuditLogPruneJob = async () => { + // clear previous job + await queueService.stopRepeatableJob( + QueueName.AuditLogPrune, + QueueJobs.AuditLogPrune, + { pattern: "0 0 * * *", utc: true }, + QueueName.AuditLogPrune // just a job id + ); + + await queueService.queue(QueueName.AuditLogPrune, QueueJobs.AuditLogPrune, undefined, { + delay: 5000, + jobId: QueueName.AuditLogPrune, + repeat: { pattern: "0 0 * * *", utc: true } + }); + }; + + queueService.listen(QueueName.AuditLogPrune, "failed", (err) => { + logger.error(err?.failedReason, `${QueueName.AuditLogPrune}: log pruning failed`); + }); + return { - pushToLog + pushToLog, + startAuditLogPruneJob }; }; diff --git a/backend/src/ee/services/audit-log/audit-log-service.ts b/backend/src/ee/services/audit-log/audit-log-service.ts index 7f5d0007f..c1d5c6925 100644 --- a/backend/src/ee/services/audit-log/audit-log-service.ts +++ b/backend/src/ee/services/audit-log/audit-log-service.ts @@ -34,10 +34,7 @@ export const auditLogServiceFactory = ({ auditLogActor }: TListProjectAuditLogDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.AuditLogs - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs); const auditLogs = await auditLogDAL.find({ startDate, endDate, @@ -48,20 +45,17 @@ export const auditLogServiceFactory = ({ actor: auditLogActor, projectId }); - return auditLogs.map( - ({ eventType: logEventType, actor: eActor, actorMetadata, eventMetadata, ...el }) => ({ - ...el, - event: { type: logEventType, metadata: eventMetadata }, - actor: { type: eActor, metadata: actorMetadata } - }) - ); + return auditLogs.map(({ eventType: logEventType, actor: eActor, actorMetadata, eventMetadata, ...el }) => ({ + ...el, + event: { type: logEventType, metadata: eventMetadata }, + actor: { type: eActor, metadata: actorMetadata } + })); }; const createAuditLog = async (data: TCreateAuditLogDTO) => { // add all cases in which project id or org id cannot be added if (data.event.type !== EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH) { - if (!data.projectId && !data.orgId) - throw new BadRequestError({ message: "Must either project id or org id" }); + if (!data.projectId && !data.orgId) throw new BadRequestError({ message: "Must either project id or org id" }); } return auditLogQueue.pushToLog(data); }; diff --git a/backend/src/ee/services/license/licence-fns.ts b/backend/src/ee/services/license/licence-fns.ts index ca69b7d0e..7014eac5f 100644 --- a/backend/src/ee/services/license/licence-fns.ts +++ b/backend/src/ee/services/license/licence-fns.ts @@ -31,15 +31,11 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ secretRotation: true }); -export const setupLicenceRequestWithStore = ( - baseURL: string, - refreshUrl: string, - licenseKey: string -) => { +export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => { let token: string; const licenceReq = axios.create({ baseURL, - timeout: 35 * 1000, + timeout: 35 * 1000 // signal: AbortSignal.timeout(60 * 1000) }); @@ -47,7 +43,7 @@ export const setupLicenceRequestWithStore = ( const appCfg = getConfig(); const { data: { token: authToken } - } = await request.post( + } = await request.post<{ token: string }>( refreshUrl, {}, { @@ -75,18 +71,18 @@ export const setupLicenceRequestWithStore = ( licenceReq.interceptors.response.use( (response) => response, async (err) => { - const originalRequest = err.config; + const originalRequest = (err as AxiosError).config; // eslint-disable-next-line - if ((err as AxiosError)?.response?.status === 401 && !originalRequest._retry) { + if ((err as AxiosError)?.response?.status === 401 && !(originalRequest as any)._retry) { // eslint-disable-next-line - originalRequest._retry = true; + (originalRequest as any)._retry = true; // injected // refresh await refreshLicence(); licenceReq.defaults.headers.common.Authorization = `Bearer ${token}`; - return licenceReq(originalRequest); + return licenceReq(originalRequest!); } return Promise.reject(err); diff --git a/backend/src/ee/services/license/license-dal.ts b/backend/src/ee/services/license/license-dal.ts index 208cb6f9b..4e70dfb5a 100644 --- a/backend/src/ee/services/license/license-dal.ts +++ b/backend/src/ee/services/license/license-dal.ts @@ -13,7 +13,7 @@ export const licenseDALFactory = (db: TDbClient) => { .where({ status: OrgMembershipStatus.Accepted }) .andWhere((bd) => { if (orgId) { - bd.where({ orgId }); + void bd.where({ orgId }); } }) .count(); diff --git a/backend/src/ee/services/license/license-service.ts b/backend/src/ee/services/license/license-service.ts index d633916e0..6a7073b4e 100644 --- a/backend/src/ee/services/license/license-service.ts +++ b/backend/src/ee/services/license/license-service.ts @@ -1,3 +1,9 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +// eslint-disable @typescript-eslint/no-unsafe-assignment + +// TODO(akhilmhdh): With tony find out the api structure and fill it here + import { ForbiddenError } from "@casl/ability"; import NodeCache from "node-cache"; @@ -14,6 +20,7 @@ import { InstanceType, TAddOrgPmtMethodDTO, TAddOrgTaxIdDTO, + TCreateOrgPortalSession, TDelOrgPmtMethodDTO, TDelOrgTaxIdDTO, TFeatureSet, @@ -24,7 +31,7 @@ import { TOrgPlanDTO, TOrgPlansTableDTO, TOrgPmtMethodsDTO, - TStartOrgTrailDTO, + TStartOrgTrialDTO, TUpdateOrgBillingDetailsDTO } from "./license-types"; @@ -40,11 +47,7 @@ const LICENSE_SERVER_CLOUD_LOGIN = "/api/auth/v1/license-server-login"; const LICENSE_SERVER_ON_PREM_LOGIN = "/api/auth/v1/licence-login"; const FEATURE_CACHE_KEY = (orgId: string, projectId?: string) => `${orgId}-${projectId || ""}`; -export const licenseServiceFactory = ({ - orgDAL, - permissionService, - licenseDAL -}: TLicenseServiceFactoryDep) => { +export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }: TLicenseServiceFactoryDep) => { let isValidLicense = false; let instanceType = InstanceType.OnPrem; let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures(); @@ -77,9 +80,7 @@ export const licenseServiceFactory = ({ if (token) { const { data: { currentPlan } - } = await licenseServerOnPremApi.request.get<{ currentPlan: TFeatureSet }>( - "/api/license/v1/plan" - ); + } = await licenseServerOnPremApi.request.get<{ currentPlan: TFeatureSet }>("/api/license/v1/plan"); onPremFeatures = currentPlan; instanceType = InstanceType.EnterpriseOnPrem; logger.info(`Instance type: ${InstanceType.EnterpriseOnPrem}`); @@ -91,7 +92,7 @@ export const licenseServiceFactory = ({ // else it would reach catch statement isValidLicense = true; } catch (error) { - logger.error(`init-license: encountered an error when init license [error=${error}]`); + logger.error(`init-license: encountered an error when init license [error]`, error); } }; @@ -118,7 +119,10 @@ export const licenseServiceFactory = ({ return currentPlan; } } catch (error) { - logger.error(`getPlan: encountered an error when fetching pan [orgId=${orgId}] [projectId=${projectId}] [error=${error}]`); + logger.error( + `getPlan: encountered an error when fetching pan [orgId=${orgId}] [projectId=${projectId}] [error]`, + error + ); return onPremFeatures; } return onPremFeatures; @@ -135,7 +139,7 @@ export const licenseServiceFactory = ({ if (instanceType === InstanceType.Cloud) { const { data: { customerId } - } = await licenseServerCloudApi.request.post( + } = await licenseServerCloudApi.request.post<{ customerId: string }>( "/api/license-server/v1/customers", { email, @@ -158,12 +162,9 @@ export const licenseServiceFactory = ({ const count = await licenseDAL.countOfOrgMembers(orgId); if (org?.customerId) { - await licenseServerCloudApi.request.patch( - `/api/license-server/v1/customers/${org.customerId}/cloud-plan`, - { - quantity: count - } - ); + await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, { + quantity: count + }); } featureStore.del(orgId); } else if (instanceType === InstanceType.EnterpriseOnPrem) { @@ -174,17 +175,9 @@ export const licenseServiceFactory = ({ }; // below all are api calls - const getOrgPlansTableByBillCycle = async ({ - orgId, - actor, - actorId, - billingCycle - }: TOrgPlansTableDTO) => { + const getOrgPlansTableByBillCycle = async ({ orgId, actor, actorId, billingCycle }: TOrgPlansTableDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const { data } = await licenseServerCloudApi.request.get( `/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}` ); @@ -193,24 +186,15 @@ export const licenseServiceFactory = ({ const getOrgPlan = async ({ orgId, actor, actorId, projectId }: TOrgPlanDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const plan = await getPlan(orgId, projectId); return plan; }; - const startOrgTrail = async ({ orgId, actorId, actor, success_url }: TStartOrgTrailDTO) => { + const startOrgTrial = async ({ orgId, actorId, actor, success_url }: TStartOrgTrialDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Billing - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -222,19 +206,63 @@ export const licenseServiceFactory = ({ const { data: { url } } = await licenseServerCloudApi.request.post( - `/api/license-server/v1/customers/${organization.customerId}/session/trail`, + `/api/license-server/v1/customers/${organization.customerId}/session/trial`, { success_url } ); featureStore.del(FEATURE_CACHE_KEY(orgId)); return { url }; }; + const createOrganizationPortalSession = async ({ orgId, actorId, actor }: TCreateOrgPortalSession) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing); + + const organization = await orgDAL.findOrgById(orgId); + if (!organization) { + throw new BadRequestError({ + message: "Failed to find organization" + }); + } + + const { + data: { pmtMethods } + } = await licenseServerCloudApi.request.get<{ pmtMethods: string[] }>( + `/api/license-server/v1/customers/${organization.customerId}/billing-details/payment-methods` + ); + + if (pmtMethods.length < 1) { + // case: organization has no payment method on file + // -> redirect to add payment method portal + const { + data: { url } + } = await licenseServerCloudApi.request.post( + `/api/license-server/v1/customers/${organization.customerId}/billing-details/payment-methods`, + { + success_url: `${appCfg.SITE_URL}/dashboard`, + cancel_url: `${appCfg.SITE_URL}/dashboard` + } + ); + + return { url }; + } + // case: organization has payment method on file + // -> redirect to billing portal + const { + data: { url } + } = await licenseServerCloudApi.request.post( + `/api/license-server/v1/customers/${organization.customerId}/billing-details/billing-portal`, + { + return_url: `${appCfg.SITE_URL}/dashboard` + } + ); + + return { url }; + }; + const getOrgBillingInfo = async ({ orgId, actor, actorId }: TGetOrgBillInfoDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -251,10 +279,7 @@ export const licenseServiceFactory = ({ // returns org current plan feature table const getOrgPlanTable = async ({ orgId, actor, actorId }: TGetOrgBillInfoDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -270,10 +295,7 @@ export const licenseServiceFactory = ({ const getOrgBillingDetails = async ({ orgId, actor, actorId }: TGetOrgBillInfoDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -288,18 +310,9 @@ export const licenseServiceFactory = ({ return data; }; - const updateOrgBillingDetails = async ({ - actorId, - actor, - orgId, - name, - email - }: TUpdateOrgBillingDetailsDTO) => { + const updateOrgBillingDetails = async ({ actorId, actor, orgId, name, email }: TUpdateOrgBillingDetailsDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -319,10 +332,7 @@ export const licenseServiceFactory = ({ const getOrgPmtMethods = async ({ orgId, actor, actorId }: TOrgPmtMethodsDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -339,18 +349,9 @@ export const licenseServiceFactory = ({ return pmtMethods; }; - const addOrgPmtMethods = async ({ - orgId, - actor, - actorId, - success_url, - cancel_url - }: TAddOrgPmtMethodDTO) => { + const addOrgPmtMethods = async ({ orgId, actor, actorId, success_url, cancel_url }: TAddOrgPmtMethodDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -372,10 +373,7 @@ export const licenseServiceFactory = ({ const delOrgPmtMethods = async ({ actorId, actor, orgId, pmtMethodId }: TDelOrgPmtMethodDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -392,10 +390,7 @@ export const licenseServiceFactory = ({ const getOrgTaxIds = async ({ orgId, actor, actorId }: TGetOrgTaxIdDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -413,10 +408,7 @@ export const licenseServiceFactory = ({ const addOrgTaxId = async ({ actorId, actor, orgId, type, value }: TAddOrgTaxIdDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -437,10 +429,7 @@ export const licenseServiceFactory = ({ const delOrgTaxId = async ({ orgId, actor, actorId, taxId }: TDelOrgTaxIdDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -457,10 +446,7 @@ export const licenseServiceFactory = ({ const getOrgTaxInvoices = async ({ actorId, actor, orgId }: TOrgInvoiceDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -471,18 +457,13 @@ export const licenseServiceFactory = ({ const { data: { invoices } - } = await licenseServerCloudApi.request.get( - `/api/license-server/v1/customers/${organization.customerId}/invoices` - ); + } = await licenseServerCloudApi.request.get(`/api/license-server/v1/customers/${organization.customerId}/invoices`); return invoices; }; const getOrgLicenses = async ({ orgId, actor, actorId }: TOrgLicensesDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Billing - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -493,9 +474,7 @@ export const licenseServiceFactory = ({ const { data: { licenses } - } = await licenseServerCloudApi.request.get( - `/api/license-server/v1/customers/${organization.customerId}/licenses` - ); + } = await licenseServerCloudApi.request.get(`/api/license-server/v1/customers/${organization.customerId}/licenses`); return licenses; }; @@ -511,7 +490,8 @@ export const licenseServiceFactory = ({ refreshPlan, getOrgPlan, getOrgPlansTableByBillCycle, - startOrgTrail, + startOrgTrial, + createOrganizationPortalSession, getOrgBillingInfo, getOrgPlanTable, getOrgBillingDetails, diff --git a/backend/src/ee/services/license/license-types.ts b/backend/src/ee/services/license/license-types.ts index f4cd2db1c..762aff5b2 100644 --- a/backend/src/ee/services/license/license-types.ts +++ b/backend/src/ee/services/license/license-types.ts @@ -40,10 +40,12 @@ export type TOrgPlanDTO = { projectId?: string; } & TOrgPermission; -export type TStartOrgTrailDTO = { +export type TStartOrgTrialDTO = { success_url: string; } & TOrgPermission; +export type TCreateOrgPortalSession = TOrgPermission; + export type TGetOrgBillInfoDTO = TOrgPermission; export type TOrgPlanTableDTO = TOrgPermission; diff --git a/backend/src/ee/services/permission/permission-dal.ts b/backend/src/ee/services/permission/permission-dal.ts index d2529f18b..cc35fb04e 100644 --- a/backend/src/ee/services/permission/permission-dal.ts +++ b/backend/src/ee/services/permission/permission-dal.ts @@ -9,11 +9,7 @@ export const permissionDALFactory = (db: TDbClient) => { const getOrgPermission = async (userId: string, orgId: string) => { try { const membership = await db(TableName.OrgMembership) - .leftJoin( - TableName.OrgRoles, - `${TableName.OrgMembership}.roleId`, - `${TableName.OrgRoles}.id` - ) + .leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`) .where("userId", userId) .where(`${TableName.OrgMembership}.orgId`, orgId) .select("permissions") @@ -29,11 +25,7 @@ export const permissionDALFactory = (db: TDbClient) => { const getOrgIdentityPermission = async (identityId: string, orgId: string) => { try { const membership = await db(TableName.IdentityOrgMembership) - .leftJoin( - TableName.OrgRoles, - `${TableName.IdentityOrgMembership}.roleId`, - `${TableName.OrgRoles}.id` - ) + .leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`) .where("identityId", identityId) .where(`${TableName.IdentityOrgMembership}.orgId`, orgId) .select(selectAllTableCols(TableName.IdentityOrgMembership)) @@ -48,11 +40,7 @@ export const permissionDALFactory = (db: TDbClient) => { const getProjectPermission = async (userId: string, projectId: string) => { try { const membership = await db(TableName.ProjectMembership) - .leftJoin( - TableName.ProjectRoles, - `${TableName.ProjectMembership}.roleId`, - `${TableName.ProjectRoles}.id` - ) + .leftJoin(TableName.ProjectRoles, `${TableName.ProjectMembership}.roleId`, `${TableName.ProjectRoles}.id`) .where("userId", userId) .where(`${TableName.ProjectMembership}.projectId`, projectId) .select(selectAllTableCols(TableName.ProjectMembership)) diff --git a/backend/src/ee/services/permission/permission-service.ts b/backend/src/ee/services/permission/permission-service.ts index 8549d00ee..3daf1c20a 100644 --- a/backend/src/ee/services/permission/permission-service.ts +++ b/backend/src/ee/services/permission/permission-service.ts @@ -16,12 +16,7 @@ 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, - orgMemberPermissions, - orgNoAccessPermissions, - OrgPermissionSet -} from "./org-permission"; +import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission"; import { TPermissionDALFactory } from "./permission-dal"; import { buildServiceTokenProjectPermission, @@ -188,9 +183,9 @@ export const permissionServiceFactory = ({ ? { permission: MongoAbility; membership: undefined } : { permission: MongoAbility; - membership: (T extends ActorType.USER - ? TProjectMemberships - : TIdentityProjectMemberships) & { permissions?: unknown }; + membership: (T extends ActorType.USER ? TProjectMemberships : TIdentityProjectMemberships) & { + permissions?: unknown; + }; }; const getProjectPermission = async ( @@ -214,9 +209,7 @@ export const permissionServiceFactory = ({ }; const getProjectPermissionByRole = async (role: string, projectId: string) => { - const isCustomRole = !Object.values(ProjectMembershipRole).includes( - role as ProjectMembershipRole - ); + const isCustomRole = !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole); if (isCustomRole) { const projectRole = await projectRoleDAL.findOne({ slug: role, projectId }); if (!projectRole) throw new BadRequestError({ message: "Role not found" }); diff --git a/backend/src/ee/services/permission/project-permission.ts b/backend/src/ee/services/permission/project-permission.ts index f3fb67705..5245c26e4 100644 --- a/backend/src/ee/services/permission/project-permission.ts +++ b/backend/src/ee/services/permission/project-permission.ts @@ -239,17 +239,29 @@ export const buildServiceTokenProjectPermission = ( const { can, build } = new AbilityBuilder>(createMongoAbility); scopes.forEach(({ secretPath, environment }) => { if (canWrite) { - // TODO: @Akhi - // @ts-expect-error type - can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets, { secretPath: { $glob: secretPath }, environment }); - // @ts-expect-error type - can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets, { secretPath: { $glob: secretPath }, environment }); - // @ts-expect-error type - can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets, {secretPath: { $glob: secretPath }, environment }); + // TODO: @Akhi + // @ts-expect-error type + can(ProjectPermissionActions.Edit, ProjectPermissionSub.Secrets, { + secretPath: { $glob: secretPath }, + environment + }); + // @ts-expect-error type + can(ProjectPermissionActions.Create, ProjectPermissionSub.Secrets, { + secretPath: { $glob: secretPath }, + environment + }); + // @ts-expect-error type + can(ProjectPermissionActions.Delete, ProjectPermissionSub.Secrets, { + secretPath: { $glob: secretPath }, + environment + }); } if (canRead) { - // @ts-expect-error type - can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets, { secretPath: { $glob: secretPath }, environment }); + // @ts-expect-error type + can(ProjectPermissionActions.Read, ProjectPermissionSub.Secrets, { + secretPath: { $glob: secretPath }, + environment + }); } }); @@ -258,6 +270,8 @@ export const buildServiceTokenProjectPermission = ( export const projectNoAccessPermissions = buildNoAccessProjectPermission(); +/* eslint-disable */ + /** * Extracts and formats permissions from a CASL Ability object or a raw permission set. * @param ability @@ -287,3 +301,5 @@ export const isAtLeastAsPrivilegedWorkspace = ( return set1.size >= set2.size; }; + +/* eslint-enable */ diff --git a/backend/src/ee/services/saml-config/saml-config-service.ts b/backend/src/ee/services/saml-config/saml-config-service.ts index ad6a34a37..9da2dd122 100644 --- a/backend/src/ee/services/saml-config/saml-config-service.ts +++ b/backend/src/ee/services/saml-config/saml-config-service.ts @@ -38,10 +38,7 @@ import { type TSamlConfigServiceFactoryDep = { samlConfigDAL: TSamlConfigDALFactory; userDAL: Pick; - orgDAL: Pick< - TOrgDALFactory, - "createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" - >; + orgDAL: Pick; orgBotDAL: Pick; permissionService: Pick; licenseService: Pick; @@ -68,10 +65,7 @@ export const samlConfigServiceFactory = ({ authProvider }: TCreateSamlCfgDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Sso - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso); const plan = await licenseService.getPlan(orgId); if (!plan.samlSSO) @@ -128,16 +122,8 @@ export const samlConfigServiceFactory = ({ keyEncoding: orgBot.symmetricKeyKeyEncoding as SecretKeyEncoding }); - const { - ciphertext: encryptedEntryPoint, - iv: entryPointIV, - tag: entryPointTag - } = encryptSymmetric(entryPoint, key); - const { - ciphertext: encryptedIssuer, - iv: issuerIV, - tag: issuerTag - } = encryptSymmetric(issuer, key); + const { ciphertext: encryptedEntryPoint, iv: entryPointIV, tag: entryPointTag } = encryptSymmetric(entryPoint, key); + const { ciphertext: encryptedIssuer, iv: issuerIV, tag: issuerTag } = encryptSymmetric(issuer, key); const { ciphertext: encryptedCert, iv: certIV, tag: certTag } = encryptSymmetric(cert, key); const samlConfig = await samlConfigDAL.create({ @@ -168,10 +154,7 @@ export const samlConfigServiceFactory = ({ authProvider }: TUpdateSamlCfgDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Sso - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso); const plan = await licenseService.getPlan(orgId); if (!plan.samlSSO) throw new BadRequestError({ @@ -181,8 +164,7 @@ export const samlConfigServiceFactory = ({ const updateQuery: TSamlConfigsUpdate = { authProvider, isActive }; const orgBot = await orgBotDAL.findOne({ orgId }); - if (!orgBot) - throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" }); + if (!orgBot) throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" }); const key = infisicalSymmetricDecrypt({ ciphertext: orgBot.encryptedSymmetricKey, iv: orgBot.symmetricKeyIV, @@ -201,11 +183,7 @@ export const samlConfigServiceFactory = ({ updateQuery.entryPointTag = entryPointTag; } if (issuer) { - const { - ciphertext: encryptedIssuer, - iv: issuerIV, - tag: issuerTag - } = encryptSymmetric(issuer, key); + const { ciphertext: encryptedIssuer, iv: issuerIV, tag: issuerTag } = encryptSymmetric(issuer, key); updateQuery.encryptedIssuer = encryptedIssuer; updateQuery.issuerIV = issuerIV; updateQuery.issuerTag = issuerTag; @@ -239,9 +217,9 @@ export const samlConfigServiceFactory = ({ "64f23239a5d4ed17f1e544c4": "9256337f-e3da-43d7-8266-39c9276e8426", "65348e49db355e6e4782571f": "b8a227c7-843e-410e-8982-b4976a599b69", "657a219fc8a80c2eff97eb38": "fcab1573-ae7f-4fcf-9645-646207acf035" - }; + }; - const id = UUIDToMongoId[dto.id] ?? dto.id + const id = UUIDToMongoId[dto.id] ?? dto.id; ssoConfig = await samlConfigDAL.findById(id); } @@ -249,15 +227,8 @@ export const samlConfigServiceFactory = ({ // when dto is type id means it's internally used if (dto.type === "org") { - const { permission } = await permissionService.getOrgPermission( - dto.actor, - dto.actorId, - ssoConfig!.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Sso - ); + const { permission } = await permissionService.getOrgPermission(dto.actor, dto.actorId, ssoConfig.orgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso); } const { entryPointTag, @@ -272,8 +243,7 @@ export const samlConfigServiceFactory = ({ } = ssoConfig; const orgBot = await orgBotDAL.findOne({ orgId: ssoConfig.orgId }); - if (!orgBot) - throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" }); + if (!orgBot) throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" }); const key = infisicalSymmetricDecrypt({ ciphertext: orgBot.encryptedSymmetricKey, iv: orgBot.symmetricKeyIV, @@ -330,8 +300,7 @@ export const samlConfigServiceFactory = ({ const appCfg = getConfig(); let user = await userDAL.findUserByEmail(email); const isSamlSignUpDisabled = !isSignupAllowed && !user; - if (isSamlSignUpDisabled) - throw new BadRequestError({ message: "User signup disabled", name: "Saml SSO login" }); + if (isSamlSignUpDisabled) throw new BadRequestError({ message: "User signup disabled", name: "Saml SSO login" }); const organization = await orgDAL.findOrgById(orgId); if (!organization) throw new BadRequestError({ message: "Org not found" }); @@ -400,7 +369,7 @@ export const samlConfigServiceFactory = ({ isUserCompleted, ...(relayState ? { - callbackPort: JSON.parse(relayState).callbackPort as string + callbackPort: (JSON.parse(relayState) as { callbackPort: string }).callbackPort } : {}) }, diff --git a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-approver-dal.ts b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-approver-dal.ts index cee303436..f32439499 100644 --- a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-approver-dal.ts +++ b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-approver-dal.ts @@ -2,9 +2,7 @@ import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; import { ormify } from "@app/lib/knex"; -export type TSecretApprovalPolicyApproverDALFactory = ReturnType< - typeof secretApprovalPolicyApproverDALFactory ->; +export type TSecretApprovalPolicyApproverDALFactory = ReturnType; export const secretApprovalPolicyApproverDALFactory = (db: TDbClient) => { const sapApproverOrm = ormify(db, TableName.SecretApprovalPolicyApprover); diff --git a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts index ea895262d..eec3d9a1d 100644 --- a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts +++ b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts @@ -3,13 +3,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; import { TableName, TSecretApprovalPolicies } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { - buildFindFilter, - mergeOneToManyRelation, - ormify, - selectAllTableCols, - TFindFilter -} from "@app/lib/knex"; +import { buildFindFilter, mergeOneToManyRelation, ormify, selectAllTableCols, TFindFilter } from "@app/lib/knex"; export type TSecretApprovalPolicyDALFactory = ReturnType; @@ -18,12 +12,9 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => { const sapFindQuery = (tx: Knex, filter: TFindFilter) => tx(TableName.SecretApprovalPolicy) + // eslint-disable-next-line .where(buildFindFilter(filter)) - .join( - TableName.Environment, - `${TableName.SecretApprovalPolicy}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretApprovalPolicy}.envId`, `${TableName.Environment}.id`) .join( TableName.SecretApprovalPolicyApprover, `${TableName.SecretApprovalPolicy}.id`, @@ -59,10 +50,7 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => { } }; - const find = async ( - filter: TFindFilter, - tx?: Knex - ) => { + const find = async (filter: TFindFilter, tx?: Knex) => { try { const docs = await sapFindQuery(tx || db, filter); const formatedDoc = mergeOneToManyRelation( diff --git a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts index 078d688c5..b5688545c 100644 --- a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts +++ b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts @@ -2,10 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import picomatch from "picomatch"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { containsGlobPatterns } from "@app/lib/picomatch"; import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal"; @@ -34,9 +31,7 @@ type TSecretApprovalPolicyServiceFactoryDep = { projectMembershipDAL: Pick; }; -export type TSecretApprovalPolicyServiceFactory = ReturnType< - typeof secretApprovalPolicyServiceFactory ->; +export type TSecretApprovalPolicyServiceFactory = ReturnType; export const secretApprovalPolicyServiceFactory = ({ secretApprovalPolicyDAL, @@ -105,18 +100,10 @@ export const secretApprovalPolicyServiceFactory = ({ secretPolicyId }: TUpdateSapDTO) => { const secretApprovalPolicy = await secretApprovalPolicyDAL.findById(secretPolicyId); - if (!secretApprovalPolicy) - throw new BadRequestError({ message: "Secret approval policy not found" }); + if (!secretApprovalPolicy) throw new BadRequestError({ message: "Secret approval policy not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - secretApprovalPolicy.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.SecretApproval - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, secretApprovalPolicy.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval); const updatedSap = await secretApprovalPolicyDAL.transaction(async (tx) => { const doc = await secretApprovalPolicyDAL.updateById( @@ -162,11 +149,7 @@ export const secretApprovalPolicyServiceFactory = ({ const sapPolicy = await secretApprovalPolicyDAL.findById(secretPolicyId); if (!sapPolicy) throw new BadRequestError({ message: "Secret approval policy not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - sapPolicy.projectId - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, sapPolicy.projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.SecretApproval @@ -178,20 +161,13 @@ export const secretApprovalPolicyServiceFactory = ({ const getSecretApprovalPolicyByProjectId = async ({ actorId, actor, projectId }: TListSapDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretApproval - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval); const sapPolicies = await secretApprovalPolicyDAL.find({ projectId }); return sapPolicies; }; - const getSecretApprovalPolicy = async ( - projectId: string, - environment: string, - secretPath: string - ) => { + const getSecretApprovalPolicy = async (projectId: string, environment: string, secretPath: string) => { const env = await projectEnvDAL.findOne({ slug: environment, projectId }); if (!env) throw new BadRequestError({ message: "Environment not found" }); @@ -199,14 +175,11 @@ export const secretApprovalPolicyServiceFactory = ({ if (!policies.length) return; // this will filter policies either without scoped to secret path or the one that matches with secret path const policiesFilteredByPath = policies.filter( - ({ secretPath: policyPath }) => - !policyPath || picomatch.isMatch(secretPath, policyPath, { strictSlashes: false }) + ({ secretPath: policyPath }) => !policyPath || picomatch.isMatch(secretPath, policyPath, { strictSlashes: false }) ); // now sort by priority. exact secret path gets first match followed by glob followed by just env scoped // if that is tie get by first createdAt - const policiesByPriority = policiesFilteredByPath.sort( - (a, b) => getPolicyScore(b) - getPolicyScore(a) - ); + const policiesByPriority = policiesFilteredByPath.sort((a, b) => getPolicyScore(b) - getPolicyScore(a)); const finalPolicy = policiesByPriority.shift(); return finalPolicy; }; diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts index cda07eff9..05fe1b8f8 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-dal.ts @@ -1,15 +1,14 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { SecretApprovalRequestsSchema, TableName, TSecretApprovalRequests } from "@app/db/schemas"; -import { DatabaseError } from "@app/lib/errors"; import { - ormify, - selectAllTableCols, - sqlNestRelationships, - stripUndefinedInWhere, - TFindFilter -} from "@app/lib/knex"; + SecretApprovalRequestsSchema, + TableName, + TSecretApprovalRequests, + TSecretApprovalRequestsSecrets +} from "@app/db/schemas"; +import { DatabaseError } from "@app/lib/errors"; +import { ormify, selectAllTableCols, sqlNestRelationships, stripUndefinedInWhere, TFindFilter } from "@app/lib/knex"; import { RequestState } from "./secret-approval-request-types"; @@ -31,11 +30,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { const findQuery = (filter: TFindFilter, tx: Knex) => tx(TableName.SecretApprovalRequest) .where(filter) - .join( - TableName.SecretFolder, - `${TableName.SecretApprovalRequest}.folderId`, - `${TableName.SecretFolder}.id` - ) + .join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`) .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .join( TableName.SecretApprovalPolicy, @@ -87,8 +82,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { { key: "reviewerMemberId", label: "reviewers" as const, - mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => - member ? { member, status } : undefined + mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined) }, { key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId } ] @@ -109,26 +103,19 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { .with( "temp", (tx || db)(TableName.SecretApprovalRequest) - .join( - TableName.SecretFolder, - `${TableName.SecretApprovalRequest}.folderId`, - `${TableName.SecretFolder}.id` - ) - .join( - TableName.Environment, - `${TableName.SecretFolder}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`) + .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .join( TableName.SecretApprovalPolicyApprover, `${TableName.SecretApprovalRequest}.policyId`, `${TableName.SecretApprovalPolicyApprover}.policyId` ) .where({ projectId }) - .andWhere((bd) => - bd - .where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId) - .orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId) + .andWhere( + (bd) => + void bd + .where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId) + .orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId) ) .select("status", `${TableName.SecretApprovalRequest}.id`) .groupBy(`${TableName.SecretApprovalRequest}.id`, "status") @@ -141,11 +128,11 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { return { open: parseInt( - (docs.find(({ status }) => status === RequestState.Open)?.count as string) || "0", + (docs.find(({ status }) => status === RequestState.Open) as { count: string })?.count || "0", 10 ), closed: parseInt( - (docs.find(({ status }) => status === RequestState.Closed)?.count as string) || "0", + (docs.find(({ status }) => status === RequestState.Closed) as { count: string })?.count || "0", 10 ) }; @@ -155,31 +142,15 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { }; const findByProjectId = async ( - { - status, - limit = 20, - offset = 0, - projectId, - committer, - environment, - membershipId - }: TFindQueryFilter, + { status, limit = 20, offset = 0, projectId, committer, environment, membershipId }: TFindQueryFilter, tx?: Knex ) => { try { // akhilmhdh: If ever u wanted a 1 to so many relationship connected with pagination // this is the place u wanna look at. const query = (tx || db)(TableName.SecretApprovalRequest) - .join( - TableName.SecretFolder, - `${TableName.SecretApprovalRequest}.folderId`, - `${TableName.SecretFolder}.id` - ) - .join( - TableName.Environment, - `${TableName.SecretFolder}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`) + .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .join( TableName.SecretApprovalPolicy, `${TableName.SecretApprovalRequest}.policyId`, @@ -195,7 +166,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { `${TableName.SecretApprovalRequest}.id`, `${TableName.SecretApprovalRequestReviewer}.requestId` ) - .leftJoin( + .leftJoin( TableName.SecretApprovalRequestSecret, `${TableName.SecretApprovalRequestSecret}.requestId`, `${TableName.SecretApprovalRequest}.id` @@ -208,10 +179,11 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { committerId: committer }) ) - .andWhere((bd) => - bd - .where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId) - .orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId) + .andWhere( + (bd) => + void bd + .where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId) + .orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId) ) .select(selectAllTableCols(TableName.SecretApprovalRequest)) .select( @@ -257,8 +229,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => { { key: "reviewerMemberId", label: "reviewers" as const, - mapper: ({ reviewerMemberId: member, reviewerStatus: s }) => - member ? { member, status: s } : undefined + mapper: ({ reviewerMemberId: member, reviewerStatus: s }) => (member ? { member, status: s } : undefined) }, { key: "approverId", diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-reviewer-dal.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-reviewer-dal.ts index a2a93f258..13478f38d 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-reviewer-dal.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-reviewer-dal.ts @@ -2,9 +2,7 @@ import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; import { ormify } from "@app/lib/knex"; -export type TSecretApprovalRequestReviewerDALFactory = ReturnType< - typeof secretApprovalRequestReviewerDALFactory ->; +export type TSecretApprovalRequestReviewerDALFactory = ReturnType; export const secretApprovalRequestReviewerDALFactory = (db: TDbClient) => { const secretApprovalRequestReviewerOrm = ormify(db, TableName.SecretApprovalRequestReviewer); diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts index a0d25ae65..9b4742255 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-secret-dal.ts @@ -1,13 +1,11 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { SecretApprovalRequestsSecretsSchema, TableName } from "@app/db/schemas"; +import { SecretApprovalRequestsSecretsSchema, TableName, TSecretTags } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify, selectAllTableCols, sqlNestRelationships } from "@app/lib/knex"; -export type TSecretApprovalRequestSecretDALFactory = ReturnType< - typeof secretApprovalRequestSecretDALFactory ->; +export type TSecretApprovalRequestSecretDALFactory = ReturnType; export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { const secretApprovalRequestSecretOrm = ormify(db, TableName.SecretApprovalRequestSecret); @@ -25,16 +23,8 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { `${TableName.SecretApprovalRequestSecret}.id`, `${TableName.SecretApprovalRequestSecretTag}.secretId` ) - .leftJoin( - TableName.SecretTag, - `${TableName.SecretApprovalRequestSecretTag}.tagId`, - `${TableName.SecretTag}.id` - ) - .leftJoin( - TableName.Secret, - `${TableName.SecretApprovalRequestSecret}.secretId`, - `${TableName.Secret}.id` - ) + .leftJoin(TableName.SecretTag, `${TableName.SecretApprovalRequestSecretTag}.tagId`, `${TableName.SecretTag}.id`) + .leftJoin(TableName.Secret, `${TableName.SecretApprovalRequestSecret}.secretId`, `${TableName.Secret}.id`) .leftJoin( TableName.SecretVersion, `${TableName.SecretVersion}.id`, @@ -45,7 +35,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { `${TableName.SecretVersionTag}.${TableName.SecretVersion}Id`, `${TableName.SecretVersion}.id` ) - .leftJoin( + .leftJoin( db.ref(TableName.SecretTag).as("secVerTag"), `${TableName.SecretVersionTag}.${TableName.SecretTag}Id`, db.ref("id").withSchema("secVerTag") @@ -75,37 +65,24 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { db.ref("secretValueCiphertext").withSchema(TableName.Secret).as("orgSecValueCiphertext"), db.ref("secretCommentIV").withSchema(TableName.Secret).as("orgSecCommentIV"), db.ref("secretCommentTag").withSchema(TableName.Secret).as("orgSecCommentTag"), - db - .ref("secretCommentCiphertext") - .withSchema(TableName.Secret) - .as("orgSecCommentCiphertext") + db.ref("secretCommentCiphertext").withSchema(TableName.Secret).as("orgSecCommentCiphertext") ) .select( db.ref("version").withSchema(TableName.SecretVersion).as("secVerVersion"), db.ref("secretKeyIV").withSchema(TableName.SecretVersion).as("secVerKeyIV"), db.ref("secretKeyTag").withSchema(TableName.SecretVersion).as("secVerKeyTag"), - db - .ref("secretKeyCiphertext") - .withSchema(TableName.SecretVersion) - .as("secVerKeyCiphertext"), + db.ref("secretKeyCiphertext").withSchema(TableName.SecretVersion).as("secVerKeyCiphertext"), db.ref("secretValueIV").withSchema(TableName.SecretVersion).as("secVerValueIV"), db.ref("secretValueTag").withSchema(TableName.SecretVersion).as("secVerValueTag"), - db - .ref("secretValueCiphertext") - .withSchema(TableName.SecretVersion) - .as("secVerValueCiphertext"), + db.ref("secretValueCiphertext").withSchema(TableName.SecretVersion).as("secVerValueCiphertext"), db.ref("secretCommentIV").withSchema(TableName.SecretVersion).as("secVerCommentIV"), db.ref("secretCommentTag").withSchema(TableName.SecretVersion).as("secVerCommentTag"), - db - .ref("secretCommentCiphertext") - .withSchema(TableName.SecretVersion) - .as("secVerCommentCiphertext") + db.ref("secretCommentCiphertext").withSchema(TableName.SecretVersion).as("secVerCommentCiphertext") ); const formatedDoc = sqlNestRelationships({ data: doc, key: "id", - parentMapper: (data) => - SecretApprovalRequestsSecretsSchema.omit({ secretVersion: true }).parse(data), + parentMapper: (data) => SecretApprovalRequestsSecretsSchema.omit({ secretVersion: true }).parse(data), childrenMapper: [ { key: "tagJnId", @@ -186,15 +163,14 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => { { key: "secVerTagId", label: "tags" as const, - mapper: ({ - secVerTagId: id, - secVerTagName: name, - secVerTagSlug: slug, - secVerTagColor: color - }) => ({ + mapper: ({ secVerTagId: id, secVerTagName: name, secVerTagSlug: slug, secVerTagColor: color }) => ({ + // eslint-disable-next-line id, + // eslint-disable-next-line name, + // eslint-disable-next-line slug, + // eslint-disable-next-line color }) } diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts index 81be86304..d1ecd51ed 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts @@ -42,10 +42,7 @@ type TSecretApprovalRequestServiceFactoryDep = { secretApprovalRequestDAL: TSecretApprovalRequestDALFactory; secretApprovalRequestSecretDAL: TSecretApprovalRequestSecretDALFactory; secretApprovalRequestReviewerDAL: TSecretApprovalRequestReviewerDALFactory; - folderDAL: Pick< - TSecretFolderDALFactory, - "findBySecretPath" | "findById" | "findSecretPathByFolderIds" - >; + folderDAL: Pick; secretTagDAL: Pick; secretBlindIndexDAL: Pick; snapshotService: Pick; @@ -61,9 +58,7 @@ type TSecretApprovalRequestServiceFactoryDep = { secretQueueService: Pick; }; -export type TSecretApprovalRequestServiceFactory = ReturnType< - typeof secretApprovalRequestServiceFactory ->; +export type TSecretApprovalRequestServiceFactory = ReturnType; export const secretApprovalRequestServiceFactory = ({ secretApprovalRequestDAL, @@ -79,14 +74,9 @@ export const secretApprovalRequestServiceFactory = ({ secretQueueService }: TSecretApprovalRequestServiceFactoryDep) => { const requestCount = async ({ projectId, actor, actorId }: TApprovalRequestCountDTO) => { - if (actor === ActorType.SERVICE) - throw new BadRequestError({ message: "Cannot use service token" }); + if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); - const { membership } = await permissionService.getProjectPermission( - actor as ActorType.USER, - actorId, - projectId - ); + const { membership } = await permissionService.getProjectPermission(actor as ActorType.USER, actorId, projectId); const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, membership.id); return count; @@ -102,8 +92,7 @@ export const secretApprovalRequestServiceFactory = ({ limit, offset }: TListApprovalsDTO) => { - if (actor === ActorType.SERVICE) - throw new BadRequestError({ message: "Cannot use service token" }); + if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); const { membership } = await permissionService.getProjectPermission(actor, actorId, projectId); const approvals = await secretApprovalRequestDAL.findByProjectId({ @@ -119,12 +108,10 @@ export const secretApprovalRequestServiceFactory = ({ }; const getSecretApprovalDetails = async ({ actor, actorId, id }: TSecretApprovalDetailsDTO) => { - if (actor === ActorType.SERVICE) - throw new BadRequestError({ message: "Cannot use service token" }); + if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); const secretApprovalRequest = await secretApprovalRequestDAL.findById(id); - if (!secretApprovalRequest) - throw new BadRequestError({ message: "Secret approval request not found" }); + if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" }); const { policy } = secretApprovalRequest; const { membership } = await permissionService.getProjectPermission( @@ -149,8 +136,7 @@ export const secretApprovalRequestServiceFactory = ({ const reviewApproval = async ({ approvalId, actor, status, actorId }: TReviewRequestDTO) => { const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId); - if (!secretApprovalRequest) - throw new BadRequestError({ message: "Secret approval request not found" }); + if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" }); if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" }); const { policy } = secretApprovalRequest; @@ -191,8 +177,7 @@ export const secretApprovalRequestServiceFactory = ({ const updateApprovalStatus = async ({ actorId, status, approvalId, actor }: TStatusChangeDTO) => { const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId); - if (!secretApprovalRequest) - throw new BadRequestError({ message: "Secret approval request not found" }); + if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" }); if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" }); const { policy } = secretApprovalRequest; @@ -209,8 +194,7 @@ export const secretApprovalRequestServiceFactory = ({ throw new UnauthorizedError({ message: "User has no access" }); } - if (secretApprovalRequest.hasMerged) - throw new BadRequestError({ message: "Approval request has been merged" }); + if (secretApprovalRequest.hasMerged) throw new BadRequestError({ message: "Approval request has been merged" }); if (secretApprovalRequest.status === RequestState.Closed && status === RequestState.Closed) throw new BadRequestError({ message: "Approval request is already closed" }); if (secretApprovalRequest.status === RequestState.Open && status === RequestState.Open) @@ -223,22 +207,13 @@ export const secretApprovalRequestServiceFactory = ({ return { ...secretApprovalRequest, ...updatedRequest }; }; - const mergeSecretApprovalRequest = async ({ - approvalId, - actor, - actorId - }: TMergeSecretApprovalRequestDTO) => { + const mergeSecretApprovalRequest = async ({ approvalId, actor, actorId }: TMergeSecretApprovalRequestDTO) => { const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId); - if (!secretApprovalRequest) - throw new BadRequestError({ message: "Secret approval request not found" }); + if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" }); if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" }); const { policy, folderId, projectId } = secretApprovalRequest; - const { membership } = await permissionService.getProjectPermission( - ActorType.USER, - actorId, - projectId - ); + const { membership } = await permissionService.getProjectPermission(ActorType.USER, actorId, projectId); if ( membership.role !== ProjectMembershipRole.Admin && secretApprovalRequest.committerId !== membership.id && @@ -256,28 +231,24 @@ export const secretApprovalRequestServiceFactory = ({ (approverId) => reviewers[approverId.toString()] === ApprovalStatus.APPROVED ).length; - if (!hasMinApproval) - throw new BadRequestError({ message: "Doesn't have minimum approvals needed" }); - const secretApprovalSecrets = await secretApprovalRequestSecretDAL.findByRequestId( - secretApprovalRequest.id - ); + if (!hasMinApproval) throw new BadRequestError({ message: "Doesn't have minimum approvals needed" }); + const secretApprovalSecrets = await secretApprovalRequestSecretDAL.findByRequestId(secretApprovalRequest.id); if (!secretApprovalSecrets) throw new BadRequestError({ message: "No secrets found" }); const conflicts: Array<{ secretId: string; op: CommitType }> = []; let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Create); if (secretCreationCommits.length) { - const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = - await secretService.fnSecretBlindIndexCheckV2({ - folderId, - inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => { - if (!secretBlindIndex) { - throw new BadRequestError({ - message: "Missing secret blind index" - }); - } - return { secretBlindIndex }; - }) - }); + const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({ + folderId, + inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => { + if (!secretBlindIndex) { + throw new BadRequestError({ + message: "Missing secret blind index" + }); + } + return { secretBlindIndex }; + }) + }); secretCreationCommits .filter(({ secretBlindIndex }) => conflictGroupByBlindIndex[secretBlindIndex || ""]) .forEach((el) => { @@ -290,23 +261,19 @@ export const secretApprovalRequestServiceFactory = ({ let secretUpdationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Update); if (secretUpdationCommits.length) { - const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = - await secretService.fnSecretBlindIndexCheckV2({ - folderId, - inputSecrets: secretUpdationCommits - .filter( - ({ secretBlindIndex, secret }) => - secret && secret.secretBlindIndex !== secretBlindIndex - ) - .map(({ secretBlindIndex }) => { - if (!secretBlindIndex) { - throw new BadRequestError({ - message: "Missing secret blind index" - }); - } - return { secretBlindIndex }; - }) - }); + const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({ + folderId, + inputSecrets: secretUpdationCommits + .filter(({ secretBlindIndex, secret }) => secret && secret.secretBlindIndex !== secretBlindIndex) + .map(({ secretBlindIndex }) => { + if (!secretBlindIndex) { + throw new BadRequestError({ + message: "Missing secret blind index" + }); + } + return { secretBlindIndex }; + }) + }); secretUpdationCommits .filter( ({ secretBlindIndex, secretId }) => @@ -318,14 +285,11 @@ export const secretApprovalRequestServiceFactory = ({ secretUpdationCommits = secretUpdationCommits.filter( ({ secretBlindIndex, secretId }) => - Boolean(secretId) && - (secretBlindIndex ? !conflictGroupByBlindIndex[secretBlindIndex] : true) + Boolean(secretId) && (secretBlindIndex ? !conflictGroupByBlindIndex[secretBlindIndex] : true) ); } - const secretDeletionCommits = secretApprovalSecrets.filter( - ({ op }) => op === CommitType.Delete - ); + const secretDeletionCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Delete); const mergeStatus = await secretApprovalRequestDAL.transaction(async (tx) => { const newSecrets = secretCreationCommits.length @@ -442,27 +406,20 @@ export const secretApprovalRequestServiceFactory = ({ secretPath, environment }: TGenerateSecretApprovalRequestDTO) => { - if (actor === ActorType.SERVICE) - throw new BadRequestError({ message: "Cannot use service token" }); + if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); - const { permission, membership } = await permissionService.getProjectPermission( - actor, - actorId, - projectId - ); + const { permission, membership } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) ); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); - if (!folder) - throw new BadRequestError({ message: "Folder not found", name: "GenSecretApproval" }); + if (!folder) throw new BadRequestError({ message: "Folder not found", name: "GenSecretApproval" }); const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); const commits: Omit[] = []; const commitTagIds: Record = {}; @@ -496,38 +453,28 @@ export const secretApprovalRequestServiceFactory = ({ // get all blind index // Find all those secrets // if not throw not found - const { keyName2BlindIndex, secrets: secretsToBeUpdated } = - await secretService.fnSecretBlindIndexCheck({ - inputSecrets: updatedSecrets, - folderId, - isNew: false, - blindIndexCfg - }); + const { keyName2BlindIndex, secrets: secretsToBeUpdated } = await secretService.fnSecretBlindIndexCheck({ + inputSecrets: updatedSecrets, + folderId, + isNew: false, + blindIndexCfg + }); // now find any secret that needs to update its name // same process as above - const nameUpdatedSecrets = updatedSecrets.filter(({ newSecretName }) => - Boolean(newSecretName) - ); - const { keyName2BlindIndex: newKeyName2BlindIndex } = - await secretService.fnSecretBlindIndexCheck({ - inputSecrets: nameUpdatedSecrets, - folderId, - isNew: true, - blindIndexCfg - }); + const nameUpdatedSecrets = updatedSecrets.filter(({ newSecretName }) => Boolean(newSecretName)); + const { keyName2BlindIndex: newKeyName2BlindIndex } = await secretService.fnSecretBlindIndexCheck({ + inputSecrets: nameUpdatedSecrets, + folderId, + isNew: true, + blindIndexCfg + }); - const secsGroupedByBlindIndex = groupBy( - secretsToBeUpdated, - (el) => el.secretBlindIndex as string - ); + const secsGroupedByBlindIndex = groupBy(secretsToBeUpdated, (el) => el.secretBlindIndex as string); const updatedSecretIds = updatedSecrets.map( (el) => secsGroupedByBlindIndex[keyName2BlindIndex[el.secretName]][0].id ); - const latestSecretVersions = await secretVersionDAL.findLatestVersionMany( - folderId, - updatedSecretIds - ); + const latestSecretVersions = await secretVersionDAL.findLatestVersionMany(folderId, updatedSecretIds); commits.push( ...updatedSecrets.map(({ newSecretName, secretName, tagIds, ...el }) => { const secretId = secsGroupedByBlindIndex[keyName2BlindIndex[secretName]][0].id; @@ -562,17 +509,13 @@ export const secretApprovalRequestServiceFactory = ({ blindIndexCfg }); const secretsGroupedByBlindIndex = groupBy(secrets, (i) => { - if (!i.secretBlindIndex) - throw new BadRequestError({ message: "Missing secret blind index" }); + if (!i.secretBlindIndex) throw new BadRequestError({ message: "Missing secret blind index" }); return i.secretBlindIndex; }); const deletedSecretIds = deletedSecrets.map( (el) => secretsGroupedByBlindIndex[keyName2BlindIndex[el.secretName]][0].id ); - const latestSecretVersions = await secretVersionDAL.findLatestVersionMany( - folderId, - deletedSecretIds - ); + const latestSecretVersions = await secretVersionDAL.findLatestVersionMany(folderId, deletedSecretIds); commits.push( ...deletedSecrets.map((el) => { const secretId = secretsGroupedByBlindIndex[keyName2BlindIndex[el.secretName]][0].id; diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts index c010277d5..008b977e6 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-types.ts @@ -1,8 +1,4 @@ -import { - TImmutableDBKeys, - TSecretApprovalPolicies, - TSecretApprovalRequestsSecrets -} from "@app/db/schemas"; +import { TImmutableDBKeys, TSecretApprovalPolicies, TSecretApprovalRequestsSecrets } from "@app/db/schemas"; import { TProjectPermission } from "@app/lib/types"; export enum CommitType { @@ -24,14 +20,7 @@ export enum ApprovalStatus { type TApprovalCreateSecret = Omit< TSecretApprovalRequestsSecrets, - | TImmutableDBKeys - | "version" - | "algorithm" - | "keyEncoding" - | "requestId" - | "op" - | "secretVersion" - | "secretBlindIndex" + TImmutableDBKeys | "version" | "algorithm" | "keyEncoding" | "requestId" | "op" | "secretVersion" | "secretBlindIndex" > & { secretName: string; tagIds?: string[]; diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-dal.ts b/backend/src/ee/services/secret-rotation/secret-rotation-dal.ts index d1504e3d7..7feafdc6b 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-dal.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-dal.ts @@ -14,21 +14,13 @@ export const secretRotationDALFactory = (db: TDbClient) => { const findQuery = (filter: TFindFilter, tx: Knex) => tx(TableName.SecretRotation) .where(filter) - .join( - TableName.Environment, - `${TableName.SecretRotation}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretRotation}.envId`, `${TableName.Environment}.id`) .leftJoin( TableName.SecretRotationOutput, `${TableName.SecretRotation}.id`, `${TableName.SecretRotationOutput}.rotationId` ) - .join( - TableName.Secret, - `${TableName.SecretRotationOutput}.secretId`, - `${TableName.Secret}.id` - ) + .join(TableName.Secret, `${TableName.SecretRotationOutput}.secretId`, `${TableName.Secret}.id`) .select(selectAllTableCols(TableName.SecretRotation)) .select(tx.ref("name").withSchema(TableName.Environment).as("envName")) .select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug")) @@ -102,11 +94,7 @@ export const secretRotationDALFactory = (db: TDbClient) => { const findById = async (id: string, tx?: Knex) => { try { const doc = await (tx || db)(TableName.SecretRotation) - .join( - TableName.Environment, - `${TableName.SecretRotation}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretRotation}.envId`, `${TableName.Environment}.id`) .where({ [`${TableName.SecretRotation}.id` as "id"]: id }) .select(selectAllTableCols(TableName.SecretRotation)) .select( @@ -125,8 +113,7 @@ export const secretRotationDALFactory = (db: TDbClient) => { } }; - const findRotationOutputsByRotationId = async (rotationId: string) => - secretRotationOutputOrm.find({ rotationId }); + const findRotationOutputsByRotationId = async (rotationId: string) => secretRotationOutputOrm.find({ rotationId }); return { ...secretRotationOrm, diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue-fn.ts b/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue-fn.ts index bcd96e090..c67477bfd 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue-fn.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue-fn.ts @@ -1,3 +1,8 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable no-param-reassign */ import axios from "axios"; import jmespath from "jmespath"; @@ -6,12 +11,7 @@ import knex from "knex"; import { getConfig } from "@app/lib/config/env"; import { alphaNumericNanoId } from "@app/lib/nanoid"; -import { - TAssignOp, - TDbProviderClients, - TDirectAssignOp, - THttpProviderFunction -} from "../templates/types"; +import { TAssignOp, TDbProviderClients, TDirectAssignOp, THttpProviderFunction } from "../templates/types"; import { TSecretRotationData, TSecretRotationDbFn } from "./secret-rotation-queue-types"; const REGEX = /\${([^}]+)}/g; @@ -37,7 +37,7 @@ export const interpolate = (data: any, getValue: (key: string) => unknown) => { if ((data as { ref: string })?.ref) return getValue((data as { ref: string }).ref); const temp = data as Record; // for converting ts object to record type Object.keys(temp).forEach((key) => { - temp[key as keyof typeof temp] = interpolate(data[key as keyof typeof temp], getValue); + temp[key] = interpolate(data[key], getValue); }); } return data; @@ -59,10 +59,7 @@ const getInterpolationValue = (variables: TSecretRotationData) => (key: string) return variables[type as keyof TSecretRotationData][keyName]; }; -export const secretRotationHttpFn = async ( - func: THttpProviderFunction, - variables: TSecretRotationData -) => { +export const secretRotationHttpFn = async (func: THttpProviderFunction, variables: TSecretRotationData) => { // string interpolation const headers = interpolate(func.header, getInterpolationValue(variables)); const url = interpolate(func.url, getInterpolationValue(variables)); @@ -112,10 +109,7 @@ export const secretRotationDbFn = async ({ return data; }; -export const secretRotationPreSetFn = ( - op: Record, - variables: TSecretRotationData -) => { +export const secretRotationPreSetFn = (op: Record, variables: TSecretRotationData) => { const getValFn = getInterpolationValue(variables); Object.entries(op || {}).forEach(([key, assignFn]) => { const [type, keyName] = key.split(".") as [keyof TSecretRotationData, string]; @@ -123,10 +117,7 @@ export const secretRotationPreSetFn = ( }); }; -export const secretRotationHttpSetFn = async ( - func: THttpProviderFunction, - variables: TSecretRotationData -) => { +export const secretRotationHttpSetFn = async (func: THttpProviderFunction, variables: TSecretRotationData) => { const getValFn = getInterpolationValue(variables); // http setter const res = await secretRotationHttpFn(func, variables); @@ -140,10 +131,7 @@ export const secretRotationHttpSetFn = async ( }); }; -export const getDbSetQuery = ( - db: TDbProviderClients, - variables: { username: string; password: string } -) => { +export const getDbSetQuery = (db: TDbProviderClients, variables: { username: string; password: string }) => { if (db === TDbProviderClients.Pg) { return { query: `ALTER USER ?? WITH PASSWORD '${variables.password}'`, diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue.ts b/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue.ts index 5c4dc5e30..9e69f0a8f 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-queue/secret-rotation-queue.ts @@ -6,6 +6,7 @@ import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption"; import { daysToMillisecond, secondsToMillis } from "@app/lib/dates"; +import { BadRequestError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue"; @@ -17,11 +18,7 @@ import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; import { TSecretRotationDALFactory } from "../secret-rotation-dal"; import { rotationTemplates } from "../templates"; -import { - TDbProviderClients, - TProviderFunctionTypes, - TSecretRotationProviderTemplate -} from "../templates/types"; +import { TDbProviderClients, TProviderFunctionTypes, TSecretRotationProviderTemplate } from "../templates/types"; import { getDbSetQuery, secretRotationDbFn, @@ -29,12 +26,7 @@ import { secretRotationHttpSetFn, secretRotationPreSetFn } from "./secret-rotation-queue-fn"; -import { - TSecretRotationData, - TSecretRotationDbFn, - TSecretRotationEncData -} from "./secret-rotation-queue-types"; -import { BadRequestError } from "@app/lib/errors"; +import { TSecretRotationData, TSecretRotationDbFn, TSecretRotationEncData } from "./secret-rotation-queue-types"; export type TSecretRotationQueueFactory = ReturnType; @@ -70,7 +62,7 @@ export const secretRotationQueueFactory = ({ }: TSecretRotationQueueFactoryDep) => { const addToQueue = async (rotationId: string, interval: number) => { const appCfg = getConfig(); - queue.queue( + await queue.queue( QueueName.SecretRotation, QueueJobs.SecretRotation, { rotationId }, @@ -78,10 +70,7 @@ export const secretRotationQueueFactory = ({ jobId: rotationId, repeat: { // on prod it this will be in days, in development this will be second - every: - appCfg.NODE_ENV === "development" - ? secondsToMillis(interval) - : daysToMillisecond(interval), + every: appCfg.NODE_ENV === "development" ? secondsToMillis(interval) : daysToMillisecond(interval), immediately: true } } @@ -95,10 +84,7 @@ export const secretRotationQueueFactory = ({ QueueJobs.SecretRotation, { // on prod it this will be in days, in development this will be second - every: - appCfg.NODE_ENV === "development" - ? secondsToMillis(interval) - : daysToMillisecond(interval) + every: appCfg.NODE_ENV === "development" ? secondsToMillis(interval) : daysToMillisecond(interval) }, rotationId ); @@ -108,22 +94,16 @@ export const secretRotationQueueFactory = ({ const { rotationId } = job.data; logger.info(`secretRotationQueue.process: [rotationDocument=${rotationId}]`); const secretRotation = await secretRotationDAL.findById(rotationId); - const rotationProvider = rotationTemplates.find( - ({ name }) => name === secretRotation?.provider - ); + const rotationProvider = rotationTemplates.find(({ name }) => name === secretRotation?.provider); try { - if (!rotationProvider || !secretRotation) - throw new DisableRotationErrors({ message: "Provider not found" }); + if (!rotationProvider || !secretRotation) throw new DisableRotationErrors({ message: "Provider not found" }); const rotationOutputs = await secretRotationDAL.findRotationOutputsByRotationId(rotationId); - if (!rotationOutputs.length) - throw new DisableRotationErrors({ message: "Secrets not found" }); + if (!rotationOutputs.length) throw new DisableRotationErrors({ message: "Secrets not found" }); // deep copy - const provider = JSON.parse( - JSON.stringify(rotationProvider) - ) as TSecretRotationProviderTemplate; + const provider = JSON.parse(JSON.stringify(rotationProvider)) as TSecretRotationProviderTemplate; // now get the encrypted variable values // in includes the inputs, the previous outputs @@ -156,20 +136,11 @@ export const secretRotationQueueFactory = ({ ? variables.inputs.username2 : variables.inputs.username1; } else { - newCredential.internal.username = lastCred - ? lastCred.internal.username - : variables.inputs.username1; + newCredential.internal.username = lastCred ? lastCred.internal.username : variables.inputs.username1; } // set a random value for new password newCredential.internal.rotated_password = alphaNumericNanoId(32); - const { - admin_username: username, - admin_password: password, - host, - database, - port, - ca - } = newCredential.inputs; + const { admin_username: username, admin_password: password, host, database, port, ca } = newCredential.inputs; const dbFunctionArg = { username, password, @@ -177,10 +148,7 @@ export const secretRotationQueueFactory = ({ database, port, ca: ca as string, - client: - provider.template.client === TDbProviderClients.MySql - ? "mysql2" - : provider.template.client + client: provider.template.client === TDbProviderClients.MySql ? "mysql2" : provider.template.client } as TSecretRotationDbFn; // set function await secretRotationDbFn({ @@ -265,7 +233,7 @@ export const secretRotationQueueFactory = ({ return { ...el, secretId: id, - secretBlindIndex: el.secretBlindIndex as string + secretBlindIndex: el.secretBlindIndex }; }), tx @@ -288,7 +256,7 @@ export const secretRotationQueueFactory = ({ logger.error(error); if (error instanceof DisableRotationErrors) { if (job.id) { - queue.stopRepeatableJobByJobId(QueueName.SecretRotation, job.id); + await queue.stopRepeatableJobByJobId(QueueName.SecretRotation, job.id); } } diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-service.ts b/backend/src/ee/services/secret-rotation/secret-rotation-service.ts index 88f555d94..e10d7fa63 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-service.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-service.ts @@ -47,10 +47,7 @@ export const secretRotationServiceFactory = ({ }: TSecretRotationServiceFactoryDep) => { const getProviderTemplates = async ({ actor, actorId, projectId }: TProjectPermission) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRotation - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation); return { custom: [], @@ -93,8 +90,7 @@ export const secretRotationServiceFactory = ({ const plan = await licenseService.getPlan(project.orgId); if (!plan.secretRotation) throw new BadRequestError({ - message: - "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation." + message: "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation." }); const selectedTemplate = rotationTemplates.find(({ name }) => name === provider); @@ -152,24 +148,14 @@ export const secretRotationServiceFactory = ({ const [doc] = await secretRotationDAL.find({ id: rotationId }); if (!doc) throw new BadRequestError({ message: "Rotation not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - doc.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRotation - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation); return doc; }; const getByProjectId = async ({ actorId, projectId, actor }: TListByProjectIdDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRotation - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation); const doc = await secretRotationDAL.find({ projectId }); return doc; }; @@ -182,19 +168,11 @@ export const secretRotationServiceFactory = ({ const plan = await licenseService.getPlan(project.orgId); if (!plan.secretRotation) throw new BadRequestError({ - message: - "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation." + message: "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation." }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - doc.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.SecretRotation - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation); await secretRotationQueue.removeFromQueue(doc.id, doc.interval); await secretRotationQueue.addToQueue(doc.id, doc.interval); return doc; @@ -204,11 +182,7 @@ export const secretRotationServiceFactory = ({ const doc = await secretRotationDAL.findById(rotationId); if (!doc) throw new BadRequestError({ message: "Rotation not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - doc.projectId - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.SecretRotation diff --git a/backend/src/ee/services/secret-rotation/templates/mysql.ts b/backend/src/ee/services/secret-rotation/templates/mysql.ts index 86a382d6e..723560a3e 100644 --- a/backend/src/ee/services/secret-rotation/templates/mysql.ts +++ b/backend/src/ee/services/secret-rotation/templates/mysql.ts @@ -23,15 +23,7 @@ export const MYSQL_TEMPLATE = { }, ca: { type: "string", desc: "SSL certificate for db auth(string)" } }, - required: [ - "admin_username", - "admin_password", - "host", - "database", - "username1", - "username2", - "port" - ], + required: ["admin_username", "admin_password", "host", "database", "username1", "username2", "port"], additionalProperties: false }, outputs: { diff --git a/backend/src/ee/services/secret-rotation/templates/postgres.ts b/backend/src/ee/services/secret-rotation/templates/postgres.ts index 318ca650d..c894631cb 100644 --- a/backend/src/ee/services/secret-rotation/templates/postgres.ts +++ b/backend/src/ee/services/secret-rotation/templates/postgres.ts @@ -23,15 +23,7 @@ export const POSTGRES_TEMPLATE = { }, ca: { type: "string", desc: "SSL certificate for db auth(string)" } }, - required: [ - "admin_username", - "admin_password", - "host", - "database", - "username1", - "username2", - "port" - ], + required: ["admin_username", "admin_password", "host", "database", "username1", "username2", "port"], additionalProperties: false }, outputs: { diff --git a/backend/src/ee/services/secret-scanning/git-app-dal.ts b/backend/src/ee/services/secret-scanning/git-app-dal.ts index 8a3ec706e..8044b0d6b 100644 --- a/backend/src/ee/services/secret-scanning/git-app-dal.ts +++ b/backend/src/ee/services/secret-scanning/git-app-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TGitAppOrgInsert } from "@app/db/schemas"; +import { TableName, TGitAppOrgInsert } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; @@ -12,11 +12,7 @@ export const gitAppDALFactory = (db: TDbClient) => { const upsert = async (data: TGitAppOrgInsert, tx?: Knex) => { try { - const [doc] = await (tx || db)(TableName.GitAppOrg) - .insert(data) - .onConflict("orgId") - .merge() - .returning("*"); + const [doc] = await (tx || db)(TableName.GitAppOrg).insert(data).onConflict("orgId").merge().returning("*"); return doc; } catch (error) { throw new DatabaseError({ error, name: "UpsertGitAppOrm" }); diff --git a/backend/src/ee/services/secret-scanning/git-app-install-session-dal.ts b/backend/src/ee/services/secret-scanning/git-app-install-session-dal.ts index 9956d88ec..11f8eb53c 100644 --- a/backend/src/ee/services/secret-scanning/git-app-install-session-dal.ts +++ b/backend/src/ee/services/secret-scanning/git-app-install-session-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TGitAppInstallSessionsInsert } from "@app/db/schemas"; +import { TableName, TGitAppInstallSessionsInsert } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; diff --git a/backend/src/ee/services/secret-scanning/secret-scanning-dal.ts b/backend/src/ee/services/secret-scanning/secret-scanning-dal.ts index 4b5dcb378..828322ad2 100644 --- a/backend/src/ee/services/secret-scanning/secret-scanning-dal.ts +++ b/backend/src/ee/services/secret-scanning/secret-scanning-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TSecretScanningGitRisksInsert } from "@app/db/schemas"; +import { TableName, TSecretScanningGitRisksInsert } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; @@ -12,10 +12,7 @@ export const secretScanningDALFactory = (db: TDbClient) => { const upsert = async (data: TSecretScanningGitRisksInsert[], tx?: Knex) => { try { - const docs = await (tx || db)(TableName.SecretScanningGitRisk) - .insert(data) - .onConflict("fingerprint") - .merge(); + const docs = await (tx || db)(TableName.SecretScanningGitRisk).insert(data).onConflict("fingerprint").merge(); return docs; } catch (error) { throw new DatabaseError({ error, name: "GitRiskUpsert" }); diff --git a/backend/src/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-fns.ts b/backend/src/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-fns.ts index b444cf56b..2e74a1caf 100644 --- a/backend/src/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-fns.ts +++ b/backend/src/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-fns.ts @@ -1,3 +1,4 @@ +import { Octokit } from "@octokit/rest"; import { exec } from "child_process"; import { mkdir, readFile, rm, writeFile } from "fs"; import { tmpdir } from "os"; @@ -11,7 +12,7 @@ export function createTempFolder(): Promise { const tempFolderName = Math.random().toString(36).substring(2); const tempFolderPath = join(tempDir, tempFolderName); - mkdir(tempFolderPath, (err: any) => { + mkdir(tempFolderPath, (err) => { if (err) { reject(err); } else { @@ -115,7 +116,7 @@ export function convertKeysToLowercase(obj: T): T { } export async function scanFullRepoContentAndGetFindings( - octokit: any, + octokit: Octokit, installationId: string, repositoryFullName: string ): Promise { @@ -125,11 +126,13 @@ export async function scanFullRepoContentAndGetFindings( try { const { data: { token } - } = await octokit.apps.createInstallationAccessToken({ installation_id: installationId }); + } = await octokit.apps.createInstallationAccessToken({ + installation_id: Number(installationId) + }); await cloneRepo(token, repositoryFullName, repoPath); await runInfisicalScanOnRepo(repoPath, findingsPath); const findingsData = await readFindingsFile(findingsPath); - return JSON.parse(findingsData); + return JSON.parse(findingsData) as SecretMatch[]; } finally { await deleteTempFolder(tempFolder); } @@ -144,7 +147,7 @@ export async function scanContentAndGetFindings(textContent: string): Promise { if (!Object.keys(allFindingsByFingerprint).length) return; - secretScanningDAL.upsert( + await secretScanningDAL.upsert( Object.keys(allFindingsByFingerprint).map((key) => ({ installationId, email: allFindingsByFingerprint[key].Email, @@ -186,7 +179,9 @@ export const secretScanningQueueFactory = ({ }); const findings = await scanFullRepoContentAndGetFindings( - octokit, + // this is because of collision of octokit in probot and github + // eslint-disable-next-line + octokit as any, installationId, repository.fullName ); diff --git a/backend/src/ee/services/secret-scanning/secret-scanning-service.ts b/backend/src/ee/services/secret-scanning/secret-scanning-service.ts index 63aae869f..e150f30f3 100644 --- a/backend/src/ee/services/secret-scanning/secret-scanning-service.ts +++ b/backend/src/ee/services/secret-scanning/secret-scanning-service.ts @@ -4,10 +4,7 @@ import { ForbiddenError } from "@casl/ability"; import { WebhookEventMap } from "@octokit/webhooks-types"; import { ProbotOctokit } from "probot"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "@app/ee/services/permission/org-permission"; +import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { getConfig } from "@app/lib/config/env"; import { UnauthorizedError } from "@app/lib/errors"; @@ -44,30 +41,19 @@ export const secretScanningServiceFactory = ({ }: TSecretScanningServiceFactoryDep) => { const createInstallationSession = async ({ actor, orgId, actorId }: TInstallAppSessionDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.SecretScanning - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning); const sessionId = crypto.randomBytes(16).toString("hex"); await gitAppInstallSessionDAL.upsert({ orgId, sessionId, userId: actorId }); return { sessionId }; }; - const linkInstallationToOrg = async ({ - sessionId, - actorId, - installationId, - actor - }: TLinkInstallSessionDTO) => { + const linkInstallationToOrg = async ({ sessionId, actorId, installationId, actor }: TLinkInstallSessionDTO) => { const session = await gitAppInstallSessionDAL.findOne({ sessionId }); if (!session) throw new UnauthorizedError({ message: "Session not found" }); const { permission } = await permissionService.getOrgPermission(actor, actorId, session.orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.SecretScanning - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning); const installatedApp = await gitAppOrgDAL.transaction(async (tx) => { await gitAppInstallSessionDAL.deleteById(session.id, tx); return gitAppOrgDAL.upsert({ orgId: session.orgId, installationId, userId: actorId }, tx); @@ -99,10 +85,7 @@ export const secretScanningServiceFactory = ({ const getOrgInstallationStatus = async ({ actorId, orgId, actor }: TGetOrgInstallStatusDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.SecretScanning - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning); const appInstallation = await gitAppOrgDAL.findOne({ orgId }); return Boolean(appInstallation); @@ -110,26 +93,14 @@ export const secretScanningServiceFactory = ({ const getRisksByOrg = async ({ actor, orgId, actorId }: TGetOrgRisksDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.SecretScanning - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning); const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] }); return { risks }; }; - const updateRiskStatus = async ({ - actorId, - orgId, - actor, - riskId, - status - }: TUpdateRiskStatusDTO) => { + const updateRiskStatus = async ({ actorId, orgId, actor, riskId, status }: TUpdateRiskStatusDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.SecretScanning - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning); const isRiskResolved = Boolean( [ @@ -169,11 +140,7 @@ export const secretScanningServiceFactory = ({ const handleRepoDeleteEvent = async (installationId: string, repositoryIds: string[]) => { await secretScanningDAL.transaction(async (tx) => { if (repositoryIds.length) { - await Promise.all( - repositoryIds.map((repoId) => - secretScanningDAL.delete({ repositoryId: repoId }, tx) - ) - ); + await Promise.all(repositoryIds.map((repoId) => secretScanningDAL.delete({ repositoryId: repoId }, tx))); } await gitAppOrgDAL.delete({ installationId }, tx); }); diff --git a/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts b/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts index d1e5bad6b..26148958f 100644 --- a/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts +++ b/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts @@ -29,17 +29,11 @@ type TSecretSnapshotServiceFactoryDep = { snapshotSecretDAL: TSnapshotSecretDALFactory; snapshotFolderDAL: TSnapshotFolderDALFactory; secretVersionDAL: Pick; - folderVersionDAL: Pick< - TSecretFolderVersionDALFactory, - "findLatestVersionByFolderId" | "insertMany" - >; + folderVersionDAL: Pick; secretDAL: Pick; secretTagDAL: Pick; secretVersionTagDAL: Pick; - folderDAL: Pick< - TSecretFolderDALFactory, - "findById" | "findBySecretPath" | "delete" | "insertMany" - >; + folderDAL: Pick; permissionService: Pick; licenseService: Pick; }; @@ -67,10 +61,7 @@ export const secretSnapshotServiceFactory = ({ path }: TProjectSnapshotCountDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); const folder = await folderDAL.findBySecretPath(projectId, environment, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); @@ -89,40 +80,26 @@ export const secretSnapshotServiceFactory = ({ offset = 0 }: TProjectSnapshotListDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); const folder = await folderDAL.findBySecretPath(projectId, environment, path); if (!folder) throw new BadRequestError({ message: "Folder not found" }); - const snapshots = await snapshotDAL.find( - { folderId: folder.id }, - { limit, offset, sort: [["createdAt", "desc"]] } - ); + const snapshots = await snapshotDAL.find({ folderId: folder.id }, { limit, offset, sort: [["createdAt", "desc"]] }); return snapshots; }; const getSnapshotData = async ({ actorId, actor, id }: TGetSnapshotDataDTO) => { const snapshot = await snapshotDAL.findSecretSnapshotDataById(id); if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - snapshot.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); return snapshot; }; const performSnapshot = async (folderId: string) => { try { - if (!licenseService.isValidLicense) - throw new InternalServerError({ message: "Invalid license" }); + if (!licenseService.isValidLicense) throw new InternalServerError({ message: "Invalid license" }); const snapshot = await snapshotDAL.transaction(async (tx) => { const folder = await folderDAL.findById(folderId, tx); @@ -170,11 +147,7 @@ export const secretSnapshotServiceFactory = ({ const snapshot = await snapshotDAL.findById(snapshotId); if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - snapshot.projectId - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback @@ -199,9 +172,7 @@ export const secretSnapshotServiceFactory = ({ id, // this means don't bump up the version if not root folder // because below ones can be same version as nothing changed - version: deletedTopLevelFolders[folderId] - ? latestFolderVersion + 1 - : latestFolderVersion, + version: deletedTopLevelFolders[folderId] ? latestFolderVersion + 1 : latestFolderVersion, name, parentId: folderId })) @@ -211,22 +182,10 @@ export const secretSnapshotServiceFactory = ({ const secrets = await secretDAL.insertMany( rollbackSnaps.flatMap(({ secretVersions, folderId }) => secretVersions.map( - ({ - latestSecretVersion, - version, - updatedAt, - createdAt, - secretId, - envId, - id, - tags, - ...el - }) => ({ + ({ latestSecretVersion, version, updatedAt, createdAt, secretId, envId, id, tags, ...el }) => ({ ...el, id: secretId, - version: deletedTopLevelSecsGroupById[secretId] - ? latestSecretVersion + 1 - : latestSecretVersion, + version: deletedTopLevelSecsGroupById[secretId] ? latestSecretVersion + 1 : latestSecretVersion, folderId }) ) @@ -239,8 +198,7 @@ export const secretSnapshotServiceFactory = ({ secretVersions.forEach((secVer) => { secVer.tags.forEach((tag) => { secretTagsToBeInsert.push({ secretsId: secVer.secretId, secret_tagsId: tag.id }); - if (!secretVerTagToBeInsert?.[secVer.secretId]) - secretVerTagToBeInsert[secVer.secretId] = []; + if (!secretVerTagToBeInsert?.[secVer.secretId]) secretVerTagToBeInsert[secVer.secretId] = []; secretVerTagToBeInsert[secVer.secretId].push(tag.id); }); }); diff --git a/backend/src/ee/services/secret-snapshot/snapshot-dal.ts b/backend/src/ee/services/secret-snapshot/snapshot-dal.ts index 66c72ca53..41524c6eb 100644 --- a/backend/src/ee/services/secret-snapshot/snapshot-dal.ts +++ b/backend/src/ee/services/secret-snapshot/snapshot-dal.ts @@ -57,11 +57,7 @@ export const snapshotDALFactory = (db: TDbClient) => { const data = await (tx || db)(TableName.Snapshot) .where(`${TableName.Snapshot}.id`, snapshotId) .join(TableName.Environment, `${TableName.Snapshot}.envId`, `${TableName.Environment}.id`) - .leftJoin( - TableName.SnapshotSecret, - `${TableName.Snapshot}.id`, - `${TableName.SnapshotSecret}.snapshotId` - ) + .leftJoin(TableName.SnapshotSecret, `${TableName.Snapshot}.id`, `${TableName.SnapshotSecret}.snapshotId`) .leftJoin( TableName.SecretVersion, `${TableName.SnapshotSecret}.secretVersionId`, @@ -77,11 +73,7 @@ export const snapshotDALFactory = (db: TDbClient) => { `${TableName.SecretVersionTag}.${TableName.SecretTag}Id`, `${TableName.SecretTag}.id` ) - .leftJoin( - TableName.SnapshotFolder, - `${TableName.SnapshotFolder}.snapshotId`, - `${TableName.Snapshot}.id` - ) + .leftJoin(TableName.SnapshotFolder, `${TableName.SnapshotFolder}.snapshotId`, `${TableName.Snapshot}.id`) .leftJoin( TableName.SecretFolderVersion, `${TableName.SnapshotFolder}.folderVersionId`, @@ -131,13 +123,13 @@ export const snapshotDALFactory = (db: TDbClient) => { { key: "tagVersionId", label: "tags" as const, - mapper: ({ - tagId: id, - tagName: name, - tagSlug: slug, - tagColor: color, - tagVersionId: vId - }) => ({ id, name, slug, color, vId }) + mapper: ({ tagId: id, tagName: name, tagSlug: slug, tagColor: color, tagVersionId: vId }) => ({ + id, + name, + slug, + color, + vId + }) } ] }, @@ -162,7 +154,8 @@ export const snapshotDALFactory = (db: TDbClient) => { try { const data = await (tx || db) .withRecursive("parent", (qb) => { - qb.from(TableName.Snapshot) + void qb + .from(TableName.Snapshot) .leftJoin( TableName.SnapshotFolder, `${TableName.SnapshotFolder}.snapshotId`, @@ -180,44 +173,37 @@ export const snapshotDALFactory = (db: TDbClient) => { db.ref("folderId").withSchema(TableName.SecretFolderVersion).as("folderVerId") ) .where(`${TableName.Snapshot}.id`, snapshotId) - .union((cb) => - cb - .select(selectAllTableCols(TableName.Snapshot)) - .select({ depth: db.raw("parent.depth + 1") }) - .select( - db.ref("name").withSchema(TableName.SecretFolderVersion).as("folderVerName"), - db.ref("folderId").withSchema(TableName.SecretFolderVersion).as("folderVerId") - ) - .from(TableName.Snapshot) - .join( - db(TableName.Snapshot) - .groupBy("folderId") - .max("createdAt") - .select("folderId") - .as("latestVersion"), - `${TableName.Snapshot}.createdAt`, - "latestVersion.max" - ) - .leftJoin( - TableName.SnapshotFolder, - `${TableName.SnapshotFolder}.snapshotId`, - `${TableName.Snapshot}.id` - ) - .leftJoin( - TableName.SecretFolderVersion, - `${TableName.SnapshotFolder}.folderVersionId`, - `${TableName.SecretFolderVersion}.id` - ) - .join("parent", "parent.folderVerId", `${TableName.Snapshot}.folderId`) + .union( + (cb) => + void cb + .select(selectAllTableCols(TableName.Snapshot)) + .select({ depth: db.raw("parent.depth + 1") }) + .select( + db.ref("name").withSchema(TableName.SecretFolderVersion).as("folderVerName"), + db.ref("folderId").withSchema(TableName.SecretFolderVersion).as("folderVerId") + ) + .from(TableName.Snapshot) + .join( + db(TableName.Snapshot).groupBy("folderId").max("createdAt").select("folderId").as("latestVersion"), + `${TableName.Snapshot}.createdAt`, + "latestVersion.max" + ) + .leftJoin( + TableName.SnapshotFolder, + `${TableName.SnapshotFolder}.snapshotId`, + `${TableName.Snapshot}.id` + ) + .leftJoin( + TableName.SecretFolderVersion, + `${TableName.SnapshotFolder}.folderVersionId`, + `${TableName.SecretFolderVersion}.id` + ) + .join("parent", "parent.folderVerId", `${TableName.Snapshot}.folderId`) ); }) .orderBy("depth", "asc") .from("parent") - .leftJoin( - TableName.SnapshotSecret, - `parent.id`, - `${TableName.SnapshotSecret}.snapshotId` - ) + .leftJoin(TableName.SnapshotSecret, `parent.id`, `${TableName.SnapshotSecret}.snapshotId`) .leftJoin( TableName.SecretVersion, `${TableName.SnapshotSecret}.secretVersionId`, @@ -270,11 +256,7 @@ export const snapshotDALFactory = (db: TDbClient) => { const formated = sqlNestRelationships({ data, key: "snapshotId", - parentMapper: ({ - snapshotId: id, - snapshotFolderId: folderId, - snapshotParentFolderId: parentFolderId - }) => ({ + parentMapper: ({ snapshotId: id, snapshotFolderId: folderId, snapshotParentFolderId: parentFolderId }) => ({ id, folderId, parentFolderId @@ -285,19 +267,19 @@ export const snapshotDALFactory = (db: TDbClient) => { label: "secretVersions" as const, mapper: (el) => ({ ...SecretVersionsSchema.parse(el), - latestSecretVersion: el.latestSecretVersion + latestSecretVersion: el.latestSecretVersion as number }), childrenMapper: [ { key: "tagVersionId", label: "tags" as const, - mapper: ({ - tagId: id, - tagName: name, - tagSlug: slug, - tagColor: color, - tagVersionId: vId - }) => ({ id, name, slug, color, vId }) + mapper: ({ tagId: id, tagName: name, tagSlug: slug, tagColor: color, tagVersionId: vId }) => ({ + id, + name, + slug, + color, + vId + }) } ] }, @@ -307,7 +289,7 @@ export const snapshotDALFactory = (db: TDbClient) => { mapper: ({ folderVerId: id, folderVerName: name, latestFolderVersion }) => ({ id, name, - latestFolderVersion + latestFolderVersion: latestFolderVersion as number }) } ] @@ -326,11 +308,7 @@ export const snapshotDALFactory = (db: TDbClient) => { const docs = await (tx || db)(TableName.Snapshot) .where(`${TableName.Snapshot}.folderId`, folderId) .join( - (tx || db)(TableName.Snapshot) - .groupBy("folderId") - .max("createdAt") - .select("folderId") - .as("latestVersion"), + (tx || db)(TableName.Snapshot).groupBy("folderId").max("createdAt").select("folderId").as("latestVersion"), (bd) => { bd.on(`${TableName.Snapshot}.folderId`, "latestVersion.folderId").andOn( `${TableName.Snapshot}.createdAt`, diff --git a/backend/src/ee/services/trusted-ip/trusted-ip-service.ts b/backend/src/ee/services/trusted-ip/trusted-ip-service.ts index 72b0eef9f..a443a32f0 100644 --- a/backend/src/ee/services/trusted-ip/trusted-ip-service.ts +++ b/backend/src/ee/services/trusted-ip/trusted-ip-service.ts @@ -28,36 +28,22 @@ export const trustedIpServiceFactory = ({ }: TTrustedIpServiceFactoryDep) => { const listIpsByProjectId = async ({ projectId, actor, actorId }: TProjectPermission) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.IpAllowList - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList); const trustedIps = await trustedIpDAL.find({ projectId }); return trustedIps; }; - const addProjectIp = async ({ - projectId, - actorId, - actor, - ipAddress: ip, - comment, - isActive - }: TCreateIpDTO) => { + const addProjectIp = async ({ projectId, actorId, actor, ipAddress: ip, comment, isActive }: TCreateIpDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.IpAllowList - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); const project = await projectDAL.findById(projectId); const plan = await licenseService.getPlan(project.orgId); if (!plan.ipAllowlisting) throw new BadRequestError({ - message: - "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." + message: "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." }); const isValidIp = isValidIpOrCidr(ip); @@ -79,26 +65,15 @@ export const trustedIpServiceFactory = ({ return { trustedIp, project }; // for audit log }; - const updateProjectIp = async ({ - projectId, - actorId, - actor, - ipAddress: ip, - comment, - trustedIpId - }: TUpdateIpDTO) => { + const updateProjectIp = async ({ projectId, actorId, actor, ipAddress: ip, comment, trustedIpId }: TUpdateIpDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.IpAllowList - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); const project = await projectDAL.findById(projectId); const plan = await licenseService.getPlan(project.orgId); if (!plan.ipAllowlisting) throw new BadRequestError({ - message: - "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." + message: "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." }); const isValidIp = isValidIpOrCidr(ip); @@ -124,17 +99,13 @@ export const trustedIpServiceFactory = ({ const deleteProjectIp = async ({ projectId, actorId, actor, trustedIpId }: TDeleteIpDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.IpAllowList - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); const project = await projectDAL.findById(projectId); const plan = await licenseService.getPlan(project.orgId); if (!plan.ipAllowlisting) throw new BadRequestError({ - message: - "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." + message: "Failed to add IP access range due to plan restriction. Upgrade plan to add IP access range." }); const [trustedIp] = await trustedIpDAL.delete({ projectId, id: trustedIpId }); diff --git a/backend/src/lib/casl/index.ts b/backend/src/lib/casl/index.ts index 1e853cc4d..9e5cb29d3 100644 --- a/backend/src/lib/casl/index.ts +++ b/backend/src/lib/casl/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { buildMongoQueryMatcher, MongoAbility } from "@casl/ability"; import { FieldCondition, FieldInstruction, JsInterpreter } from "@ucast/mongo2js"; import picomatch from "picomatch"; @@ -12,7 +13,7 @@ const $glob: FieldInstruction = { }; const glob: JsInterpreter> = (node, object, context) => { - const secretPath = context.get(object, node.field); + const secretPath = context.get(object, node.field) as string; const permissionSecretGlobPath = node.value; return picomatch.isMatch(secretPath, permissionSecretGlobPath, { strictSlashes: false }); }; @@ -23,7 +24,7 @@ export const conditionsMatcher = buildMongoQueryMatcher({ $glob }, { glob }); * Extracts and formats permissions from a CASL Ability object or a raw permission set. */ const extractPermissions = (ability: MongoAbility) => - ability.rules.map((permission) => `${permission.action}_${permission.subject}`); + ability.rules.map((permission) => `${permission.action as string}_${permission.subject as string}`); /** * Compares two sets of permissions to determine if the first set is at least as privileged as the second set. diff --git a/backend/src/lib/config/env.ts b/backend/src/lib/config/env.ts index 1b9f9c631..62217749e 100644 --- a/backend/src/lib/config/env.ts +++ b/backend/src/lib/config/env.ts @@ -39,9 +39,7 @@ const envSchema = z // Telemetry TELEMETRY_ENABLED: zodStrBool.default("true"), POSTHOG_HOST: zpStr(z.string().optional().default("https://app.posthog.com")), - POSTHOG_PROJECT_API_KEY: zpStr( - z.string().optional().default("phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE") - ), + POSTHOG_PROJECT_API_KEY: zpStr(z.string().optional().default("phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE")), LOOPS_API_KEY: zpStr(z.string().optional()), // jwt options AUTH_SECRET: zpStr(z.string()).default(process.env.JWT_AUTH_SECRET), // for those still using old JWT_AUTH_SECRET @@ -57,7 +55,12 @@ const envSchema = z CLIENT_SECRET_GITHUB_LOGIN: zpStr(z.string().optional()), CLIENT_ID_GITLAB_LOGIN: zpStr(z.string().optional()), CLIENT_SECRET_GITLAB_LOGIN: zpStr(z.string().optional()), - CLIENT_GITLAB_LOGIN_URL: zpStr(z.string().optional().default(process.env.URL_GITLAB_LOGIN ?? GITLAB_URL)), // fallback since URL_GITLAB_LOGIN has been renamed + CLIENT_GITLAB_LOGIN_URL: zpStr( + z + .string() + .optional() + .default(process.env.URL_GITLAB_LOGIN ?? GITLAB_URL) + ), // fallback since URL_GITLAB_LOGIN has been renamed // integration client secrets // heroku CLIENT_ID_HEROKU: zpStr(z.string().optional()), @@ -122,7 +125,7 @@ export const initEnvConfig = (logger: Logger) => { logger.error(parsedEnv.error.issues); process.exit(-1); } - + envCfg = Object.freeze(parsedEnv.data); return envCfg; }; diff --git a/backend/src/lib/config/request.ts b/backend/src/lib/config/request.ts index 091713727..8636b7476 100644 --- a/backend/src/lib/config/request.ts +++ b/backend/src/lib/config/request.ts @@ -5,6 +5,7 @@ export const request = axios.create(); axiosRetry(request, { retries: 3, + // eslint-disable-next-line retryDelay: axiosRetry.exponentialDelay, retryCondition: (err) => axiosRetry.isNetworkError(err) || axiosRetry.isRetryableError(err) }); diff --git a/backend/src/lib/crypto/encryption.ts b/backend/src/lib/crypto/encryption.ts index ef1a7afe0..74febccec 100644 --- a/backend/src/lib/crypto/encryption.ts +++ b/backend/src/lib/crypto/encryption.ts @@ -20,11 +20,7 @@ export const BLOCK_SIZE_BYTES_16 = 16; export const decryptSymmetric = ({ ciphertext, iv, tag, key }: TDecryptSymmetricInput): string => { const secretKey = crypto.createSecretKey(key, "base64"); - const decipher = crypto.createDecipheriv( - SecretEncryptionAlgo.AES_256_GCM, - secretKey, - Buffer.from(iv, "base64") - ); + const decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, secretKey, Buffer.from(iv, "base64")); decipher.setAuthTag(Buffer.from(tag, "base64")); let cleartext = decipher.update(ciphertext, "base64", "utf8"); cleartext += decipher.final("utf8"); @@ -62,17 +58,8 @@ export const encryptSymmetric128BitHexKeyUTF8 = (plaintext: string, key: string) }; }; -export const decryptSymmetric128BitHexKeyUTF8 = ({ - ciphertext, - iv, - tag, - key -}: TDecryptSymmetricInput): string => { - const decipher = crypto.createDecipheriv( - SecretEncryptionAlgo.AES_256_GCM, - key, - Buffer.from(iv, "base64") - ); +export const decryptSymmetric128BitHexKeyUTF8 = ({ ciphertext, iv, tag, key }: TDecryptSymmetricInput): string => { + const decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, key, Buffer.from(iv, "base64")); decipher.setAuthTag(Buffer.from(tag, "base64")); @@ -104,12 +91,7 @@ export type TDecryptAsymmetricInput = { privateKey: string; }; -export const decryptAsymmetric = ({ - ciphertext, - nonce, - publicKey, - privateKey -}: TDecryptAsymmetricInput) => { +export const decryptAsymmetric = ({ ciphertext, nonce, publicKey, privateKey }: TDecryptAsymmetricInput) => { const plaintext: Uint8Array | null = nacl.box.open( naclUtils.decodeBase64(ciphertext), naclUtils.decodeBase64(nonce), @@ -223,7 +205,7 @@ export const infisicalSymmetricEncypt = (data: string) => { throw new Error("Missing both encryption keys"); }; -export const infisicalSymmetricDecrypt = ({ +export const infisicalSymmetricDecrypt = ({ keyEncoding, ciphertext, tag, diff --git a/backend/src/lib/fn/array.ts b/backend/src/lib/fn/array.ts index abe8e3466..1e075101b 100644 --- a/backend/src/lib/fn/array.ts +++ b/backend/src/lib/fn/array.ts @@ -23,13 +23,10 @@ export const groupBy = ( * to convert each item in the list to a comparable identity * value */ -export const unique = ( - array: readonly T[], - toKey?: (item: T) => K -): T[] => { +export const unique = (array: readonly T[], toKey?: (item: T) => K): T[] => { const valueMap = array.reduce( (acc, item) => { - const key = toKey ? toKey(item) : (item as any as string | number | symbol); + const key = toKey ? toKey(item) : (item as unknown as string | number | symbol); if (acc[key]) return acc; acc[key] = item; return acc; diff --git a/backend/src/lib/fn/dates.ts b/backend/src/lib/fn/dates.ts new file mode 100644 index 000000000..e69de29bb diff --git a/backend/src/lib/fn/object.ts b/backend/src/lib/fn/object.ts index 2fadc685c..87db80343 100644 --- a/backend/src/lib/fn/object.ts +++ b/backend/src/lib/fn/object.ts @@ -2,10 +2,7 @@ * Pick a list of properties from an object * into a new object */ -export const pick = ( - obj: T, - keys: TKeys[] -): Pick => { +export const pick = (obj: T, keys: TKeys[]): Pick => { if (!obj) return {} as Pick; return keys.reduce( (acc, key) => { @@ -21,9 +18,9 @@ export const pick = ( * object. Optional second argument shakes out values * by custom evaluation. */ -export const shake = ( +export const shake = ( obj: T, - filter: (value: any) => boolean = (x) => x === undefined || x === null + filter: (value: unknown) => boolean = (x) => x === undefined || x === null ): Omit => { if (!obj) return {} as T; const keys = Object.keys(obj) as (keyof T)[]; diff --git a/backend/src/lib/ip/index.ts b/backend/src/lib/ip/index.ts index f14ed4f41..30f710d19 100644 --- a/backend/src/lib/ip/index.ts +++ b/backend/src/lib/ip/index.ts @@ -111,13 +111,7 @@ export type TIp = { /** * Validates the IP address [ipAddress] against the trusted IPs [trustedIps]. */ -export const checkIPAgainstBlocklist = ({ - ipAddress, - trustedIps -}: { - ipAddress: string; - trustedIps: TIp[]; -}) => { +export const checkIPAgainstBlocklist = ({ ipAddress, trustedIps }: { ipAddress: string; trustedIps: TIp[] }) => { const blockList = new net.BlockList(); for (const trustedIp of trustedIps) { diff --git a/backend/src/lib/knex/index.ts b/backend/src/lib/knex/index.ts index 515089e65..37fae624e 100644 --- a/backend/src/lib/knex/index.ts +++ b/backend/src/lib/knex/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ import { Knex } from "knex"; import { Tables } from "knex/types/tables"; @@ -15,22 +16,22 @@ export const withTransaction = (db: Knex, dal: K) => ({ ...dal }); -export type TFindFilter = Partial & { +export type TFindFilter = Partial & { $in?: Partial<{ [k in keyof R]: R[k][] }>; }; export const buildFindFilter = - ({ $in, ...filter }: TFindFilter) => + ({ $in, ...filter }: TFindFilter) => (bd: Knex.QueryBuilder) => { - bd.where(filter); + void bd.where(filter); if ($in) { Object.entries($in).forEach(([key, val]) => { - bd.whereIn(key as any, val as any); + void bd.whereIn(key as never, val as never); }); } return bd; }; -export type TFindOpt = { +export type TFindOpt = { limit?: number; offset?: number; sort?: Array<[keyof R, "asc" | "desc"] | [keyof R, "asc" | "desc", "first" | "last"]>; @@ -40,11 +41,7 @@ export type TFindOpt = { // What is ormify // It is to inject typical operations like find, findOne, update, delete, create // This will avoid writing most common ones each time -export const ormify = ( - db: Knex, - tableName: Tname, - dal?: DbOps -) => ({ +export const ormify = (db: Knex, tableName: Tname, dal?: DbOps) => ({ transaction: async (cb: (tx: Knex) => Promise) => db.transaction(async (trx) => { const res = await cb(trx); @@ -53,7 +50,7 @@ export const ormify = ( findById: async (id: string, tx?: Knex) => { try { const result = await (tx || db)(tableName) - .where({ id } as any) + .where({ id } as never) .first("*"); return result; } catch (error) { @@ -74,12 +71,10 @@ export const ormify = ( ) => { try { const query = (tx || db)(tableName).where(buildFindFilter(filter)); - if (limit) query.limit(limit); - if (offset) query.offset(offset); + if (limit) void query.limit(limit); + if (offset) void query.offset(offset); if (sort) { - query.orderBy( - sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls })) - ); + void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls }))); } const res = await query; return res; @@ -90,7 +85,7 @@ export const ormify = ( create: async (data: Tables[Tname]["insert"], tx?: Knex) => { try { const [res] = await (tx || db)(tableName) - .insert(data as any) + .insert(data as never) .returning("*"); return res; } catch (error) { @@ -101,7 +96,7 @@ export const ormify = ( try { if (!data.length) return []; const res = await (tx || db)(tableName) - .insert(data as any) + .insert(data as never) .returning("*"); return res; } catch (error) { @@ -111,23 +106,19 @@ export const ormify = ( updateById: async (id: string, data: Tables[Tname]["update"], tx?: Knex) => { try { const [res] = await (tx || db)(tableName) - .where({ id } as any) - .update(data as any) + .where({ id } as never) + .update(data as never) .returning("*"); return res; } catch (error) { throw new DatabaseError({ error, name: "Update by id" }); } }, - update: async ( - filter: TFindFilter, - data: Tables[Tname]["update"], - tx?: Knex - ) => { + update: async (filter: TFindFilter, data: Tables[Tname]["update"], tx?: Knex) => { try { const res = await (tx || db)(tableName) .where(buildFindFilter(filter)) - .update(data as any) + .update(data as never) .returning("*"); return res; } catch (error) { @@ -137,7 +128,7 @@ export const ormify = ( deleteById: async (id: string, tx?: Knex) => { try { const [res] = await (tx || db)(tableName) - .where({ id } as any) + .where({ id } as never) .delete() .returning("*"); return res; @@ -147,10 +138,7 @@ export const ormify = ( }, delete: async (filter: TFindFilter, tx?: Knex) => { try { - const res = await (tx || db)(tableName) - .where(buildFindFilter(filter)) - .delete() - .returning("*"); + const res = await (tx || db)(tableName).where(buildFindFilter(filter)).delete().returning("*"); return res; } catch (error) { throw new DatabaseError({ error, name: "Delete" }); diff --git a/backend/src/lib/knex/join.ts b/backend/src/lib/knex/join.ts index 59144fbcc..6580bfa3d 100644 --- a/backend/src/lib/knex/join.ts +++ b/backend/src/lib/knex/join.ts @@ -1,7 +1,16 @@ +/* eslint-disable @typescript-eslint/ban-types */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +// TODO(akhilmhdh): make this better later + export const mergeOneToManyRelation = < - T extends Record, + T extends Record, Pk extends keyof T, - P extends Record, + P extends Record, + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint C extends any, Ck extends string = "child" >( @@ -22,7 +31,7 @@ export const mergeOneToManyRelation = < const parent = parentMapper(row) as any; parent[childKey] = []; groupedRecord.push(parent); - prevPkId = pk; + prevPkId = pk as string; prevPkIndex += 1; } groupedRecord[prevPkIndex][childKey].push(childMapper(row)); @@ -41,6 +50,7 @@ export type TSqlPackRelationships< childrenMapper: C; }; +// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint export type TChildMapper = { key: keyof T; label: U; @@ -82,7 +92,7 @@ const sqlChildMapper = < const ck = `${prefix}-${label}-${doc[childPk]}`; const val = mapper(doc); if (!lookupTable.has(ck)) { - if (typeof val !== "undefined" && val !== null) docsByPk[pk as keyof P][label].push(val); + if (typeof val !== "undefined" && val !== null) docsByPk[pk][label].push(val); lookupTable.add(ck); } if (nestedMappers && val) { diff --git a/backend/src/lib/logger/index.ts b/backend/src/lib/logger/index.ts index df012e434..21cb66920 100644 --- a/backend/src/lib/logger/index.ts +++ b/backend/src/lib/logger/index.ts @@ -1 +1 @@ -export { initLogger,logger } from "./logger"; +export { initLogger, logger } from "./logger"; diff --git a/backend/src/lib/logger/logger.ts b/backend/src/lib/logger/logger.ts index 82db1725c..c124fcf4c 100644 --- a/backend/src/lib/logger/logger.ts +++ b/backend/src/lib/logger/logger.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ // logger follows a singleton pattern // easier to use it that's all. import pino, { Logger } from "pino"; @@ -14,10 +15,10 @@ const logLevelToSeverityLookup: Record = { // eslint-disable-next-line import/no-mutable-exports export let logger: Readonly; -// akhilmhdh: -// The logger is not placed in the main app config to avoid a circular dependency. -// The config requires the logger to display errors when an invalid environment is supplied. -// On the other hand, the logger needs the config to obtain credentials for AWS or other transports. +// akhilmhdh: +// The logger is not placed in the main app config to avoid a circular dependency. +// The config requires the logger to display errors when an invalid environment is supplied. +// On the other hand, the logger needs the config to obtain credentials for AWS or other transports. // By keeping the logger separate, it becomes an independent package. const loggerConfig = z.object({ @@ -25,14 +26,23 @@ const loggerConfig = z.object({ AWS_CLOUDWATCH_LOG_REGION: z.string().default("us-east-1"), AWS_CLOUDWATCH_LOG_ACCESS_KEY_ID: z.string().min(1).optional(), AWS_CLOUDWATCH_LOG_ACCESS_KEY_SECRET: z.string().min(1).optional(), - AWS_CLOUDWATCH_LOG_INTERVAL: z.coerce.number().default(1000) + AWS_CLOUDWATCH_LOG_INTERVAL: z.coerce.number().default(1000), + NODE_ENV: z.enum(["development", "test", "production"]).default("production") }); export const initLogger = async () => { - const targets: pino.TransportMultiOptions["targets"][number][] = [ - { level: "info", target: "pino/file", options: {} } - ]; const cfg = loggerConfig.parse(process.env); + const targets: pino.TransportMultiOptions["targets"][number][] = [ + { + level: "info", + target: "pino/file", + options: { + destination: 1, + mkdir: true + } + } + ]; + if (cfg.AWS_CLOUDWATCH_LOG_ACCESS_KEY_ID && cfg.AWS_CLOUDWATCH_LOG_ACCESS_KEY_SECRET) { targets.push({ target: "@serdnam/pino-cloudwatch-transport", @@ -66,6 +76,7 @@ export const initLogger = async () => { }) } }, + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument transport ); return logger; diff --git a/backend/src/lib/zod/index.ts b/backend/src/lib/zod/index.ts index 331a5fbb6..a3cded66b 100644 --- a/backend/src/lib/zod/index.ts +++ b/backend/src/lib/zod/index.ts @@ -1,10 +1,7 @@ -import { z,ZodTypeAny } from "zod"; +import { z, ZodTypeAny } from "zod"; // this is a patched zod string to remove empty string to undefined -export const zpStr = ( - schema: T, - opt: { stripNull: boolean } = { stripNull: true } -) => +export const zpStr = (schema: T, opt: { stripNull: boolean } = { stripNull: true }) => z.preprocess((val) => { if (opt.stripNull && val === null) return undefined; if (typeof val !== "string") return val; diff --git a/backend/src/main.ts b/backend/src/main.ts index 07499f479..f85eed3ed 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -22,19 +22,21 @@ const run = async () => { const server = await main({ db, smtp, logger, queue }); const bootstrap = await bootstrapCheck({ db }); + // eslint-disable-next-line process.on("SIGINT", async () => { await server.close(); await db.destroy(); process.exit(0); }); + // eslint-disable-next-line process.on("SIGTERM", async () => { await server.close(); await db.destroy(); process.exit(0); }); - server.listen({ + await server.listen({ port: appCfg.PORT, host: appCfg.HOST, listenTextResolver: (address) => { @@ -44,4 +46,4 @@ const run = async () => { }); }; -run(); +void run(); diff --git a/backend/src/queue/queue-service.ts b/backend/src/queue/queue-service.ts index 8a170487f..58c829549 100644 --- a/backend/src/queue/queue-service.ts +++ b/backend/src/queue/queue-service.ts @@ -1,4 +1,4 @@ -import { Job, JobsOptions, Queue, RepeatOptions, Worker, WorkerListener } from "bullmq"; +import { Job, JobsOptions, Queue, QueueOptions, RepeatOptions, Worker, WorkerListener } from "bullmq"; import Redis from "ioredis"; import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types"; @@ -11,6 +11,7 @@ export enum QueueName { SecretRotation = "secret-rotation", SecretReminder = "secret-reminder", AuditLog = "audit-log", + AuditLogPrune = "audit-log-prune", IntegrationSync = "sync-integrations", SecretWebhook = "secret-webhook", SecretFullRepoScan = "secret-full-repo-scan", @@ -21,6 +22,7 @@ export enum QueueJobs { SecretReminder = "secret-reminder-job", SecretRotation = "secret-rotation-job", AuditLog = "audit-log-job", + AuditLogPrune = "audit-log-prune-job", SecWebhook = "secret-webhook-trigger", IntegrationSync = "secret-integration-pull", SecretScan = "secret-scan" @@ -45,6 +47,10 @@ export type TQueueJobTypes = { name: QueueJobs.AuditLog; payload: TCreateAuditLogDTO; }; + [QueueName.AuditLogPrune]: { + name: QueueJobs.AuditLogPrune; + payload: undefined; + }; [QueueName.SecretWebhook]: { name: QueueJobs.SecWebhook; payload: { projectId: string; environment: string; secretPath: string }; @@ -63,37 +69,36 @@ export type TQueueJobTypes = { export type TQueueServiceFactory = ReturnType; export const queueServiceFactory = (redisUrl: string) => { const connection = new Redis(redisUrl, { maxRetriesPerRequest: null }); - const queueContainer: Record< + const queueContainer = {} as Record< QueueName, Queue - > = {} as any; - const workerContainer: Record< + >; + const workerContainer = {} as Record< QueueName, Worker - > = {} as any; + >; const start = ( name: T, - jobFn: ( - job: Job - ) => Promise + jobFn: (job: Job) => Promise, + queueSettings: Omit = {} ) => { if (queueContainer[name]) { throw new Error(`${name} queue is already initialized`); } - queueContainer[name] = new Queue( - name as string, - { connection } - ); - workerContainer[name] = new Worker< - TQueueJobTypes[T]["payload"], - void, - TQueueJobTypes[T]["name"] - >(name, jobFn, { connection }); + queueContainer[name] = new Queue(name as string, { + ...queueSettings, + connection + }); + + workerContainer[name] = new Worker(name, jobFn, { + ...queueSettings, + connection + }); }; - const listen = async < + const listen = < T extends QueueName, U extends keyof WorkerListener >( @@ -134,9 +139,20 @@ export const queueServiceFactory = (redisUrl: string) => { return q.removeRepeatableByKey(job.repeatJobKey); }; + const stopJobById = async (name: T, jobId: string) => { + const q = queueContainer[name]; + const job = await q.getJob(jobId); + return job?.remove().catch(() => undefined); + }; + + const clearQueue = async (name: QueueName) => { + const q = queueContainer[name]; + await q.drain(); + }; + const shutdown = async () => { await Promise.all(Object.values(workerContainer).map((worker) => worker.close())); }; - return { start, listen, queue, shutdown, stopRepeatableJob, stopRepeatableJobByJobId }; + return { start, listen, queue, shutdown, stopRepeatableJob, stopRepeatableJobByJobId, clearQueue, stopJobById }; }; diff --git a/backend/src/server/app.ts b/backend/src/server/app.ts index c8eb0d28f..15f7bcfa8 100644 --- a/backend/src/server/app.ts +++ b/backend/src/server/app.ts @@ -5,6 +5,7 @@ import type { FastifyCookieOptions } from "@fastify/cookie"; import cookie from "@fastify/cookie"; import type { FastifyCorsOptions } from "@fastify/cors"; import cors from "@fastify/cors"; +import fastifyEtag from "@fastify/etag"; import fastifyFormBody from "@fastify/formbody"; import helmet from "@fastify/helmet"; import type { FastifyRateLimitOptions } from "@fastify/rate-limit"; @@ -50,6 +51,8 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => { secret: appCfg.COOKIE_SECRET_SIGN_KEY }); + await server.register(fastifyEtag); + await server.register(cors, { credentials: true, origin: appCfg.SITE_URL || true diff --git a/backend/src/server/config/rateLimiter.ts b/backend/src/server/config/rateLimiter.ts index 7982cff63..444158cbf 100644 --- a/backend/src/server/config/rateLimiter.ts +++ b/backend/src/server/config/rateLimiter.ts @@ -6,7 +6,7 @@ import { getConfig } from "@app/lib/config/env"; export const globalRateLimiterCfg = (): RateLimitPluginOptions => { const appCfg = getConfig(); const redis = appCfg.isRedisConfigured - ? new Redis(appCfg.REDIS_URL as string, { connectTimeout: 500, maxRetriesPerRequest: 1 }) + ? new Redis(appCfg.REDIS_URL, { connectTimeout: 500, maxRetriesPerRequest: 1 }) : null; return { diff --git a/backend/src/server/plugins/auth/inject-identity.ts b/backend/src/server/plugins/auth/inject-identity.ts index f60b9f4c5..04d4cbe0e 100644 --- a/backend/src/server/plugins/auth/inject-identity.ts +++ b/backend/src/server/plugins/auth/inject-identity.ts @@ -5,12 +5,7 @@ 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 { - ActorType, - AuthMode, - AuthModeJwtTokenPayload, - AuthTokenType -} from "@app/services/auth/auth-type"; +import { ActorType, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type"; import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types"; export type TAuthMode = @@ -87,16 +82,12 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => { switch (authMode) { case AuthMode.JWT: { - const { user, tokenVersionId } = - await server.services.authToken.fnValidateJwtIdentity(token); + const { user, tokenVersionId } = await server.services.authToken.fnValidateJwtIdentity(token); req.auth = { authMode: AuthMode.JWT, user, userId: user.id, tokenVersionId, actor }; break; } case AuthMode.IDENTITY_ACCESS_TOKEN: { - const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken( - token, - req.realIp - ); + const identity = await server.services.identityAccessToken.fnValidateIdentityAccessToken(token, req.realIp); req.auth = { authMode: AuthMode.IDENTITY_ACCESS_TOKEN, actor, @@ -106,9 +97,7 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => { break; } case AuthMode.SERVICE_TOKEN: { - const serviceToken = await server.services.serviceToken.fnValidateServiceToken( - token as string - ); + const serviceToken = await server.services.serviceToken.fnValidateServiceToken(token); req.auth = { authMode: AuthMode.SERVICE_TOKEN as const, serviceToken, diff --git a/backend/src/server/plugins/auth/superAdmin.ts b/backend/src/server/plugins/auth/superAdmin.ts index 2cd7181bd..d5dee581b 100644 --- a/backend/src/server/plugins/auth/superAdmin.ts +++ b/backend/src/server/plugins/auth/superAdmin.ts @@ -1,12 +1,17 @@ -import { FastifyRequest } from "fastify"; +import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify"; import { UnauthorizedError } from "@app/lib/errors"; import { ActorType } from "@app/services/auth/auth-type"; -export const verifySuperAdmin = async (req: T) => { +export const verifySuperAdmin = ( + req: T, + _res: FastifyReply, + done: HookHandlerDoneFunction +) => { if (req.auth.actor !== ActorType.USER || !req.auth.user.superAdmin) throw new UnauthorizedError({ name: "Unauthorized access", message: "Requires superadmin access" }); + done(); }; diff --git a/backend/src/server/plugins/auth/verify-auth.ts b/backend/src/server/plugins/auth/verify-auth.ts index cfd856dbe..a1274f356 100644 --- a/backend/src/server/plugins/auth/verify-auth.ts +++ b/backend/src/server/plugins/auth/verify-auth.ts @@ -1,17 +1,17 @@ -import { FastifyRequest } from "fastify"; +import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify"; import { UnauthorizedError } from "@app/lib/errors"; import { AuthMode } from "@app/services/auth/auth-type"; export const verifyAuth = (authStrats: AuthMode[]) => - async (req: T) => { + (req: T, _res: FastifyReply, done: HookHandlerDoneFunction) => { if (!Array.isArray(authStrats)) throw new Error("Auth strategy must be array"); - if (!req.auth) - throw new UnauthorizedError({ name: "Unauthorized access", message: "Token missing" }); + if (!req.auth) throw new UnauthorizedError({ name: "Unauthorized access", message: "Token missing" }); const isAccessAllowed = authStrats.some((strat) => strat === req.auth.authMode); if (!isAccessAllowed) { throw new UnauthorizedError({ name: `${req.url} Unauthorized Access` }); } + done(); }; diff --git a/backend/src/server/plugins/error-handler.ts b/backend/src/server/plugins/error-handler.ts index cacc91885..8587c93bd 100644 --- a/backend/src/server/plugins/error-handler.ts +++ b/backend/src/server/plugins/error-handler.ts @@ -2,33 +2,27 @@ import { ForbiddenError } from "@casl/ability"; import fastifyPlugin from "fastify-plugin"; import { ZodError } from "zod"; -import { - BadRequestError, - DatabaseError, - ForbiddenRequestError, - InternalServerError, - UnauthorizedError -} from "@app/lib/errors"; +import { BadRequestError, DatabaseError, InternalServerError, UnauthorizedError } from "@app/lib/errors"; export const fastifyErrHandler = fastifyPlugin(async (server: FastifyZodProvider) => { server.setErrorHandler((error, req, res) => { req.log.error(error); if (error instanceof BadRequestError) { - res.status(400).send({ statusCode: 400, message: error.message, error: error.name }); + void res.status(400).send({ statusCode: 400, message: error.message, error: error.name }); } else if (error instanceof UnauthorizedError) { - res.status(403).send({ statusCode: 403, message: error.message, error: error.name }); + void res.status(403).send({ statusCode: 403, message: error.message, error: error.name }); } else if (error instanceof DatabaseError || error instanceof InternalServerError) { - res.status(500).send({ statusCode: 500, message: "Something went wrong", error: error.name }); + void res.status(500).send({ statusCode: 500, message: "Something went wrong", error: error.name }); } else if (error instanceof ZodError) { - res.status(403).send({ statusCode: 403, error: "ValidationFailure", message: error.issues }); + void res.status(403).send({ statusCode: 403, error: "ValidationFailure", message: error.issues }); } else if (error instanceof ForbiddenError) { - res.status(401).send({ + void res.status(401).send({ statusCode: 401, error: "PermissionDenied", message: `You are not allowed to ${error.action} on ${error.subjectType}` }); } else { - res.send(error); + void res.send(error); } }); }); diff --git a/backend/src/server/plugins/external-nextjs.ts b/backend/src/server/plugins/external-nextjs.ts index 010e92f96..89cf45125 100644 --- a/backend/src/server/plugins/external-nextjs.ts +++ b/backend/src/server/plugins/external-nextjs.ts @@ -20,17 +20,19 @@ export const registerExternalNextjs = async ( if (standaloneMode) { const nextJsBuildPath = path.join(dir, "frontend-build"); - const { default: conf } = await import( + const { default: conf } = (await import( path.join(dir, "frontend-build/.next/required-server-files.json"), // @ts-expect-error type { assert: { type: "json" } } - ); + )) as { default: { config: string } }; + /* eslint-disable */ const { default: NextServer } = ( await import(path.join(dir, "frontend-build/node_modules/next/dist/server/next-server.js")) ).default; + const nextApp = new NextServer({ dev: false, dir: nextJsBuildPath, @@ -52,5 +54,6 @@ export const registerExternalNextjs = async ( }); server.addHook("onClose", () => nextApp.close()); await nextApp.prepare(); + /* eslint-enable */ } }; diff --git a/backend/src/server/plugins/secret-scanner.ts b/backend/src/server/plugins/secret-scanner.ts index f90b39114..8790d54d4 100644 --- a/backend/src/server/plugins/secret-scanner.ts +++ b/backend/src/server/plugins/secret-scanner.ts @@ -1,3 +1,4 @@ +import { PushEvent } from "@octokit/webhooks-types"; import { Probot } from "probot"; import SmeeClient from "smee-client"; @@ -22,7 +23,7 @@ export const registerSecretScannerGhApp = async (server: FastifyZodProvider) => app.on("push", async (context) => { const { payload } = context; - await server.services.secretScanning.handleRepoPushEvent(payload as any); + await server.services.secretScanning.handleRepoPushEvent(payload as PushEvent); }); }; @@ -49,16 +50,17 @@ export const registerSecretScannerGhApp = async (server: FastifyZodProvider) => method: "POST", url: "/", handler: async (req, res) => { - const eventName = req.headers["x-github-event"] as any; + const eventName = req.headers["x-github-event"]; const signatureSHA256 = req.headers["x-hub-signature-256"] as string; const id = req.headers["x-github-delivery"] as string; await probot.webhooks.verifyAndReceive({ id, + // @ts-expect-error type name: eventName, payload: req.body as string, signature: signatureSHA256 }); - res.send("ok"); + void res.send("ok"); } }); } diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 16d32aada..fe79a6f11 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -92,10 +92,7 @@ import { serviceTokenDALFactory } from "@app/services/service-token/service-toke import { serviceTokenServiceFactory } from "@app/services/service-token/service-token-service"; import { TSmtpService } from "@app/services/smtp/smtp-service"; import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal"; -import { - getServerCfg, - superAdminServiceFactory -} from "@app/services/super-admin/super-admin-service"; +import { getServerCfg, superAdminServiceFactory } from "@app/services/super-admin/super-admin-service"; import { telemetryServiceFactory } from "@app/services/telemetry/telemetry-service"; import { userDALFactory } from "@app/services/user/user-dal"; import { userServiceFactory } from "@app/services/user/user-service"; @@ -112,13 +109,9 @@ import { registerV3Routes } from "./v3"; export const registerRoutes = async ( server: FastifyZodProvider, - { - db, - smtp: smtpService, - queue: queueService - }: { db: Knex; smtp: TSmtpService; queue: TQueueServiceFactory } + { db, smtp: smtpService, queue: queueService }: { db: Knex; smtp: TSmtpService; queue: TQueueServiceFactory } ) => { - server.register(registerSecretScannerGhApp, { prefix: "/ss-webhook" }); + await server.register(registerSecretScannerGhApp, { prefix: "/ss-webhook" }); // db layers const userDAL = userDALFactory(db); @@ -236,6 +229,7 @@ export const registerRoutes = async ( orgDAL, incidentContactDAL, tokenService, + projectDAL, smtpService, userDAL, orgBotDAL @@ -450,6 +444,7 @@ export const registerRoutes = async ( }); await superAdminService.initServerCfg(); + await auditLogQueue.startAuditLogPruneJob(); // setup the communication with license key server await licenseService.init(); // inject all services diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts index e5ea127c5..b8b61216b 100644 --- a/backend/src/server/routes/v1/admin-router.ts +++ b/backend/src/server/routes/v1/admin-router.ts @@ -39,10 +39,10 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: (req, _, done) => { - verifyAuth([AuthMode.JWT, AuthMode.API_KEY])(req); - verifySuperAdmin(req); - done(); + onRequest: (req, res, done) => { + verifyAuth([AuthMode.JWT, AuthMode.API_KEY])(req, res, () => { + verifySuperAdmin(req, res, done); + }); }, handler: async (req) => { const config = await server.services.superAdmin.updateServerCfg(req.body); @@ -97,7 +97,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { } }); - res.setCookie("jid", token.refresh, { + void res.setCookie("jid", token.refresh, { httpOnly: true, path: "/", sameSite: "strict", diff --git a/backend/src/server/routes/v1/auth-router.ts b/backend/src/server/routes/v1/auth-router.ts index 871b56c1f..2dc0a5d50 100644 --- a/backend/src/server/routes/v1/auth-router.ts +++ b/backend/src/server/routes/v1/auth-router.ts @@ -5,18 +5,14 @@ import { getConfig } from "@app/lib/config/env"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { authRateLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; -import { - AuthMode, - AuthModeRefreshJwtTokenPayload, - AuthTokenType -} from "@app/services/auth/auth-type"; +import { AuthMode, AuthModeRefreshJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type"; export const registerAuthRoutes = async (server: FastifyZodProvider) => { server.route({ url: "/logout", method: "POST", - config:{ - rateLimit:authRateLimit + config: { + rateLimit: authRateLimit }, schema: { response: { @@ -31,7 +27,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => { if (req.auth.authMode === AuthMode.JWT) { await server.services.login.logout(req.permission.id, req.auth.tokenVersionId); } - res.cookie("jid", "", { + void res.cookie("jid", "", { httpOnly: true, path: "/", sameSite: "strict", @@ -74,10 +70,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => { message: "Failed to find refresh token" }); - const decodedToken = jwt.verify( - refreshToken, - appCfg.AUTH_SECRET - ) as AuthModeRefreshJwtTokenPayload; + const decodedToken = jwt.verify(refreshToken, appCfg.AUTH_SECRET) as AuthModeRefreshJwtTokenPayload; if (decodedToken.authTokenType !== AuthTokenType.REFRESH_TOKEN) throw new UnauthorizedError({ message: "Invalid token", name: "Auth token route" }); @@ -85,8 +78,7 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => { decodedToken.tokenVersionId, decodedToken.userId ); - if (!tokenVersion) - throw new UnauthorizedError({ message: "Invalid token", name: "Auth token route" }); + if (!tokenVersion) throw new UnauthorizedError({ message: "Invalid token", name: "Auth token route" }); if (decodedToken.refreshVersion !== tokenVersion.refreshVersion) throw new UnauthorizedError({ message: "Invalid token", name: "Auth token route" }); diff --git a/backend/src/server/routes/v1/identity-access-token-router.ts b/backend/src/server/routes/v1/identity-access-token-router.ts index b39fe08a2..4ddf7b74a 100644 --- a/backend/src/server/routes/v1/identity-access-token-router.ts +++ b/backend/src/server/routes/v1/identity-access-token-router.ts @@ -18,10 +18,9 @@ export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvid } }, handler: async (req) => { - const { accessToken, identityAccessToken } = - await server.services.identityAccessToken.renewAccessToken({ - accessToken: req.body.accessToken - }); + const { accessToken, identityAccessToken } = await server.services.identityAccessToken.renewAccessToken({ + accessToken: req.body.accessToken + }); return { accessToken, tokenType: "Bearer" as const, diff --git a/backend/src/server/routes/v1/identity-ua.ts b/backend/src/server/routes/v1/identity-ua.ts index 1c7c6e5f7..d92d2a61a 100644 --- a/backend/src/server/routes/v1/identity-ua.ts +++ b/backend/src/server/routes/v1/identity-ua.ts @@ -39,11 +39,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { }, handler: async (req) => { const { identityUa, accessToken, identityAccessToken, validClientSecretInfo } = - await server.services.identityUa.login( - req.body.clientId, - req.body.clientSecret, - req.realIp - ); + await server.services.identityUa.login(req.body.clientId, req.body.clientSecret, req.realIp); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, @@ -128,10 +124,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { identityId: identityUniversalAuth.identityId, accessTokenTTL: identityUniversalAuth.accessTokenTTL, accessTokenMaxTTL: identityUniversalAuth.accessTokenMaxTTL, - accessTokenTrustedIps: - identityUniversalAuth.accessTokenTrustedIps as TIdentityTrustedIp[], - clientSecretTrustedIps: - identityUniversalAuth.clientSecretTrustedIps as TIdentityTrustedIp[], + accessTokenTrustedIps: identityUniversalAuth.accessTokenTrustedIps as TIdentityTrustedIp[], + clientSecretTrustedIps: identityUniversalAuth.clientSecretTrustedIps as TIdentityTrustedIp[], accessTokenNumUsesLimit: identityUniversalAuth.accessTokenNumUsesLimit } } @@ -197,10 +191,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { identityId: identityUniversalAuth.identityId, accessTokenTTL: identityUniversalAuth.accessTokenTTL, accessTokenMaxTTL: identityUniversalAuth.accessTokenMaxTTL, - accessTokenTrustedIps: - identityUniversalAuth.accessTokenTrustedIps as TIdentityTrustedIp[], - clientSecretTrustedIps: - identityUniversalAuth.clientSecretTrustedIps as TIdentityTrustedIp[], + accessTokenTrustedIps: identityUniversalAuth.accessTokenTrustedIps as TIdentityTrustedIp[], + clientSecretTrustedIps: identityUniversalAuth.clientSecretTrustedIps as TIdentityTrustedIp[], accessTokenNumUsesLimit: identityUniversalAuth.accessTokenNumUsesLimit } } @@ -267,13 +259,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const { clientSecret, clientSecretData, orgId } = - await server.services.identityUa.createUaClientSecret({ - actor: req.permission.type, - actorId: req.permission.id, - identityId: req.params.identityId, - ...req.body - }); + const { clientSecret, clientSecretData, orgId } = await server.services.identityUa.createUaClientSecret({ + actor: req.permission.type, + actorId: req.permission.id, + identityId: req.params.identityId, + ...req.body + }); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, @@ -306,12 +297,11 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const { clientSecrets: clientSecretData, orgId } = - await server.services.identityUa.getUaClientSecrets({ - actor: req.permission.type, - actorId: req.permission.id, - identityId: req.params.identityId - }); + const { clientSecrets: clientSecretData, orgId } = await server.services.identityUa.getUaClientSecrets({ + actor: req.permission.type, + actorId: req.permission.id, + identityId: req.params.identityId + }); await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index db73bf293..a7df57a54 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -57,6 +57,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { projectId: integrationAuth.projectId, event: { type: EventType.CREATE_INTEGRATION, + // eslint-disable-next-line metadata: shake({ integrationId: integration.id.toString(), integration: integration.integration, @@ -71,6 +72,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { targetServiceId: integration.targetServiceId, path: integration.path, region: integration.region + // eslint-disable-next-line }) as any } }); @@ -138,6 +140,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { projectId: integration.projectId, event: { type: EventType.DELETE_INTEGRATION, + // eslint-disable-next-line metadata: shake({ integrationId: integration.id, integration: integration.integration, @@ -152,6 +155,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { targetServiceId: integration.targetServiceId, path: integration.path, region: integration.region + // eslint-disable-next-line }) as any } }); diff --git a/backend/src/server/routes/v1/organization-router.ts b/backend/src/server/routes/v1/organization-router.ts index f5fd06c87..1d74e8b1a 100644 --- a/backend/src/server/routes/v1/organization-router.ts +++ b/backend/src/server/routes/v1/organization-router.ts @@ -1,11 +1,6 @@ import { z } from "zod"; -import { - IncidentContactsSchema, - OrganizationsSchema, - OrgMembershipsSchema, - UsersSchema -} from "@app/db/schemas"; +import { IncidentContactsSchema, OrganizationsSchema, OrgMembershipsSchema, UsersSchema } from "@app/db/schemas"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -42,10 +37,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const organization = await server.services.org.findOrganizationById( - req.permission.id, - req.params.organizationId - ); + const organization = await server.services.org.findOrganizationById(req.permission.id, req.params.organizationId); return { organization }; } }); @@ -76,10 +68,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const users = await server.services.org.findAllOrgMembers( - req.permission.id, - req.params.organizationId - ); + const users = await server.services.org.findAllOrgMembers(req.permission.id, req.params.organizationId); return { users }; } }); diff --git a/backend/src/server/routes/v1/password-router.ts b/backend/src/server/routes/v1/password-router.ts index 8ec7697f7..d5c5054df 100644 --- a/backend/src/server/routes/v1/password-router.ts +++ b/backend/src/server/routes/v1/password-router.ts @@ -12,7 +12,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "POST", url: "/srp1", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, schema: { body: z.object({ @@ -39,7 +39,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "POST", url: "/change-password", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, schema: { body: z.object({ @@ -64,7 +64,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { const appCfg = getConfig(); await server.services.password.changePassword({ ...req.body, userId: req.permission.id }); - res.cookie("jid", appCfg.COOKIE_SECRET_SIGN_KEY, { + void res.cookie("jid", appCfg.COOKIE_SECRET_SIGN_KEY, { httpOnly: true, path: "/", sameSite: "strict", @@ -78,7 +78,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "POST", url: "/email/password-reset", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, schema: { body: z.object({ @@ -103,7 +103,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "POST", url: "/email/password-reset-verify", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, schema: { body: z.object({ @@ -119,10 +119,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const { token, user } = await server.services.password.verifyPasswordResetEmail( - req.body.email, - req.body.code - ); + const { token, user } = await server.services.password.verifyPasswordResetEmail(req.body.email, req.body.code); return { message: "Successfully verified email", @@ -136,7 +133,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "POST", url: "/backup-private-key", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, onRequest: verifyAuth([AuthMode.JWT]), schema: { @@ -156,10 +153,10 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const token = validateSignUpAuthorization(req.headers.authorization as string, "",false)! + const token = validateSignUpAuthorization(req.headers.authorization as string, "", false)!; const backupPrivateKey = await server.services.password.createBackupPrivateKey({ ...req.body, - userId: token.userId, + userId: token.userId }); if (!backupPrivateKey) throw new Error("Failed to create backup key"); @@ -171,7 +168,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { method: "GET", url: "/backup-private-key", config: { - rateLimit:passwordRateLimit + rateLimit: passwordRateLimit }, schema: { response: { @@ -182,10 +179,8 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const token = validateSignUpAuthorization(req.headers.authorization as string, "",false)! - const backupPrivateKey = await server.services.password.getBackupPrivateKeyOfUser( - token.userId - ); + const token = validateSignUpAuthorization(req.headers.authorization as string, "", false)!; + const backupPrivateKey = await server.services.password.getBackupPrivateKeyOfUser(token.userId); if (!backupPrivateKey) throw new Error("Failed to find backup key"); return { message: "Successfully fetched backup private key", backupPrivateKey }; @@ -213,10 +208,10 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const token = validateSignUpAuthorization(req.headers.authorization as string, "",false)! + const token = validateSignUpAuthorization(req.headers.authorization as string, "", false)!; await server.services.password.resetPasswordByBackupKey({ ...req.body, - userId: token.userId, + userId: token.userId }); return { message: "Successfully updated backup private key" }; diff --git a/backend/src/server/routes/v1/project-membership-router.ts b/backend/src/server/routes/v1/project-membership-router.ts index bacfcb1bb..7d28f470a 100644 --- a/backend/src/server/routes/v1/project-membership-router.ts +++ b/backend/src/server/routes/v1/project-membership-router.ts @@ -1,11 +1,6 @@ import { z } from "zod"; -import { - OrgMembershipsSchema, - ProjectMembershipsSchema, - UserEncryptionKeysSchema, - UsersSchema -} from "@app/db/schemas"; +import { OrgMembershipsSchema, ProjectMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; diff --git a/backend/src/server/routes/v1/project-router.ts b/backend/src/server/routes/v1/project-router.ts index 5e8ee8b46..b7574e35c 100644 --- a/backend/src/server/routes/v1/project-router.ts +++ b/backend/src/server/routes/v1/project-router.ts @@ -255,7 +255,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { response: { 200: z.object({ invitee: UsersSchema, - latestKey: ProjectKeysSchema + latestKey: ProjectKeysSchema.optional() }) } }, diff --git a/backend/src/server/routes/v1/secret-folder-router.ts b/backend/src/server/routes/v1/secret-folder-router.ts index c71767868..4a152f52e 100644 --- a/backend/src/server/routes/v1/secret-folder-router.ts +++ b/backend/src/server/routes/v1/secret-folder-router.ts @@ -25,12 +25,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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({ @@ -79,12 +74,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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({ @@ -133,12 +123,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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({ @@ -183,12 +168,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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({ diff --git a/backend/src/server/routes/v1/secret-import-router.ts b/backend/src/server/routes/v1/secret-import-router.ts index f27f9e0c2..80f980a90 100644 --- a/backend/src/server/routes/v1/secret-import-router.ts +++ b/backend/src/server/routes/v1/secret-import-router.ts @@ -31,12 +31,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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, @@ -97,12 +92,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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, @@ -155,12 +145,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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, @@ -211,12 +196,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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, @@ -268,12 +248,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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, diff --git a/backend/src/server/routes/v1/sso-router.ts b/backend/src/server/routes/v1/sso-router.ts index f3d5288b0..9c7c85b04 100644 --- a/backend/src/server/routes/v1/sso-router.ts +++ b/backend/src/server/routes/v1/sso-router.ts @@ -1,3 +1,11 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +// All the any rules are disabled because passport typesense with fastify is really poor + import { Authenticator } from "@fastify/passport"; import fastifySession from "@fastify/session"; import { Strategy as GitHubStrategy } from "passport-github"; @@ -19,9 +27,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { await server.register(passport.initialize()); await server.register(passport.secureSession()); // passport oauth strategy for Google - const isGoogleOauthActive = Boolean( - appCfg.CLIENT_ID_GOOGLE_LOGIN && appCfg.CLIENT_SECRET_GOOGLE_LOGIN - ); + const isGoogleOauthActive = Boolean(appCfg.CLIENT_ID_GOOGLE_LOGIN && appCfg.CLIENT_SECRET_GOOGLE_LOGIN); if (isGoogleOauthActive) { passport.use( new GoogleStrategy( @@ -32,6 +38,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { callbackURL: `${appCfg.SITE_URL}/api/v1/sso/google`, scope: ["profile", " email"] }, + // eslint-disable-next-line async (req, _accessToken, _refreshToken, profile, cb) => { try { const email = profile?.emails?.[0]?.value; @@ -61,9 +68,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { } // Passport strategy for Github - const isGithubOauthActive = Boolean( - appCfg.CLIENT_SECRET_GITHUB_LOGIN && appCfg.CLIENT_ID_GITHUB_LOGIN - ); + const isGithubOauthActive = Boolean(appCfg.CLIENT_SECRET_GITHUB_LOGIN && appCfg.CLIENT_ID_GITHUB_LOGIN); if (isGithubOauthActive) { passport.use( new GitHubStrategy( @@ -74,6 +79,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { callbackURL: `${appCfg.SITE_URL}/api/v1/sso/github`, scope: ["user:email"] }, + // eslint-disable-next-line async (req, accessToken, _refreshToken, profile, cb) => { try { const ghEmails = await fetchGithubEmails(accessToken); @@ -99,9 +105,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { // passport strategy for gitlab const isGitlabOauthActive = Boolean( - appCfg.CLIENT_ID_GITLAB_LOGIN && - appCfg.CLIENT_SECRET_GITLAB_LOGIN && - appCfg.CLIENT_GITLAB_LOGIN_URL + appCfg.CLIENT_ID_GITLAB_LOGIN && appCfg.CLIENT_SECRET_GITLAB_LOGIN && appCfg.CLIENT_GITLAB_LOGIN_URL ); if (isGitlabOauthActive) { passport.use( @@ -152,6 +156,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { state: req.query.callback_port, authInfo: false // this is due to zod type difference + // eslint-disable-next-line @typescript-eslint/no-explicit-any }) as any )(req, res), handler: () => {} @@ -165,19 +170,15 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { failureRedirect: "/login/provider/error", authInfo: false // this is due to zod type difference - }) as any, + }) as never, handler: (req, res) => { if (req.passportUser.isUserCompleted) { return res.redirect( - `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } return res.redirect( - `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } }); @@ -214,15 +215,11 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { handler: (req, res) => { if (req.passportUser.isUserCompleted) { return res.redirect( - `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } return res.redirect( - `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } }); @@ -242,6 +239,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { state: req.query.callback_port, authInfo: false // this is due to zod type difference + // eslint-disable-next-line @typescript-eslint/no-explicit-any }) as any )(req, res), handler: () => {} @@ -255,19 +253,16 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { failureRedirect: "/login/provider/error", authInfo: false // this is due to zod type difference + // eslint-disable-next-line @typescript-eslint/no-explicit-any }) as any, handler: (req, res) => { if (req.passportUser.isUserCompleted) { return res.redirect( - `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } return res.redirect( - `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent( - req.passportUser.providerAuthToken - )}` + `${appCfg.SITE_URL}/signup/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}` ); } }); diff --git a/backend/src/server/routes/v1/user-action-router.ts b/backend/src/server/routes/v1/user-action-router.ts index 5ce66eb79..c730cdb91 100644 --- a/backend/src/server/routes/v1/user-action-router.ts +++ b/backend/src/server/routes/v1/user-action-router.ts @@ -21,10 +21,7 @@ export const registerUserActionRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const userAction = await server.services.user.createUserAction( - req.permission.id, - req.body.action - ); + const userAction = await server.services.user.createUserAction(req.permission.id, req.body.action); return { userAction, message: "Successfully recorded user action" }; } }); @@ -44,10 +41,7 @@ export const registerUserActionRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const userAction = await server.services.user.getUserAction( - req.permission.id, - req.query.action - ); + const userAction = await server.services.user.getUserAction(req.permission.id, req.query.action); return { userAction }; } }); diff --git a/backend/src/server/routes/v1/webhook-router.ts b/backend/src/server/routes/v1/webhook-router.ts index 558192f68..2b3e66398 100644 --- a/backend/src/server/routes/v1/webhook-router.ts +++ b/backend/src/server/routes/v1/webhook-router.ts @@ -183,7 +183,11 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => { querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim().optional(), - secretPath: z.string().trim().optional().transform((val)=> val?removeTrailingSlash(val):val) + secretPath: z + .string() + .trim() + .optional() + .transform((val) => (val ? removeTrailingSlash(val) : val)) }), response: { 200: z.object({ diff --git a/backend/src/server/routes/v2/index.ts b/backend/src/server/routes/v2/index.ts index a73f9de52..1f423c084 100644 --- a/backend/src/server/routes/v2/index.ts +++ b/backend/src/server/routes/v2/index.ts @@ -19,8 +19,8 @@ export const registerV2Routes = async (server: FastifyZodProvider) => { ); await server.register( async (projectServer) => { - projectServer.register(registerProjectRouter); - projectServer.register(registerIdentityProjectRouter); + await projectServer.register(registerProjectRouter); + await projectServer.register(registerIdentityProjectRouter); }, { prefix: "/workspace" } ); diff --git a/backend/src/server/routes/v2/mfa-router.ts b/backend/src/server/routes/v2/mfa-router.ts index 02006a470..cbe7f1cbf 100644 --- a/backend/src/server/routes/v2/mfa-router.ts +++ b/backend/src/server/routes/v2/mfa-router.ts @@ -1,8 +1,8 @@ -import jwt, { JwtPayload } from "jsonwebtoken"; +import jwt from "jsonwebtoken"; import { z } from "zod"; import { getConfig } from "@app/lib/config/env"; -import { AuthTokenType } from "@app/services/auth/auth-type"; +import { AuthModeMfaJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type"; export const registerMfaRouter = async (server: FastifyZodProvider) => { const cfg = getConfig(); @@ -12,18 +12,17 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => { const authorizationHeader = req.headers.authorization; if (!authorizationHeader || !authorizationHeader.startsWith("Bearer ")) { - res.status(401).send({ error: "Missing bearer token" }); + void res.status(401).send({ error: "Missing bearer token" }); return res; } const token = authorizationHeader.split(" ")[1]; if (!token) { - res.status(401).send({ error: "Missing bearer token" }); + void res.status(401).send({ error: "Missing bearer token" }); return res; } - const decodedToken = jwt.verify(token, cfg.AUTH_SECRET) as JwtPayload; - if (decodedToken.authTokenType !== AuthTokenType.MFA_TOKEN) - throw new Error("Unauthorized access"); + const decodedToken = jwt.verify(token, cfg.AUTH_SECRET) as AuthModeMfaJwtTokenPayload; + if (decodedToken.authTokenType !== AuthTokenType.MFA_TOKEN) throw new Error("Unauthorized access"); const user = await server.store.user.findById(decodedToken.userId); if (!user) throw new Error("User not found"); @@ -79,7 +78,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => { mfaToken: req.body.mfaToken }); - res.setCookie("jid", token.refresh, { + void res.setCookie("jid", token.refresh, { httpOnly: true, path: "/", sameSite: "strict", diff --git a/backend/src/server/routes/v2/organization-router.ts b/backend/src/server/routes/v2/organization-router.ts index ed4a894c5..df94f65c7 100644 --- a/backend/src/server/routes/v2/organization-router.ts +++ b/backend/src/server/routes/v2/organization-router.ts @@ -1,11 +1,6 @@ import { z } from "zod"; -import { - OrganizationsSchema, - OrgMembershipsSchema, - UserEncryptionKeysSchema, - UsersSchema -} from "@app/db/schemas"; +import { OrganizationsSchema, OrgMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { ActorType, AuthMode } from "@app/services/auth/auth-type"; @@ -38,14 +33,48 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { handler: async (req) => { if (req.auth.actor !== ActorType.USER) return; - const users = await server.services.org.findAllOrgMembers( - req.permission.id, - req.params.organizationId - ); + const users = await server.services.org.findAllOrgMembers(req.permission.id, req.params.organizationId); return { users }; } }); + server.route({ + method: "GET", + url: "/:organizationId/workspaces", + schema: { + params: z.object({ + organizationId: z.string().trim() + }), + response: { + 200: z.object({ + workspaces: z + .object({ + id: z.string(), + name: z.string(), + organization: z.string(), + environments: z + .object({ + name: z.string(), + slug: z.string() + }) + .array() + }) + .array() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const workspaces = await server.services.org.findAllWorkspaces({ + actor: req.permission.type, + actorId: req.permission.id, + orgId: req.params.organizationId + }); + + return { workspaces }; + } + }); + server.route({ method: "PATCH", url: "/:organizationId/memberships/:membershipId", diff --git a/backend/src/server/routes/v2/project-router.ts b/backend/src/server/routes/v2/project-router.ts index 86f25d89d..e90a36060 100644 --- a/backend/src/server/routes/v2/project-router.ts +++ b/backend/src/server/routes/v2/project-router.ts @@ -37,7 +37,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { event: { type: EventType.GET_WORKSPACE_KEY, metadata: { - keyId: key.id + keyId: key?.id as string } } }); diff --git a/backend/src/server/routes/v2/user-router.ts b/backend/src/server/routes/v2/user-router.ts index 071a70539..8061aa0e5 100644 --- a/backend/src/server/routes/v2/user-router.ts +++ b/backend/src/server/routes/v2/user-router.ts @@ -1,11 +1,6 @@ import { z } from "zod"; -import { - AuthTokenSessionsSchema, - OrganizationsSchema, - UserEncryptionKeysSchema, - UsersSchema -} from "@app/db/schemas"; +import { AuthTokenSessionsSchema, OrganizationsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas"; import { ApiKeysSchema } from "@app/db/schemas/api-keys"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMethod, AuthMode } from "@app/services/auth/auth-type"; @@ -47,11 +42,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { }, preHandler: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), handler: async (req) => { - const user = await server.services.user.updateUserName( - req.permission.id, - req.body.firstName, - req.body.lastName - ); + const user = await server.services.user.updateUserName(req.permission.id, req.body.firstName, req.body.lastName); return { user }; } }); @@ -71,10 +62,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { }, preHandler: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), handler: async (req) => { - const user = await server.services.user.updateAuthMethods( - req.permission.id, - req.body.authMethods - ); + const user = await server.services.user.updateAuthMethods(req.permission.id, req.body.authMethods); return { user }; } }); @@ -128,11 +116,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const apiKeys = await server.services.apiKey.createApiKey( - req.permission.id, - req.body.name, - req.body.expiresIn - ); + const apiKeys = await server.services.apiKey.createApiKey(req.permission.id, req.body.name, req.body.expiresIn); return apiKeys; } }); @@ -152,10 +136,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { }, onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { - const apiKeyData = await server.services.apiKey.deleteApiKey( - req.permission.id, - req.params.apiKeyDataId - ); + const apiKeyData = await server.services.apiKey.deleteApiKey(req.permission.id, req.params.apiKeyDataId); return { apiKeyData }; } }); diff --git a/backend/src/server/routes/v3/login-router.ts b/backend/src/server/routes/v3/login-router.ts index 1c513c183..0cda8e5ff 100644 --- a/backend/src/server/routes/v3/login-router.ts +++ b/backend/src/server/routes/v3/login-router.ts @@ -81,7 +81,7 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => { return { mfaEnabled: true, token: data.token } as const; // for discriminated union } - res.setCookie("jid", data.token.refresh, { + void res.setCookie("jid", data.token.refresh, { httpOnly: true, path: "/", sameSite: "strict", diff --git a/backend/src/server/routes/v3/secret-router.ts b/backend/src/server/routes/v3/secret-router.ts index db14eb0f6..8df8533b8 100644 --- a/backend/src/server/routes/v3/secret-router.ts +++ b/backend/src/server/routes/v3/secret-router.ts @@ -61,12 +61,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { // just for delivery hero usecase let { secretPath, environment, workspaceId } = req.query; @@ -80,8 +75,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { } } - if (!workspaceId || !environment) - throw new BadRequestError({ message: "Missing workspace id or environment" }); + if (!workspaceId || !environment) throw new BadRequestError({ message: "Missing workspace id or environment" }); const { secrets, imports } = await server.services.secret.getSecretsRaw({ actorId: req.permission.id, @@ -144,12 +138,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { let { secretPath, environment, workspaceId } = req.query; if (req.auth.actor === ActorType.SERVICE) { @@ -162,8 +151,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { } } - if (!workspaceId || !environment) - throw new BadRequestError({ message: "Missing workspace id or environment" }); + if (!workspaceId || !environment) throw new BadRequestError({ message: "Missing workspace id or environment" }); const secret = await server.services.secret.getSecretByNameRaw({ actorId: req.permission.id, @@ -218,9 +206,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: z.string().trim(), environment: z.string().trim(), secretPath: z.string().trim().default("/").transform(removeTrailingSlash), - secretValue: z - .string() - .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), + secretValue: z.string().transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), secretComment: z.string().trim().optional().default(""), skipMultilineEncoding: z.boolean().optional(), type: z.nativeEnum(SecretType).default(SecretType.Shared) @@ -231,12 +217,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secret = await server.services.secret.createSecretRaw({ actorId: req.permission.id, @@ -293,9 +274,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretValue: z - .string() - .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), + secretValue: z.string().transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), secretPath: z.string().trim().default("/").transform(removeTrailingSlash), skipMultilineEncoding: z.boolean().optional(), type: z.nativeEnum(SecretType).default(SecretType.Shared) @@ -306,12 +285,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secret = await server.services.secret.updateSecretRaw({ actorId: req.permission.id, @@ -375,12 +349,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secret = await server.services.secret.deleteSecretRaw({ actorId: req.permission.id, @@ -474,12 +443,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { secrets, imports } = await server.services.secret.getSecrets({ actorId: req.permission.id, @@ -549,12 +513,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const secret = await server.services.secret.getSecretByName({ actorId: req.permission.id, @@ -634,18 +593,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) ) }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { workspaceId: projectId, @@ -673,32 +625,31 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Create]: [ - { - secretName: req.params.secretName, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretCommentCiphertext, - skipMultilineEncoding, - secretKeyTag, - secretKeyCiphertext, - secretKeyIV - } - ] - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Create]: [ + { + secretName: req.params.secretName, + secretValueCiphertext, + secretValueIV, + secretValueTag, + secretCommentIV, + secretCommentTag, + secretCommentCiphertext, + skipMultilineEncoding, + secretKeyTag, + secretKeyCiphertext, + secretKeyIV + } + ] + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, @@ -810,18 +761,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) ) }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { const { secretValueCiphertext, @@ -854,34 +798,33 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Update]: [ - { - secretName: req.params.secretName, - newSecretName, - secretValueCiphertext, - secretValueIV, - secretValueTag, - secretCommentIV, - secretCommentTag, - secretCommentCiphertext, - skipMultilineEncoding, - secretKeyTag, - secretKeyCiphertext, - secretKeyIV, - tagIds: tags - } - ] - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Update]: [ + { + secretName: req.params.secretName, + newSecretName, + secretValueCiphertext, + secretValueIV, + secretValueTag, + secretCommentIV, + secretCommentTag, + secretCommentCiphertext, + skipMultilineEncoding, + secretKeyTag, + secretKeyCiphertext, + secretKeyIV, + tagIds: tags + } + ] + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, @@ -980,18 +923,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }) ) }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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) { @@ -1003,22 +939,21 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Delete]: [ - { - secretName: req.params.secretName - } - ] - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Delete]: [ + { + secretName: req.params.secretName + } + ] + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, @@ -1110,18 +1045,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { z.object({ secrets: SecretsSchema.omit({ secretBlindIndex: true }).array() }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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) { @@ -1133,18 +1061,17 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Create]: inputSecrets.filter(({ type }) => type === "shared") - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Create]: inputSecrets.filter(({ type }) => type === "shared") + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, @@ -1236,18 +1163,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { z.object({ secrets: SecretsSchema.omit({ secretBlindIndex: true }).array() }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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) { @@ -1259,18 +1179,17 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Update]: inputSecrets.filter(({ type }) => type === "shared") - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Update]: inputSecrets.filter(({ type }) => type === "shared") + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, @@ -1350,18 +1269,11 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { z.object({ secrets: SecretsSchema.omit({ secretBlindIndex: true }).array() }), - z - .object({ approval: SecretApprovalRequestsSchema }) - .describe("When secret protection policy is enabled") + z.object({ approval: SecretApprovalRequestsSchema }).describe("When secret protection policy is enabled") ]) } }, - onRequest: verifyAuth([ - AuthMode.JWT, - AuthMode.API_KEY, - AuthMode.SERVICE_TOKEN, - AuthMode.IDENTITY_ACCESS_TOKEN - ]), + 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) { @@ -1373,18 +1285,17 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { projectId }); if (policy) { - const approval = - await server.services.secretApprovalRequest.generateSecretApprovalRequest({ - actorId: req.permission.id, - actor: req.permission.type, - secretPath, - environment, - projectId, - policy, - data: { - [CommitType.Delete]: inputSecrets.filter(({ type }) => type === "shared") - } - }); + const approval = await server.services.secretApprovalRequest.generateSecretApprovalRequest({ + actorId: req.permission.id, + actor: req.permission.type, + secretPath, + environment, + projectId, + policy, + data: { + [CommitType.Delete]: inputSecrets.filter(({ type }) => type === "shared") + } + }); await server.services.auditLog.createAuditLog({ projectId: req.body.workspaceId, ...req.auditLogInfo, diff --git a/backend/src/server/routes/v3/signup-router.ts b/backend/src/server/routes/v3/signup-router.ts index fe64a4ec2..2a2f50f43 100644 --- a/backend/src/server/routes/v3/signup-router.ts +++ b/backend/src/server/routes/v3/signup-router.ts @@ -48,10 +48,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - const { token, user } = await server.services.signup.verifyEmailSignup( - req.body.email, - req.body.code - ); + const { token, user } = await server.services.signup.verifyEmailSignup(req.body.email, req.body.code); return { message: "Successfuly verified email", token, user }; } }); @@ -93,21 +90,16 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { if (!userAgent) throw new Error("user agent header is required"); const appCfg = getConfig(); - const { user, accessToken, refreshToken } = - await server.services.signup.completeEmailAccountSignup({ - ...req.body, - ip: req.realIp, - userAgent, - authorization: req.headers.authorization as string - }); + const { user, accessToken, refreshToken } = await server.services.signup.completeEmailAccountSignup({ + ...req.body, + ip: req.realIp, + userAgent, + authorization: req.headers.authorization as string + }); - server.services.telemetry.sendLoopsEvent( - user.email, - user.firstName || "", - user.lastName || "" - ); + void server.services.telemetry.sendLoopsEvent(user.email, user.firstName || "", user.lastName || ""); - server.services.telemetry.sendPostHogEvents({ + void server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.UserSignedUp, distinctId: user.email, properties: { @@ -116,7 +108,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { } }); - res.setCookie("jid", refreshToken, { + void res.setCookie("jid", refreshToken, { httpOnly: true, path: "/", sameSite: "strict", @@ -161,14 +153,13 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { if (!userAgent) throw new Error("user agent header is required"); const appCfg = getConfig(); - const { user, accessToken, refreshToken } = - await server.services.signup.completeAccountInvite({ - ...req.body, - ip: req.realIp, - userAgent - }); + const { user, accessToken, refreshToken } = await server.services.signup.completeAccountInvite({ + ...req.body, + ip: req.realIp, + userAgent + }); - res.setCookie("jid", refreshToken, { + void res.setCookie("jid", refreshToken, { httpOnly: true, path: "/", sameSite: "strict", diff --git a/backend/src/services/api-key/api-key-service.ts b/backend/src/services/api-key/api-key-service.ts index 23057e1a4..39ccbecce 100644 --- a/backend/src/services/api-key/api-key-service.ts +++ b/backend/src/services/api-key/api-key-service.ts @@ -45,8 +45,7 @@ export const apiKeyServiceFactory = ({ apiKeyDAL, userDAL }: TApiKeyServiceFacto const deleteApiKey = async (userId: string, apiKeyId: string) => { const [apiKeyData] = await apiKeyDAL.delete({ id: apiKeyId, userId }); - if (!apiKeyData) - throw new BadRequestError({ message: "Failed to find api key", name: "delete api key" }); + if (!apiKeyData) throw new BadRequestError({ message: "Failed to find api key", name: "delete api key" }); return formatApiKey(apiKeyData); }; diff --git a/backend/src/services/auth-token/auth-token-dal.ts b/backend/src/services/auth-token/auth-token-dal.ts index edd8ef74c..075ae7384 100644 --- a/backend/src/services/auth-token/auth-token-dal.ts +++ b/backend/src/services/auth-token/auth-token-dal.ts @@ -7,16 +7,12 @@ import { ormify } from "@app/lib/knex"; import { TDeleteTokenForUserDALDTO } from "./auth-token-types"; -export type TTokenDALConfig = {}; - export type TTokenDALFactory = ReturnType; export const tokenDALFactory = (db: TDbClient) => { const authOrm = ormify(db, TableName.AuthTokens); - const findOneTokenSession = async ( - filter: Partial - ): Promise => { + const findOneTokenSession = async (filter: Partial): Promise => { try { const doc = await db(TableName.AuthTokenSession).where(filter).first(); return doc; @@ -31,20 +27,14 @@ export const tokenDALFactory = (db: TDbClient) => { orgId }: TDeleteTokenForUserDALDTO): Promise => { try { - const doc = await db(TableName.AuthTokens) - .where({ userId, type, orgId }) - .delete() - .returning("*"); + const doc = await db(TableName.AuthTokens).where({ userId, type, orgId }).delete().returning("*"); return doc; } catch (error) { throw new DatabaseError({ error, name: "DeleteTokenForUser" }); } }; - const decrementTriesField = async ({ - userId, - type - }: TDeleteTokenForUserDALDTO): Promise => { + const decrementTriesField = async ({ userId, type }: TDeleteTokenForUserDALDTO): Promise => { try { await db(TableName.AuthTokens).where({ userId, type }).decrement("triesLeft", 1); } catch (error) { @@ -101,10 +91,7 @@ export const tokenDALFactory = (db: TDbClient) => { const deleteTokenSession = async (filter: Partial, tx?: Knex) => { try { - const sessions = await (tx || db)(TableName.AuthTokenSession) - .where(filter) - .del() - .returning("*"); + const sessions = await (tx || db)(TableName.AuthTokenSession).where(filter).del().returning("*"); return sessions; } catch (error) { throw new DatabaseError({ name: "Delete token session", error }); diff --git a/backend/src/services/auth-token/auth-token-service.ts b/backend/src/services/auth-token/auth-token-service.ts index d19691209..7fae75c30 100644 --- a/backend/src/services/auth-token/auth-token-service.ts +++ b/backend/src/services/auth-token/auth-token-service.ts @@ -9,12 +9,7 @@ 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, - TIssueAuthTokenDTO, - TokenType, - TValidateTokenForUserDTO -} from "./auth-token-types"; +import { TCreateTokenForUserDTO, TIssueAuthTokenDTO, TokenType, TValidateTokenForUserDTO } from "./auth-token-types"; type TAuthTokenServiceFactoryDep = { tokenDAL: TTokenDALFactory; @@ -125,14 +120,10 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL }: TAuthTokenServiceFact return session; }; - const clearTokenSessionById = async ( - userId: string, - sessionId: string - ): Promise => + const clearTokenSessionById = async (userId: string, sessionId: string): Promise => tokenDAL.incrementTokenSessionVersion(userId, sessionId); - const getUserTokenSessionById = async (id: string, userId: string) => - tokenDAL.findOneTokenSession({ id, userId }); + const getUserTokenSessionById = async (id: string, userId: string) => tokenDAL.findOneTokenSession({ id, userId }); const getTokenSessionByUser = async (userId: string) => tokenDAL.findTokenSessions({ userId }); @@ -145,8 +136,7 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL }: TAuthTokenServiceFact userId: token.userId }); if (!session) throw new UnauthorizedError({ name: "Session not found" }); - if (token.accessVersion !== session.accessVersion) - throw new UnauthorizedError({ name: "Stale session" }); + 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" }); diff --git a/backend/src/services/auth/auth-fns.ts b/backend/src/services/auth/auth-fns.ts index 91f34345c..3e65f7e05 100644 --- a/backend/src/services/auth/auth-fns.ts +++ b/backend/src/services/auth/auth-fns.ts @@ -3,19 +3,12 @@ import jwt from "jsonwebtoken"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; -import { - AuthModeProviderJwtTokenPayload, - AuthModeProviderSignUpTokenPayload, - AuthTokenType -} from "./auth-type"; +import { AuthModeProviderJwtTokenPayload, AuthModeProviderSignUpTokenPayload, AuthTokenType } from "./auth-type"; export const validateProviderAuthToken = (providerToken: string, email: string) => { if (!providerToken) throw new UnauthorizedError(); const appCfg = getConfig(); - const decodedToken = jwt.verify( - providerToken, - appCfg.AUTH_SECRET - ) as AuthModeProviderJwtTokenPayload; + const decodedToken = jwt.verify(providerToken, appCfg.AUTH_SECRET) as AuthModeProviderJwtTokenPayload; if (decodedToken.authTokenType !== AuthTokenType.PROVIDER_TOKEN) throw new UnauthorizedError(); if (decodedToken.email !== email) throw new Error("Invalid auth credentials"); @@ -23,10 +16,7 @@ export const validateProviderAuthToken = (providerToken: string, email: string) export const validateSignUpAuthorization = (token: string, userId: string, validate = true) => { const appCfg = getConfig(); - const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>token?.split(" ", 2) ?? [ - null, - null - ]; + const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>token?.split(" ", 2) ?? [null, null]; if (AUTH_TOKEN_TYPE === null) { throw new BadRequestError({ message: "Missing Authorization Header in the request header." }); } @@ -41,10 +31,7 @@ export const validateSignUpAuthorization = (token: string, userId: string, valid }); } - const decodedToken = jwt.verify( - AUTH_TOKEN_VALUE, - appCfg.AUTH_SECRET - ) as AuthModeProviderSignUpTokenPayload; + const decodedToken = jwt.verify(AUTH_TOKEN_VALUE, appCfg.AUTH_SECRET) as AuthModeProviderSignUpTokenPayload; if (!validate) return decodedToken; if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) throw new UnauthorizedError(); diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index e10fe6830..d63baeaca 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -25,11 +25,7 @@ type TAuthLoginServiceFactoryDep = { }; export type TAuthLoginFactory = ReturnType; -export const authLoginServiceFactory = ({ - userDAL, - tokenService, - smtpService -}: TAuthLoginServiceFactoryDep) => { +export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }: TAuthLoginServiceFactoryDep) => { /* * Private * Not exported. This is to update user device list @@ -37,9 +33,7 @@ export const authLoginServiceFactory = ({ */ const updateUserDeviceSession = async (user: TUsers, ip: string, userAgent: string) => { const devices = await UserDeviceSchema.parseAsync(user.devices || []); - const isDeviceSeen = devices.some( - (device) => device.ip === ip && device.userAgent === userAgent - ); + const isDeviceSeen = devices.some((device) => device.ip === ip && device.userAgent === userAgent); if (!isDeviceSeen) { const newDeviceList = devices.concat([{ ip, userAgent }]); @@ -159,8 +153,7 @@ export const authLoginServiceFactory = ({ validateProviderAuthToken(providerAuthToken as string, email); } - if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) - throw new Error("Failed to authenticate. Try again?"); + if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?"); const isValidClientProof = await srpCheckClientProof( userEnc.salt, userEnc.verifier, @@ -176,11 +169,9 @@ export const authLoginServiceFactory = ({ }); // send multi factor auth token if they it enabled if (userEnc.isMfaEnabled) { - const mfaToken = jwt.sign( - { authTokenType: AuthTokenType.MFA_TOKEN, userId: userEnc.userId }, - cfg.AUTH_SECRET, - { expiresIn: cfg.JWT_MFA_LIFETIME } - ); + const mfaToken = jwt.sign({ authTokenType: AuthTokenType.MFA_TOKEN, userId: userEnc.userId }, cfg.AUTH_SECRET, { + expiresIn: cfg.JWT_MFA_LIFETIME + }); await sendUserMfaCode(userEnc.userId, userEnc.email); return { isMfaEnabled: true, token: mfaToken } as const; @@ -230,8 +221,7 @@ export const authLoginServiceFactory = ({ let user = await userDAL.findUserByEmail(email); const appCfg = getConfig(); const isOauthSignUpDisabled = !isSignupAllowed && !user; - if (isOauthSignUpDisabled) - throw new BadRequestError({ message: "User signup disabled", name: "Oauth 2 login" }); + if (isOauthSignUpDisabled) throw new BadRequestError({ message: "User signup disabled", name: "Oauth 2 login" }); if (!user) { user = await userDAL.create({ email, firstName, lastName, authMethods: [authMethod] }); diff --git a/backend/src/services/auth/auth-password-service.ts b/backend/src/services/auth/auth-password-service.ts index 3ea656184..ff07d422f 100644 --- a/backend/src/services/auth/auth-password-service.ts +++ b/backend/src/services/auth/auth-password-service.ts @@ -9,11 +9,7 @@ import { TokenType } from "../auth-token/auth-token-types"; import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service"; import { TUserDALFactory } from "../user/user-dal"; import { TAuthDALFactory } from "./auth-dal"; -import { - TChangePasswordDTO, - TCreateBackupPrivateKeyDTO, - TResetPasswordViaBackupKeyDTO -} from "./auth-password-type"; +import { TChangePasswordDTO, TCreateBackupPrivateKeyDTO, TResetPasswordViaBackupKeyDTO } from "./auth-password-type"; import { AuthTokenType } from "./auth-type"; type TAuthPasswordServiceFactoryDep = { @@ -70,8 +66,7 @@ export const authPaswordServiceFactory = ({ serverPrivateKey: null, clientPublicKey: null }); - if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) - throw new Error("Failed to authenticate. Try again?"); + if (!userEnc.serverPrivateKey || !userEnc.clientPublicKey) throw new Error("Failed to authenticate. Try again?"); const isValidClientProof = await srpCheckClientProof( userEnc.salt, userEnc.verifier, @@ -200,8 +195,7 @@ export const authPaswordServiceFactory = ({ throw new Error("Failed to find user"); } - if (!userEnc.clientPublicKey || !userEnc.serverPrivateKey) - throw new Error("failed to create backup key"); + if (!userEnc.clientPublicKey || !userEnc.serverPrivateKey) throw new Error("failed to create backup key"); const isValidClientProff = await srpCheckClientProof( userEnc.salt, userEnc.verifier, diff --git a/backend/src/services/auth/auth-signup-service.ts b/backend/src/services/auth/auth-signup-service.ts index 17e45845f..6090a2129 100644 --- a/backend/src/services/auth/auth-signup-service.ts +++ b/backend/src/services/auth/auth-signup-service.ts @@ -148,9 +148,7 @@ export const authSignupServiceFactory = ({ }); const hasSamlEnabled = user?.authMethods?.some((authMethod) => - [AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes( - authMethod as AuthMethod - ) + [AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(authMethod as AuthMethod) ); if (!hasSamlEnabled) { @@ -162,9 +160,7 @@ export const authSignupServiceFactory = ({ { userId: user.id, status: OrgMembershipStatus.Accepted } ); const uniqueOrgId = [...new Set(updatedMembersips.map(({ orgId }) => orgId))]; - await Promise.allSettled( - uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId)) - ); + await Promise.allSettled(uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId))); const tokenSession = await tokenService.getUserTokenSession({ userAgent, @@ -259,9 +255,7 @@ export const authSignupServiceFactory = ({ tx ); const uniqueOrgId = [...new Set(updatedMembersips.map(({ orgId }) => orgId))]; - await Promise.allSettled( - uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId)) - ); + await Promise.allSettled(uniqueOrgId.map((orgId) => licenseService.updateSubscriptionOrgMemberCount(orgId))); return { info: us, key: userEncKey }; }); diff --git a/backend/src/services/auth/auth-type.ts b/backend/src/services/auth/auth-type.ts index 183c6170e..26417b0e8 100644 --- a/backend/src/services/auth/auth-type.ts +++ b/backend/src/services/auth/auth-type.ts @@ -41,6 +41,11 @@ export type AuthModeJwtTokenPayload = { accessVersion: number; }; +export type AuthModeMfaJwtTokenPayload = { + authTokenType: AuthTokenType.MFA_TOKEN; + userId: string; +}; + export type AuthModeRefreshJwtTokenPayload = { authTokenType: AuthTokenType.REFRESH_TOKEN; userId: string; diff --git a/backend/src/services/identity-access-token/identity-access-token-dal.ts b/backend/src/services/identity-access-token/identity-access-token-dal.ts index 5919136a0..42fb5bba5 100644 --- a/backend/src/services/identity-access-token/identity-access-token-dal.ts +++ b/backend/src/services/identity-access-token/identity-access-token-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TIdentityAccessTokens } from "@app/db/schemas"; +import { TableName, TIdentityAccessTokens } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify, selectAllTableCols } from "@app/lib/knex"; @@ -14,11 +14,7 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => { try { const doc = await (tx || db)(TableName.IdentityAccessToken) .where(filter) - .join( - TableName.Identity, - `${TableName.Identity}.id`, - `${TableName.IdentityAccessToken}.identityId` - ) + .join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityAccessToken}.identityId`) .leftJoin( TableName.IdentityUaClientSecret, `${TableName.IdentityAccessToken}.identityUAClientSecretId`, diff --git a/backend/src/services/identity-access-token/identity-access-token-service.ts b/backend/src/services/identity-access-token/identity-access-token-service.ts index 7509b9dff..cdc8effe2 100644 --- a/backend/src/services/identity-access-token/identity-access-token-service.ts +++ b/backend/src/services/identity-access-token/identity-access-token-service.ts @@ -7,18 +7,13 @@ import { checkIPAgainstBlocklist, TIp } from "@app/lib/ip"; import { AuthTokenType } from "../auth/auth-type"; import { TIdentityAccessTokenDALFactory } from "./identity-access-token-dal"; -import { - TIdentityAccessTokenJwtPayload, - TRenewAccessTokenDTO -} from "./identity-access-token-types"; +import { TIdentityAccessTokenJwtPayload, TRenewAccessTokenDTO } from "./identity-access-token-types"; type TIdentityAccessTokenServiceFactoryDep = { identityAccessTokenDAL: TIdentityAccessTokenDALFactory; }; -export type TIdentityAccessTokenServiceFactory = ReturnType< - typeof identityAccessTokenServiceFactory ->; +export type TIdentityAccessTokenServiceFactory = ReturnType; export const identityAccessTokenServiceFactory = ({ identityAccessTokenDAL @@ -88,9 +83,10 @@ export const identityAccessTokenServiceFactory = ({ const renewAccessToken = async ({ accessToken }: TRenewAccessTokenDTO) => { const appCfg = getConfig(); - const decodedToken = jwt.verify(accessToken, appCfg.AUTH_SECRET) as JwtPayload; - if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) - throw new UnauthorizedError(); + const decodedToken = jwt.verify(accessToken, appCfg.AUTH_SECRET) as JwtPayload & { + identityAccessTokenId: string; + }; + if (decodedToken.authTokenType !== AuthTokenType.IDENTITY_ACCESS_TOKEN) throw new UnauthorizedError(); const identityAccessToken = await identityAccessTokenDAL.findOne({ [`${TableName.IdentityAccessToken}.id` as "id"]: decodedToken.identityAccessTokenId, @@ -100,20 +96,14 @@ export const identityAccessTokenServiceFactory = ({ validateAccessTokenExp(identityAccessToken); - const updatedIdentityAccessToken = await identityAccessTokenDAL.updateById( - identityAccessToken.id, - { - accessTokenLastRenewedAt: new Date() - } - ); + const updatedIdentityAccessToken = await identityAccessTokenDAL.updateById(identityAccessToken.id, { + accessTokenLastRenewedAt: new Date() + }); return { accessToken, identityAccessToken: updatedIdentityAccessToken }; }; - const fnValidateIdentityAccessToken = async ( - token: TIdentityAccessTokenJwtPayload, - ipAddress?: string - ) => { + const fnValidateIdentityAccessToken = async (token: TIdentityAccessTokenJwtPayload, ipAddress?: string) => { const identityAccessToken = await identityAccessTokenDAL.findOne({ [`${TableName.IdentityAccessToken}.id` as "id"]: token.identityAccessTokenId, isAccessTokenRevoked: false diff --git a/backend/src/services/identity-project/identity-project-dal.ts b/backend/src/services/identity-project/identity-project-dal.ts index fd906cf10..dbb864387 100644 --- a/backend/src/services/identity-project/identity-project-dal.ts +++ b/backend/src/services/identity-project/identity-project-dal.ts @@ -14,11 +14,7 @@ export const identityProjectDALFactory = (db: TDbClient) => { try { const docs = await (tx || db)(TableName.IdentityProjectMembership) .where(`${TableName.IdentityProjectMembership}.projectId`, projectId) - .join( - TableName.Identity, - `${TableName.IdentityProjectMembership}.identityId`, - `${TableName.Identity}.id` - ) + .join(TableName.Identity, `${TableName.IdentityProjectMembership}.identityId`, `${TableName.Identity}.id`) .leftJoin( TableName.ProjectRoles, `${TableName.IdentityProjectMembership}.roleId`, diff --git a/backend/src/services/identity-project/identity-project-service.ts b/backend/src/services/identity-project/identity-project-service.ts index d8c42dde6..05e0bd68b 100644 --- a/backend/src/services/identity-project/identity-project-service.ts +++ b/backend/src/services/identity-project/identity-project-service.ts @@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import { ProjectMembershipRole, TProjectRoles } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors"; @@ -24,10 +21,7 @@ type TIdentityProjectServiceFactoryDep = { identityProjectDAL: TIdentityProjectDALFactory; projectDAL: Pick; identityOrgMembershipDAL: Pick; - permissionService: Pick< - TPermissionServiceFactory, - "getProjectPermission" | "getProjectPermissionByRole" - >; + permissionService: Pick; }; export type TIdentityProjectServiceFactory = ReturnType; @@ -38,18 +32,9 @@ export const identityProjectServiceFactory = ({ identityOrgMembershipDAL, projectDAL }: TIdentityProjectServiceFactoryDep) => { - const createProjectIdentity = async ({ - identityId, - actor, - actorId, - projectId, - role - }: TCreateProjectIdentityDTO) => { + const createProjectIdentity = async ({ identityId, actor, actorId, projectId, role }: TCreateProjectIdentityDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Identity - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Identity); const existingIdentity = await identityProjectDAL.findOne({ identityId, projectId }); if (existingIdentity) @@ -67,8 +52,10 @@ export const identityProjectServiceFactory = ({ message: `Failed to find identity with id ${identityId}` }); - const { permission: rolePermission, role: customRole } = - await permissionService.getProjectPermissionByRole(role, project.id); + const { permission: rolePermission, role: customRole } = await permissionService.getProjectPermissionByRole( + role, + project.id + ); const hasPriviledge = isAtLeastAsPrivileged(permission, rolePermission); if (!hasPriviledge) throw new ForbiddenRequestError({ @@ -85,18 +72,9 @@ export const identityProjectServiceFactory = ({ return projectIdentity; }; - const updateProjectIdentity = async ({ - projectId, - identityId, - role, - actor, - actorId - }: TUpdateProjectIdentityDTO) => { + const updateProjectIdentity = async ({ projectId, identityId, role, actor, actorId }: TUpdateProjectIdentityDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Identity - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); const projectIdentity = await identityProjectDAL.findOne({ identityId, projectId }); if (!projectIdentity) @@ -115,8 +93,10 @@ export const identityProjectServiceFactory = ({ let customRole: TProjectRoles | undefined; if (role) { - const { permission: rolePermission, role: customOrgRole } = - await permissionService.getProjectPermissionByRole(role, projectIdentity.projectId); + const { permission: rolePermission, role: customOrgRole } = await permissionService.getProjectPermissionByRole( + role, + projectIdentity.projectId + ); const isCustomRole = Boolean(customOrgRole); const hasRequiredNewRolePermission = isAtLeastAsPrivileged(permission, rolePermission); @@ -135,12 +115,7 @@ export const identityProjectServiceFactory = ({ return updatedProjectIdentity; }; - const deleteProjectIdentity = async ({ - identityId, - actorId, - actor, - projectId - }: TDeleteProjectIdentityDTO) => { + const deleteProjectIdentity = async ({ identityId, actorId, actor, projectId }: TDeleteProjectIdentityDTO) => { const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId }); if (!identityProjectMembership) throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` }); @@ -150,10 +125,7 @@ export const identityProjectServiceFactory = ({ actorId, identityProjectMembership.projectId ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Identity - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Identity); const { permission: identityRolePermission } = await permissionService.getProjectPermission( ActorType.IDENTITY, identityId, @@ -169,10 +141,7 @@ export const identityProjectServiceFactory = ({ const listProjectIdentities = async ({ projectId, actor, actorId }: TListProjectIdentityDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Identity - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Identity); const identityMemberhips = await identityProjectDAL.findByProjectId(projectId); return identityMemberhips; diff --git a/backend/src/services/identity-ua/identity-ua-service.ts b/backend/src/services/identity-ua/identity-ua-service.ts index 1eb528977..aa5c3c895 100644 --- a/backend/src/services/identity-ua/identity-ua-service.ts +++ b/backend/src/services/identity-ua/identity-ua-service.ts @@ -6,10 +6,7 @@ import jwt from "jsonwebtoken"; import { IdentityAuthMethod } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "@app/ee/services/permission/org-permission"; +import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { getConfig } from "@app/lib/config/env"; @@ -71,8 +68,7 @@ export const identityUaServiceFactory = ({ ); if (!validClientSecretInfo) throw new UnauthorizedError(); - const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = - validClientSecretInfo; + const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = validClientSecretInfo; if (clientSecretTTL > 0) { const clientSecretCreated = new Date(validClientSecretInfo.createdAt); const ttlInMilliseconds = clientSecretTTL * 1000; @@ -97,16 +93,12 @@ export const identityUaServiceFactory = ({ isClientSecretRevoked: true }); throw new UnauthorizedError({ - message: - "Failed to authenticate identity credentials due to client secret number of uses limit reached" + message: "Failed to authenticate identity credentials due to client secret number of uses limit reached" }); } const identityAccessToken = await identityUaDAL.transaction(async (tx) => { - const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage( - validClientSecretInfo.id, - tx - ); + const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo.id, tx); const newToken = await identityAccessTokenDAL.create( { identityId: identityUa.identityId, @@ -132,10 +124,7 @@ export const identityUaServiceFactory = ({ } as TIdentityAccessTokenJwtPayload, appCfg.AUTH_SECRET, { - expiresIn: - identityAccessToken.accessTokenMaxTTL === 0 - ? undefined - : identityAccessToken.accessTokenMaxTTL + expiresIn: identityAccessToken.accessTokenMaxTTL === 0 ? undefined : identityAccessToken.accessTokenMaxTTL } ); return { accessToken, identityUa, validClientSecretInfo, identityAccessToken }; @@ -162,35 +151,26 @@ export const identityUaServiceFactory = ({ throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); } - const { permission } = await permissionService.getOrgPermission( - actor, - actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Identity - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity); const plan = await licenseService.getPlan(identityMembershipOrg.orgId); - const reformattedClientSecretTrustedIps = clientSecretTrustedIps.map( - (clientSecretTrustedIp) => { - if ( - !plan.ipAllowlisting && - clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" && - clientSecretTrustedIp.ipAddress !== "::/0" - ) - throw new BadRequestError({ - message: - "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." - }); - if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress)) - throw new BadRequestError({ - message: "The IP is not a valid IPv4, IPv6, or CIDR block" - }); - return extractIPDetails(clientSecretTrustedIp.ipAddress); - } - ); + const reformattedClientSecretTrustedIps = clientSecretTrustedIps.map((clientSecretTrustedIp) => { + if ( + !plan.ipAllowlisting && + clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" && + clientSecretTrustedIp.ipAddress !== "::/0" + ) + throw new BadRequestError({ + message: + "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." + }); + if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress)) + throw new BadRequestError({ + message: "The IP is not a valid IPv4, IPv6, or CIDR block" + }); + return extractIPDetails(clientSecretTrustedIp.ipAddress); + }); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -254,41 +234,31 @@ export const identityUaServiceFactory = ({ if ( (accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL) > 0 && - (accessTokenTTL || uaIdentityAuth.accessTokenMaxTTL) > - (accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL) + (accessTokenTTL || uaIdentityAuth.accessTokenMaxTTL) > (accessTokenMaxTTL || uaIdentityAuth.accessTokenMaxTTL) ) { throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); } - const { permission } = await permissionService.getOrgPermission( - actor, - actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Identity - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity); const plan = await licenseService.getPlan(identityMembershipOrg.orgId); - const reformattedClientSecretTrustedIps = clientSecretTrustedIps?.map( - (clientSecretTrustedIp) => { - if ( - !plan.ipAllowlisting && - clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" && - clientSecretTrustedIp.ipAddress !== "::/0" - ) - throw new BadRequestError({ - message: - "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." - }); - if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress)) - throw new BadRequestError({ - message: "The IP is not a valid IPv4, IPv6, or CIDR block" - }); - return extractIPDetails(clientSecretTrustedIp.ipAddress); - } - ); + const reformattedClientSecretTrustedIps = clientSecretTrustedIps?.map((clientSecretTrustedIp) => { + if ( + !plan.ipAllowlisting && + clientSecretTrustedIp.ipAddress !== "0.0.0.0/0" && + clientSecretTrustedIp.ipAddress !== "::/0" + ) + throw new BadRequestError({ + message: + "Failed to add IP access range to service token due to plan restriction. Upgrade plan to add IP access range." + }); + if (!isValidIpOrCidr(clientSecretTrustedIp.ipAddress)) + throw new BadRequestError({ + message: "The IP is not a valid IPv4, IPv6, or CIDR block" + }); + return extractIPDetails(clientSecretTrustedIp.ipAddress); + }); const reformattedAccessTokenTrustedIps = accessTokenTrustedIps?.map((accessTokenTrustedIp) => { if ( !plan.ipAllowlisting && @@ -330,15 +300,8 @@ export const identityUaServiceFactory = ({ const uaIdentityAuth = await identityUaDAL.findOne({ identityId }); - const { permission } = await permissionService.getOrgPermission( - actor, - actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Identity - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); return { ...uaIdentityAuth, orgId: identityMembershipOrg.orgId }; }; @@ -356,15 +319,8 @@ export const identityUaServiceFactory = ({ throw new BadRequestError({ message: "The identity does not have universal auth" }); - const { permission } = await permissionService.getOrgPermission( - actor, - actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Identity - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity); const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, @@ -409,15 +365,8 @@ export const identityUaServiceFactory = ({ throw new BadRequestError({ message: "The identity does not have universal auth" }); - const { permission } = await permissionService.getOrgPermission( - actor, - actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Identity - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, @@ -441,27 +390,15 @@ export const identityUaServiceFactory = ({ return { clientSecrets, orgId: identityMembershipOrg.orgId }; }; - const revokeUaClientSecret = async ({ - identityId, - actorId, - actor, - clientSecretId - }: TRevokeUaClientSecretDTO) => { + const revokeUaClientSecret = async ({ identityId, actorId, actor, clientSecretId }: TRevokeUaClientSecretDTO) => { const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId }); if (!identityMembershipOrg) throw new BadRequestError({ message: "Failed to find identity" }); if (identityMembershipOrg.identity?.authMethod !== IdentityAuthMethod.Univeral) throw new BadRequestError({ message: "The identity does not have universal auth" }); - const { permission } = await permissionService.getOrgPermission( - actor, - actorId, - identityMembershipOrg.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Identity - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, identityMembershipOrg.orgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity); const { permission: rolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, diff --git a/backend/src/services/identity/identity-org-dal.ts b/backend/src/services/identity/identity-org-dal.ts index c5413d058..95d742f33 100644 --- a/backend/src/services/identity/identity-org-dal.ts +++ b/backend/src/services/identity/identity-org-dal.ts @@ -14,11 +14,7 @@ export const identityOrgDALFactory = (db: TDbClient) => { try { const [data] = await (tx || db)(TableName.IdentityOrgMembership) .where(filter) - .join( - TableName.Identity, - `${TableName.IdentityOrgMembership}.identityId`, - `${TableName.Identity}.id` - ) + .join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`) .select(selectAllTableCols(TableName.IdentityOrgMembership)) .select(db.ref("name").withSchema(TableName.Identity)) .select(db.ref("authMethod").withSchema(TableName.Identity)); @@ -35,16 +31,8 @@ export const identityOrgDALFactory = (db: TDbClient) => { try { const docs = await (tx || db)(TableName.IdentityOrgMembership) .where(`${TableName.IdentityOrgMembership}.orgId`, orgId) - .join( - TableName.Identity, - `${TableName.IdentityOrgMembership}.identityId`, - `${TableName.Identity}.id` - ) - .leftJoin( - TableName.OrgRoles, - `${TableName.IdentityOrgMembership}.roleId`, - `${TableName.OrgRoles}.id` - ) + .join(TableName.Identity, `${TableName.IdentityOrgMembership}.identityId`, `${TableName.Identity}.id`) + .leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`) .select(selectAllTableCols(TableName.IdentityOrgMembership)) // cr stands for custom role .select(db.ref("id").as("crId").withSchema(TableName.OrgRoles)) diff --git a/backend/src/services/identity/identity-service.ts b/backend/src/services/identity/identity-service.ts index 09fb5fb36..3dd494034 100644 --- a/backend/src/services/identity/identity-service.ts +++ b/backend/src/services/identity/identity-service.ts @@ -1,10 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import { OrgMembershipRole, TOrgRoles } from "@app/db/schemas"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "@app/ee/services/permission/org-permission"; +import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors"; @@ -30,17 +27,15 @@ export const identityServiceFactory = ({ }: TIdentityServiceFactoryDep) => { const createIdentity = async ({ name, role, actor, orgId, actorId }: TCreateIdentityDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Identity - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Identity); - const { permission: rolePermission, role: customRole } = - await permissionService.getOrgPermissionByRole(role, orgId); + const { permission: rolePermission, role: customRole } = await permissionService.getOrgPermissionByRole( + role, + orgId + ); const isCustomRole = Boolean(customRole); const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, rolePermission); - if (!hasRequiredPriviledges) - throw new BadRequestError({ message: "Failed to create a more privileged identity" }); + if (!hasRequiredPriviledges) throw new BadRequestError({ message: "Failed to create a more privileged identity" }); const identity = await identityDAL.transaction(async (tx) => { const newIdentity = await identityDAL.create({ name }, tx); @@ -56,24 +51,15 @@ export const identityServiceFactory = ({ return newIdentity; }); - return identity; }; const updateIdentity = async ({ id, role, name, actor, actorId }: TUpdateIdentityDTO) => { const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id }); - if (!identityOrgMembership) - throw new BadRequestError({ message: `Failed to find identity with id ${id}` }); + if (!identityOrgMembership) throw new BadRequestError({ message: `Failed to find identity with id ${id}` }); - const { permission } = await permissionService.getOrgPermission( - actor, - actorId, - identityOrgMembership.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Identity - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, identityOrgMembership.orgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity); const { permission: identityRolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, @@ -86,8 +72,10 @@ export const identityServiceFactory = ({ let customRole: TOrgRoles | undefined; if (role) { - const { permission: rolePermission, role: customOrgRole } = - await permissionService.getOrgPermissionByRole(role, identityOrgMembership.orgId); + const { permission: rolePermission, role: customOrgRole } = await permissionService.getOrgPermissionByRole( + role, + identityOrgMembership.orgId + ); const isCustomRole = Boolean(customOrgRole); const hasRequiredNewRolePermission = isAtLeastAsPrivileged(permission, rolePermission); @@ -97,9 +85,7 @@ export const identityServiceFactory = ({ } const identity = await identityDAL.transaction(async (tx) => { - const newIdentity = name - ? await identityDAL.updateById(id, { name }, tx) - : await identityDAL.findById(id, tx); + const newIdentity = name ? await identityDAL.updateById(id, { name }, tx) : await identityDAL.findById(id, tx); if (role) { await identityOrgMembershipDAL.update( { identityId: id }, @@ -118,18 +104,10 @@ export const identityServiceFactory = ({ const deleteIdentity = async ({ actorId, actor, id }: TDeleteIdentityDTO) => { const identityOrgMembership = await identityOrgMembershipDAL.findOne({ identityId: id }); - if (!identityOrgMembership) - throw new BadRequestError({ message: `Failed to find identity with id ${id}` }); + if (!identityOrgMembership) throw new BadRequestError({ message: `Failed to find identity with id ${id}` }); - const { permission } = await permissionService.getOrgPermission( - actor, - actorId, - identityOrgMembership.orgId - ); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Identity - ); + const { permission } = await permissionService.getOrgPermission(actor, actorId, identityOrgMembership.orgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity); const { permission: identityRolePermission } = await permissionService.getOrgPermission( ActorType.IDENTITY, id, @@ -145,10 +123,7 @@ export const identityServiceFactory = ({ const listOrgIdentities = async ({ orgId, actor, actorId }: TOrgPermission) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Identity - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Identity); const identityMemberhips = await identityOrgMembershipDAL.findByOrgId(orgId); return identityMemberhips; diff --git a/backend/src/services/integration-auth/integration-app-list.ts b/backend/src/services/integration-auth/integration-app-list.ts index 39e629606..17b1b63ad 100644 --- a/backend/src/services/integration-auth/integration-app-list.ts +++ b/backend/src/services/integration-auth/integration-app-list.ts @@ -22,11 +22,7 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string }) interface GCPApp { projectNumber: string; projectId: string; - lifecycleState: - | "ACTIVE" - | "LIFECYCLE_STATE_UNSPECIFIED" - | "DELETE_REQUESTED" - | "DELETE_IN_PROGRESS"; + lifecycleState: "ACTIVE" | "LIFECYCLE_STATE_UNSPECIFIED" | "DELETE_REQUESTED" | "DELETE_IN_PROGRESS"; name: string; createTime: string; parent: { @@ -59,8 +55,8 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string }) ...(pageToken ? { pageToken } : {}) }); - const res: GCPGetProjectsRes = ( - await request.get(`${IntegrationUrls.GCP_API_URL}/v1/projects`, { + const res = ( + await request.get(`${IntegrationUrls.GCP_API_URL}/v1/projects`, { params, headers: { Authorization: `Bearer ${accessToken}`, @@ -81,8 +77,8 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string }) // eslint-disable-next-line for await (const gcpApp of gcpApps) { try { - const res: GCPGetServiceRes = ( - await request.get( + const res = ( + await request.get( `${IntegrationUrls.GCP_SERVICE_USAGE_URL}/v1/projects/${gcpApp.projectId}/services/${IntegrationUrls.GCP_SECRET_MANAGER_SERVICE_NAME}`, { headers: { @@ -113,7 +109,7 @@ const getAppsGCPSecretManager = async ({ accessToken }: { accessToken: string }) */ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.HEROKU_API_URL}/apps`, { + await request.get<{ name: string }[]>(`${IntegrationUrls.HEROKU_API_URL}/apps`, { headers: { Accept: "application/vnd.heroku+json; version=3", Authorization: `Bearer ${accessToken}` @@ -121,7 +117,7 @@ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => { }) ).data; - const apps = res.map((a: any) => ({ + const apps = res.map((a) => ({ name: a.name })); @@ -131,15 +127,9 @@ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => { /** * Return list of names of apps for Vercel integration */ -const getAppsVercel = async ({ - accessToken, - teamId -}: { - teamId?: string | null; - accessToken: string; -}) => { +const getAppsVercel = async ({ accessToken, teamId }: { teamId?: string | null; accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.VERCEL_API_URL}/v9/projects`, { + await request.get<{ projects: { name: string; id: string }[] }>(`${IntegrationUrls.VERCEL_API_URL}/v9/projects`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" @@ -154,7 +144,7 @@ const getAppsVercel = async ({ }) ).data; - const apps = res.projects.map((a: any) => ({ + const apps = res.projects.map((a) => ({ name: a.name, appId: a.id })); @@ -166,7 +156,7 @@ const getAppsVercel = async ({ * Return list of sites for Netlify integration */ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => { - const apps: any = []; + const apps: Array<{ name: string; appId: string }> = []; let page = 1; const perPage = 10; let hasMorePages = true; @@ -179,15 +169,18 @@ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => { filter: "all" }); - const { data } = await request.get(`${IntegrationUrls.NETLIFY_API_URL}/api/v1/sites`, { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + const { data } = await request.get<{ name: string; site_id: string }[]>( + `${IntegrationUrls.NETLIFY_API_URL}/api/v1/sites`, + { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }); + ); - data.forEach((a: any) => { + data.forEach((a) => { apps.push({ name: a.name, appId: a.site_id @@ -238,8 +231,8 @@ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => { } ); - if (response.data.length > 0) { - repos = repos.concat(response.data); + if ((response.data as GitHubApp[]).length > 0) { + repos = repos.concat(response.data as GitHubApp[]); page += 1; } else { hasMore = false; @@ -267,7 +260,7 @@ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => { */ const getAppsRender = async ({ accessToken }: { accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.RENDER_API_URL}/v1/services`, { + await request.get<{ service: { name: string; id: string } }[]>(`${IntegrationUrls.RENDER_API_URL}/v1/services`, { headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/json", @@ -276,7 +269,7 @@ const getAppsRender = async ({ accessToken }: { accessToken: string }) => { }) ).data; - const apps = res.map((a: any) => ({ + const apps = res.map((a) => ({ name: a.service.name, appId: a.service.id })); @@ -309,7 +302,9 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => { projects: { edges } } } - } = await request.post( + } = await request.post<{ + data: { projects: { edges: { node: { name: string; id: string } }[] } }; + }>( IntegrationUrls.RAILWAY_API_URL, { query, @@ -324,7 +319,7 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => { } ); - const apps = edges.map((e: any) => ({ + const apps = edges.map((e) => ({ name: e.node.name, appId: e.node.id })); @@ -335,24 +330,21 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => { /** * Return list of sites for Laravel Forge integration */ -const getAppsLaravelForge = async ({ - accessToken, - serverId -}: { - accessToken: string; - serverId?: string; -}) => { +const getAppsLaravelForge = async ({ accessToken, serverId }: { accessToken: string; serverId?: string }) => { const res = ( - await request.get(`${IntegrationUrls.LARAVELFORGE_API_URL}/api/v1/servers/${serverId}/sites`, { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "Content-Type": "application/json" + await request.get<{ sites: { name: string; id: string }[] }>( + `${IntegrationUrls.LARAVELFORGE_API_URL}/api/v1/servers/${serverId}/sites`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json", + "Content-Type": "application/json" + } } - }) + ) ).data.sites; - const apps = res.map((a: any) => ({ + const apps = res.map((a) => ({ name: a.name, appId: a.id })); @@ -382,8 +374,8 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => { } `; - const res: FlyioApp[] = ( - await request.post( + const res = ( + await request.post<{ data: { apps: { nodes: FlyioApp[] } } }>( IntegrationUrls.FLYIO_API_URL, { query, @@ -401,7 +393,7 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => { ) ).data.data.apps.nodes; - const apps = res.map((a: FlyioApp) => ({ + const apps = res.map((a) => ({ name: a.name, appId: a.id })); @@ -414,7 +406,7 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => { */ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.CIRCLECI_API_URL}/v1.1/projects`, { + await request.get<{ reponame: string }[]>(`${IntegrationUrls.CIRCLECI_API_URL}/v1.1/projects`, { headers: { "Circle-Token": accessToken, "Accept-Encoding": "application/json" @@ -422,7 +414,7 @@ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => { }) ).data; - const apps = res?.map((a: any) => ({ + const apps = res?.map((a) => ({ name: a?.reponame })); @@ -431,7 +423,7 @@ const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => { const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.TRAVISCI_API_URL}/repos`, { + await request.get<{ id: string; slug: string }[]>(`${IntegrationUrls.TRAVISCI_API_URL}/repos`, { headers: { Authorization: `token ${accessToken}`, "Accept-Encoding": "application/json" @@ -439,7 +431,7 @@ const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => { }) ).data; - const apps = res?.map((a: any) => ({ + const apps = res?.map((a) => ({ name: a?.slug?.split("/")[1], appId: a?.id })); @@ -450,15 +442,9 @@ const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => { /** * Return list of projects for Terraform Cloud integration */ -const getAppsTerraformCloud = async ({ - accessToken, - workspacesId -}: { - accessToken: string; - workspacesId?: string; -}) => { +const getAppsTerraformCloud = async ({ accessToken, workspacesId }: { accessToken: string; workspacesId?: string }) => { const res = ( - await request.get( + await request.get<{ data: { attributes: { name: string }; id: string } }>( `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${workspacesId}`, { headers: { @@ -510,15 +496,18 @@ const getAppsGitlab = async ({ per_page: String(perPage) }); - const { data } = await request.get(`${gitLabApiUrl}/v4/groups/${teamId}/projects`, { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + const { data } = await request.get<{ name: string; id: string }[]>( + `${gitLabApiUrl}/v4/groups/${teamId}/projects`, + { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }); + ); - data.forEach((a: any) => { + data.forEach((a) => { apps.push({ name: a.name, appId: a.id @@ -535,7 +524,7 @@ const getAppsGitlab = async ({ // case: fetch projects for individual in GitLab const { id } = ( - await request.get(`${gitLabApiUrl}/v4/user`, { + await request.get<{ id: string }>(`${gitLabApiUrl}/v4/user`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" @@ -549,7 +538,7 @@ const getAppsGitlab = async ({ per_page: String(perPage) }); - const { data } = await request.get(`${gitLabApiUrl}/v4/users/${id}/projects`, { + const { data } = await request.get<{ name: string; id: string }[]>(`${gitLabApiUrl}/v4/users/${id}/projects`, { params, headers: { Authorization: `Bearer ${accessToken}`, @@ -557,7 +546,7 @@ const getAppsGitlab = async ({ } }); - data.forEach((a: any) => { + data.forEach((a) => { apps.push({ name: a.name, appId: a.id @@ -580,7 +569,7 @@ const getAppsGitlab = async ({ */ const getAppsTeamCity = async ({ accessToken, url }: { url: string; accessToken: string }) => { const res = ( - await request.get(`${url}/app/rest/projects`, { + await request.get<{ project: { name: string; id: string }[] }>(`${url}/app/rest/projects`, { headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/json" @@ -588,7 +577,7 @@ const getAppsTeamCity = async ({ accessToken, url }: { url: string; accessToken: }) ).data.project.slice(1); - const apps = res.map((a: any) => ({ + const apps = res.map((a) => ({ name: a.name, appId: a.id })); @@ -600,14 +589,17 @@ const getAppsTeamCity = async ({ accessToken, url }: { url: string; accessToken: * Return list of projects for Supabase integration */ const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => { - const { data } = await request.get(`${IntegrationUrls.SUPABASE_API_URL}/v1/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + const { data } = await request.get<{ name: string; id: string }[]>( + `${IntegrationUrls.SUPABASE_API_URL}/v1/projects`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }); + ); - const apps = data.map((a: any) => ({ + const apps = data.map((a) => ({ name: a.name, appId: a.id })); @@ -619,14 +611,14 @@ const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => { * Return list of accounts for the Checkly integration */ const getAppsCheckly = async ({ accessToken }: { accessToken: string }) => { - const { data } = await request.get(`${IntegrationUrls.CHECKLY_API_URL}/v1/accounts`, { + const { data } = await request.get<{ name: string; id: string }[]>(`${IntegrationUrls.CHECKLY_API_URL}/v1/accounts`, { headers: { Authorization: `Bearer ${accessToken}`, Accept: "application/json" } }); - const apps = data.map((a: any) => ({ + const apps = data.map((a) => ({ name: a.name, appId: a.id })); @@ -637,14 +629,8 @@ const getAppsCheckly = async ({ accessToken }: { accessToken: string }) => { /** * Return list of projects for the Cloudflare Pages integration */ -const getAppsCloudflarePages = async ({ - accessToken, - accountId -}: { - accessToken: string; - accountId?: string; -}) => { - const { data } = await request.get( +const getAppsCloudflarePages = async ({ accessToken, accountId }: { accessToken: string; accountId?: string }) => { + const { data } = await request.get<{ result: { name: string; id: string }[] }>( `${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accountId}/pages/projects`, { headers: { @@ -654,7 +640,7 @@ const getAppsCloudflarePages = async ({ } ); - const apps = data.result.map((a: any) => ({ + const apps = data.result.map((a) => ({ name: a.name, appId: a.id })); @@ -664,14 +650,8 @@ const getAppsCloudflarePages = async ({ /** * Return list of projects for the Cloudflare Workers integration */ -const getAppsCloudflareWorkers = async ({ - accessToken, - accountId -}: { - accessToken: string; - accountId?: string; -}) => { - const { data } = await request.get( +const getAppsCloudflareWorkers = async ({ accessToken, accountId }: { accessToken: string; accountId?: string }) => { + const { data } = await request.get<{ result: { id: string }[] }>( `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accountId}/workers/services`, { headers: { @@ -681,7 +661,7 @@ const getAppsCloudflareWorkers = async ({ } ); - const apps = data.result.map((a: any) => ({ + const apps = data.result.map((a) => ({ name: a.id, appId: a.id })); @@ -691,13 +671,7 @@ const getAppsCloudflareWorkers = async ({ /** * Return list of repositories for the BitBucket integration based on provided BitBucket workspace */ -const getAppsBitBucket = async ({ - accessToken, - workspaceSlug -}: { - accessToken: string; - workspaceSlug?: string; -}) => { +const getAppsBitBucket = async ({ accessToken, workspaceSlug }: { accessToken: string; workspaceSlug?: string }) => { interface RepositoriesResponse { size: number; page: number; @@ -759,14 +733,17 @@ const getAppsNorthflank = async ({ accessToken }: { accessToken: string }) => { data: { data: { projects } } - } = await request.get(`${IntegrationUrls.NORTHFLANK_API_URL}/v1/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + } = await request.get<{ data: { projects: { name: string; id: string }[] } }>( + `${IntegrationUrls.NORTHFLANK_API_URL}/v1/projects`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }); + ); - const apps = projects.map((a: any) => ({ + const apps = projects.map((a) => ({ name: a.name, appId: a.id })); @@ -779,15 +756,18 @@ const getAppsNorthflank = async ({ accessToken }: { accessToken: string }) => { */ const getAppsCodefresh = async ({ accessToken }: { accessToken: string }) => { const res = ( - await request.get(`${IntegrationUrls.CODEFRESH_API_URL}/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + await request.get<{ projects: { projectName: string; id: string }[] }>( + `${IntegrationUrls.CODEFRESH_API_URL}/projects`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }) + ) ).data; - const apps = res.projects.map((a: any) => ({ + const apps = res.projects.map((a) => ({ name: a.projectName, appId: a.id })); @@ -799,20 +779,23 @@ const getAppsCodefresh = async ({ accessToken }: { accessToken: string }) => { * Return list of projects for Windmill integration */ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => { - const { data } = await request.get(`${IntegrationUrls.WINDMILL_API_URL}/workspaces/list`, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + const { data } = await request.get<{ id: string; name: string }[]>( + `${IntegrationUrls.WINDMILL_API_URL}/workspaces/list`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }); + ); // check for write access of secrets in windmill workspaces - const writeAccessCheck = data.map(async (app: any) => { + const writeAccessCheck = data.map(async (app) => { try { const userPath = "u/user/variable"; const folderPath = "f/folder/variable"; - const { data: writeUser } = await request.post( + const { data: writeUser } = await request.post( `${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/create`, { path: userPath, @@ -828,7 +811,7 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => { } ); - const { data: writeFolder } = await request.post( + const { data: writeFolder } = await request.post( `${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/create`, { path: folderPath, @@ -846,38 +829,32 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => { // is write access is allowed then delete the created secrets from workspace if (writeUser && writeFolder) { - await request.delete( - `${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${userPath}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } + await request.delete(`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${userPath}`, { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ); + }); - await request.delete( - `${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${folderPath}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } + await request.delete(`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${folderPath}`, { + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ); + }); return app; } return { error: "cannot write secret" }; - } catch (err: any) { - return { error: err.message }; + } catch (err) { + return { error: (err as Error).message }; } }); const appsWriteResponses = await Promise.all(writeAccessCheck); - const appsWithWriteAccess = appsWriteResponses.filter((appRes: any) => !appRes.error); + const appsWithWriteAccess = appsWriteResponses.filter((appRes) => !(appRes as { error: string })?.error); - const apps = appsWithWriteAccess.map((a: any) => ({ + const apps = (appsWithWriteAccess as { id: string; name: string }[]).map((a) => ({ name: a.name, appId: a.id })); @@ -908,7 +885,7 @@ const getAppsDigitalOceanAppPlatform = async ({ accessToken }: { accessToken: st } const res = ( - await request.get(`${IntegrationUrls.DIGITAL_OCEAN_API_URL}/v2/apps`, { + await request.get<{ apps: DigitalOceanApp[] }>(`${IntegrationUrls.DIGITAL_OCEAN_API_URL}/v2/apps`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" @@ -916,14 +893,16 @@ const getAppsDigitalOceanAppPlatform = async ({ accessToken }: { accessToken: st }) ).data; - return (res.apps ?? []).map((a: DigitalOceanApp) => ({ + return (res.apps ?? []).map((a) => ({ name: a.spec.name, appId: a.id })); }; const getAppsHasuraCloud = async ({ accessToken }: { accessToken: string }) => { - const res = await request.post( + const res = await request.post<{ + data: { projects: { name: string; tenant: { id: string } }[] }; + }>( IntegrationUrls.HASURA_CLOUD_API_URL, { query: "query MyQuery { projects { name tenant { id } } }" @@ -936,19 +915,15 @@ const getAppsHasuraCloud = async ({ accessToken }: { accessToken: string }) => { } ); - const data = (res?.data?.data?.projects ?? []).map( - ({ name, tenant: { id: appId } }: { name: string; tenant: { id: string } }) => ({ name, appId }) - ); + const data = (res?.data?.data?.projects ?? []).map(({ name, tenant: { id: appId } }) => ({ + name, + appId + })); return data; }; /** * Return list of applications for Cloud66 integration - * @param {Object} obj - * @param {String} obj.accessToken - personal access token for Cloud66 API - * @returns {Object[]} apps - Cloud66 apps - * @returns {String} apps.name - name of Cloud66 app - * @returns {String} apps.appId - uid of Cloud66 app */ const getAppsCloud66 = async ({ accessToken }: { accessToken: string }) => { interface Cloud66Apps { @@ -979,19 +954,19 @@ const getAppsCloud66 = async ({ accessToken }: { accessToken: string }) => { account_name: string; is_cluster: boolean; is_inside_cluster: boolean; - cluster_name: any; + cluster_name: string; application_address: string; configstore_namespace: string; } const stacks = ( - await request.get(`${IntegrationUrls.CLOUD_66_API_URL}/3/stacks`, { + await request.get<{ response: Cloud66Apps[] }>(`${IntegrationUrls.CLOUD_66_API_URL}/3/stacks`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" } }) - ).data.response as Cloud66Apps[]; + ).data.response; const apps = stacks.map((app) => ({ name: app.name, @@ -1016,7 +991,7 @@ export const getApps = async ({ workspaceSlug?: string; url?: string | null; }): Promise => { - switch (integration) { + switch (integration as Integrations) { case Integrations.GCP_SECRET_MANAGER: return getAppsGCPSecretManager({ accessToken diff --git a/backend/src/services/integration-auth/integration-auth-service.ts b/backend/src/services/integration-auth/integration-auth-service.ts index 81b180ea4..844e01f3c 100644 --- a/backend/src/services/integration-auth/integration-auth-service.ts +++ b/backend/src/services/integration-auth/integration-auth-service.ts @@ -1,21 +1,10 @@ import { ForbiddenError } from "@casl/ability"; -import { - SecretEncryptionAlgo, - SecretKeyEncoding, - TIntegrationAuths, - TIntegrationAuthsInsert -} from "@app/db/schemas"; +import { SecretEncryptionAlgo, SecretKeyEncoding, TIntegrationAuths, TIntegrationAuthsInsert } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { request } from "@app/lib/config/request"; -import { - decryptSymmetric128BitHexKeyUTF8, - encryptSymmetric128BitHexKeyUTF8 -} from "@app/lib/crypto"; +import { decryptSymmetric128BitHexKeyUTF8, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; import { BadRequestError } from "@app/lib/errors"; import { TProjectPermission } from "@app/lib/types"; @@ -70,16 +59,9 @@ export const integrationAuthServiceFactory = ({ projectBotDAL, projectBotService }: TIntegrationAuthServiceFactoryDep) => { - const listIntegrationAuthByProjectId = async ({ - actorId, - actor, - projectId - }: TProjectPermission) => { + const listIntegrationAuthByProjectId = async ({ actorId, actor, projectId }: TProjectPermission) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const authorizations = await integrationAuthDAL.find({ projectId }); return authorizations; }; @@ -88,38 +70,20 @@ export const integrationAuthServiceFactory = ({ const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); return integrationAuth; }; - const oauthExchange = async ({ - projectId, - actorId, - actor, - integration, - url, - code - }: TOauthExchangeDTO) => { + const oauthExchange = async ({ projectId, actorId, actor, integration, url, code }: TOauthExchangeDTO) => { if (!Object.values(Integrations).includes(integration as Integrations)) throw new BadRequestError({ message: "Invalid integration" }); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Integrations - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); const bot = await projectBotDAL.findOne({ isActive: true, projectId }); - if (!bot) - throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" }); + if (!bot) throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" }); const tokenExchange = await exchangeCode({ integration, code, url }); const updateDoc: TIntegrationAuthsInsert = { @@ -178,14 +142,10 @@ export const integrationAuthServiceFactory = ({ throw new BadRequestError({ message: "Invalid integration" }); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Integrations - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); const bot = await projectBotDAL.findOne({ isActive: true, projectId }); - if (!bot) - throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" }); + if (!bot) throw new BadRequestError({ message: "Bot must be enabled for oauth2 code token exchange" }); const updateDoc: TIntegrationAuthsInsert = { projectId, @@ -251,11 +211,7 @@ export const integrationAuthServiceFactory = ({ }); } - if ( - integrationAuth.refreshCiphertext && - integrationAuth.refreshIV && - integrationAuth.refreshTag - ) { + if (integrationAuth.refreshCiphertext && integrationAuth.refreshIV && integrationAuth.refreshTag) { const refreshToken = decryptSymmetric128BitHexKeyUTF8({ key: botKey, ciphertext: integrationAuth.refreshCiphertext, @@ -287,11 +243,7 @@ export const integrationAuthServiceFactory = ({ } if (!accessToken) throw new BadRequestError({ message: "Missing access token" }); - if ( - integrationAuth.accessIdTag && - integrationAuth.accessIdIV && - integrationAuth.accessIdCiphertext - ) { + if (integrationAuth.accessIdTag && integrationAuth.accessIdIV && integrationAuth.accessIdCiphertext) { accessId = decryptSymmetric128BitHexKeyUTF8({ key: botKey, ciphertext: integrationAuth.accessIdCiphertext, @@ -302,25 +254,12 @@ export const integrationAuthServiceFactory = ({ return { accessId, accessToken }; }; - const getIntegrationApps = async ({ - actor, - actorId, - teamId, - id, - workspaceSlug - }: TIntegrationAuthAppsDTO) => { + const getIntegrationApps = async ({ actor, actorId, teamId, id, workspaceSlug }: TIntegrationAuthAppsDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken, accessId } = await getIntegrationAccessToken(integrationAuth, botKey); @@ -339,15 +278,8 @@ export const integrationAuthServiceFactory = ({ const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); @@ -359,24 +291,12 @@ export const integrationAuthServiceFactory = ({ return teams; }; - const getVercelBranches = async ({ - appId, - id, - actor, - actorId - }: TIntegrationAuthVercelBranchesDTO) => { + const getVercelBranches = async ({ appId, id, actor, actorId }: TIntegrationAuthVercelBranchesDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); @@ -399,37 +319,22 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getChecklyGroups = async ({ - actorId, - actor, - id, - accountId - }: TIntegrationAuthChecklyGroupsDTO) => { + const getChecklyGroups = async ({ actorId, actor, id, accountId }: TIntegrationAuthChecklyGroupsDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (accountId) { - const { data } = await request.get( - `${IntegrationUrls.CHECKLY_API_URL}/v1/check-groups`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json", - "X-Checkly-Account": accountId - } + const { data } = await request.get(`${IntegrationUrls.CHECKLY_API_URL}/v1/check-groups`, { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json", + "X-Checkly-Account": accountId } - ); + }); return data.map(({ name, id: groupId }) => ({ name, groupId })); } return []; @@ -439,15 +344,8 @@ export const integrationAuthServiceFactory = ({ const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const { data } = await request.get<{ results: Array<{ id: string; name: string }> }>( @@ -463,24 +361,12 @@ export const integrationAuthServiceFactory = ({ return data.results.map(({ name, id: orgId }) => ({ name, orgId })); }; - const getQoveryProjects = async ({ - actorId, - actor, - id, - orgId - }: TIntegrationAuthQoveryProjectDTO) => { + const getQoveryProjects = async ({ actorId, actor, id, orgId }: TIntegrationAuthQoveryProjectDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (orgId) { @@ -498,24 +384,12 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getQoveryEnvs = async ({ - projectId, - id, - actor, - actorId - }: TIntegrationAuthQoveryEnvironmentsDTO) => { + const getQoveryEnvs = async ({ projectId, id, actor, actorId }: TIntegrationAuthQoveryEnvironmentsDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (projectId && projectId !== "none") { @@ -538,24 +412,12 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getQoveryApps = async ({ - id, - actor, - actorId, - environmentId - }: TIntegrationAuthQoveryScopesDTO) => { + const getQoveryApps = async ({ id, actor, actorId, environmentId }: TIntegrationAuthQoveryScopesDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (environmentId) { @@ -577,24 +439,12 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getQoveryContainers = async ({ - id, - actor, - actorId, - environmentId - }: TIntegrationAuthQoveryScopesDTO) => { + const getQoveryContainers = async ({ id, actor, actorId, environmentId }: TIntegrationAuthQoveryScopesDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (environmentId) { @@ -616,24 +466,12 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getQoveryJobs = async ({ - id, - actor, - actorId, - environmentId - }: TIntegrationAuthQoveryScopesDTO) => { + const getQoveryJobs = async ({ id, actor, actorId, environmentId }: TIntegrationAuthQoveryScopesDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (environmentId) { @@ -655,24 +493,12 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getRailwayEnvironments = async ({ - id, - actor, - actorId, - appId - }: TIntegrationAuthRailwayEnvDTO) => { + const getRailwayEnvironments = async ({ id, actor, actorId, appId }: TIntegrationAuthRailwayEnvDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (appId) { @@ -721,24 +547,12 @@ export const integrationAuthServiceFactory = ({ } return []; }; - const getRailwayServices = async ({ - id, - actor, - actorId, - appId - }: TIntegrationAuthRailwayServicesDTO) => { + const getRailwayServices = async ({ id, actor, actorId, appId }: TIntegrationAuthRailwayServicesDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (appId) { @@ -806,23 +620,12 @@ export const integrationAuthServiceFactory = ({ return []; }; - const getBitbucketWorkspaces = async ({ - actorId, - actor, - id - }: TIntegrationAuthBitbucketWorkspaceDTO) => { + const getBitbucketWorkspaces = async ({ actorId, actor, id }: TIntegrationAuthBitbucketWorkspaceDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const workspaces: TBitbucketWorkspace[] = []; @@ -834,12 +637,11 @@ export const integrationAuthServiceFactory = ({ const { data }: { data: { values: TBitbucketWorkspace[]; next: string } } = await request.get( workspaceUrl, { - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ); + }); if (data?.values.length > 0) { data.values.forEach((workspace) => { @@ -856,24 +658,12 @@ export const integrationAuthServiceFactory = ({ return workspaces; }; - const getNorthFlankSecretGroups = async ({ - id, - actor, - actorId, - appId - }: TIntegrationAuthNorthflankSecretGroupDTO) => { + const getNorthFlankSecretGroups = async ({ id, actor, actorId, appId }: TIntegrationAuthNorthflankSecretGroupDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); const secretGroups: { name: string; groupId: string }[] = []; @@ -906,7 +696,7 @@ export const integrationAuthServiceFactory = ({ } ); - secrets.forEach((a: any) => { + secrets.forEach((a) => { secretGroups.push({ name: a.name, groupId: a.id @@ -923,41 +713,26 @@ export const integrationAuthServiceFactory = ({ return secretGroups; }; - const getTeamcityBuildConfigs = async ({ - appId, - id, - actorId, - actor - }: TGetIntegrationAuthTeamCityBuildConfigDTO) => { + const getTeamcityBuildConfigs = async ({ appId, id, actorId, actor }: TGetIntegrationAuthTeamCityBuildConfigDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const botKey = await projectBotService.getBotKey(integrationAuth.projectId); const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); if (appId) { const { data: { buildType } - } = await request.get<{ buildType: TTeamCityBuildConfig[] }>( - `${integrationAuth.url}/app/rest/buildTypes`, - { - params: { - locator: `project:${appId}` - }, - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } + } = await request.get<{ buildType: TTeamCityBuildConfig[] }>(`${integrationAuth.url}/app/rest/buildTypes`, { + params: { + locator: `project:${appId}` + }, + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" } - ); + }); return buildType.map(({ name, id: buildConfigId }) => ({ name, @@ -967,39 +742,20 @@ export const integrationAuthServiceFactory = ({ return []; }; - const deleteIntegrationAuths = async ({ - projectId, - integration, - actor, - actorId - }: TDeleteIntegrationAuthsDTO) => { + const deleteIntegrationAuths = async ({ projectId, integration, actor, actorId }: TDeleteIntegrationAuthsDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Integrations - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); const integrations = await integrationAuthDAL.delete({ integration, projectId }); return integrations; }; - const deleteIntegrationAuthById = async ({ - id, - actorId, - actor - }: TDeleteIntegrationAuthByIdDTO) => { + const deleteIntegrationAuthById = async ({ id, actorId, actor }: TDeleteIntegrationAuthByIdDTO) => { const integrationAuth = await integrationAuthDAL.findById(id); if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); const delIntegrationAuth = await integrationAuthDAL.transaction(async (tx) => { const doc = await integrationAuthDAL.deleteById(integrationAuth.id, tx); diff --git a/backend/src/services/integration-auth/integration-list.ts b/backend/src/services/integration-auth/integration-list.ts index 334a60e1d..d3cabbcb5 100644 --- a/backend/src/services/integration-auth/integration-list.ts +++ b/backend/src/services/integration-auth/integration-list.ts @@ -67,6 +67,7 @@ export enum IntegrationUrls { QOVERY_API_URL = "https://api.qovery.com", TERRAFORM_CLOUD_API_URL = "https://app.terraform.io", CLOUDFLARE_PAGES_API_URL = "https://api.cloudflare.com", + // eslint-disable-next-line CLOUDFLARE_WORKERS_API_URL = "https://api.cloudflare.com", BITBUCKET_API_URL = "https://api.bitbucket.org", CODEFRESH_API_URL = "https://g.codefresh.io/api", diff --git a/backend/src/services/integration-auth/integration-sync-secret.ts b/backend/src/services/integration-auth/integration-sync-secret.ts index 11ff5c5c2..abb6a7b1e 100644 --- a/backend/src/services/integration-auth/integration-sync-secret.ts +++ b/backend/src/services/integration-auth/integration-sync-secret.ts @@ -1,4 +1,11 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable no-param-reassign,no-await-in-loop */ +// Taken from old code and too much work at present thus disabling the above any rules +// resolve it later: akhilmhdh - TODO + import { CreateSecretCommand, GetSecretValueCommand, @@ -8,6 +15,7 @@ import { } from "@aws-sdk/client-secrets-manager"; import { Octokit } from "@octokit/rest"; import AWS from "aws-sdk"; +import { AxiosError } from "axios"; import sodium from "libsodium-wrappers"; import isEqual from "lodash.isequal"; import { z } from "zod"; @@ -18,9 +26,7 @@ import { BadRequestError } from "@app/lib/errors"; import { Integrations, IntegrationUrls } from "./integration-list"; -const getSecretKeyValuePair = ( - secrets: Record -) => +const getSecretKeyValuePair = (secrets: Record) => Object.keys(secrets).reduce>((prev, key) => { // eslint-disable-next-line prev[key] = secrets?.[key] === null ? null : secrets?.[key]?.value; @@ -87,8 +93,8 @@ const syncSecretsGCPSecretManager = async ({ ...(pageToken ? { pageToken } : {}) }); - const res: GCPSMListSecretsRes = ( - await request.get( + const res = ( + await request.get( `${IntegrationUrls.GCP_SECRET_MANAGER_URL}/v1/projects/${integration.appId}/secrets${filterParam}`, { params, @@ -281,9 +287,7 @@ const syncSecretsAzureKeyVault = async ({ return result; }; - const getAzureKeyVaultSecrets = await paginateAzureKeyVaultSecrets( - `${integration.app}/secrets?api-version=7.3` - ); + const getAzureKeyVaultSecrets = await paginateAzureKeyVaultSecrets(`${integration.app}/secrets?api-version=7.3`); let lastSlashIndex: number; const res = ( @@ -293,14 +297,11 @@ const syncSecretsAzureKeyVault = async ({ lastSlashIndex = getAzureKeyVaultSecret.id.lastIndexOf("/"); } - const azureKeyVaultSecret = await request.get( - `${getAzureKeyVaultSecret.id}?api-version=7.3`, - { - headers: { - Authorization: `Bearer ${accessToken}` - } + const azureKeyVaultSecret = await request.get(`${getAzureKeyVaultSecret.id}?api-version=7.3`, { + headers: { + Authorization: `Bearer ${accessToken}` } - ); + }); return { ...azureKeyVaultSecret.data, @@ -309,7 +310,7 @@ const syncSecretsAzureKeyVault = async ({ }) ) ).reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: secret }), @@ -378,8 +379,9 @@ const syncSecretsAzureKeyVault = async ({ isSecretSet = true; } catch (err) { - const error: any = err; - if (error?.response?.data?.error?.innererror?.code === "ObjectIsDeletedButRecoverable") { + const error = err as AxiosError; + // eslint-disable-next-line + if ((error?.response?.data as any)?.error?.innererror?.code === "ObjectIsDeletedButRecoverable") { await request.post( `${azIntegration.app}/deletedsecrets/${key}/recover?api-version=7.3`, {}, @@ -405,7 +407,7 @@ const syncSecretsAzureKeyVault = async ({ // Sync/push set secrets for await (const setSecret of setSecrets) { const { key, value } = setSecret; - setSecretAzureKeyVault({ + await setSecretAzureKeyVault({ key, value, integration, @@ -425,11 +427,6 @@ const syncSecretsAzureKeyVault = async ({ /** * Sync/push [secrets] to AWS parameter store - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessId - access id for AWS parameter store integration - * @param {String} obj.accessToken - access token for AWS parameter store integration */ const syncSecretsAWSParameterStore = async ({ integration, @@ -463,60 +460,60 @@ const syncSecretsAWSParameterStore = async ({ const parameterList = (await ssm.getParametersByPath(params).promise()).Parameters; - let awsParameterStoreSecretsObj: { - [key: string]: any; - } = {}; - - if (parameterList) { - awsParameterStoreSecretsObj = parameterList.reduce( - (obj: any, secret: any) => ({ + const awsParameterStoreSecretsObj = (parameterList || []) + .filter(({ Name }) => Boolean(Name)) + .reduce( + (obj, secret) => ({ ...obj, - [secret.Name.substring((integration.path as string).length)]: secret + [(secret.Name as string).substring((integration.path as string).length)]: secret }), - {} + {} as Record ); - } // Identify secrets to create - Object.keys(secrets).map(async (key) => { - if (!(key in awsParameterStoreSecretsObj)) { - // case: secret does not exist in AWS parameter store - // -> create secret - await ssm - .putParameter({ - Name: `${integration.path}${key}`, - Type: "SecureString", - Value: secrets[key].value, - Overwrite: true - }) - .promise(); - // case: secret exists in AWS parameter store - } else if (awsParameterStoreSecretsObj[key].Value !== secrets[key].value) { - // case: secret value doesn't match one in AWS parameter store - // -> update secret - await ssm - .putParameter({ - Name: `${integration.path}${key}`, - Type: "SecureString", - Value: secrets[key].value, - Overwrite: true - }) - .promise(); - } - }); + await Promise.all( + Object.keys(secrets).map(async (key) => { + if (!(key in awsParameterStoreSecretsObj)) { + // case: secret does not exist in AWS parameter store + // -> create secret + await ssm + .putParameter({ + Name: `${integration.path}${key}`, + Type: "SecureString", + Value: secrets[key].value, + Overwrite: true + }) + .promise(); + // case: secret exists in AWS parameter store + } else if (awsParameterStoreSecretsObj[key].Value !== secrets[key].value) { + // case: secret value doesn't match one in AWS parameter store + // -> update secret + await ssm + .putParameter({ + Name: `${integration.path}${key}`, + Type: "SecureString", + Value: secrets[key].value, + Overwrite: true + }) + .promise(); + } + }) + ); // Identify secrets to delete - Object.keys(awsParameterStoreSecretsObj).map(async (key) => { - if (!(key in secrets)) { - // case: - // -> delete secret - await ssm - .deleteParameter({ - Name: awsParameterStoreSecretsObj[key].Name - }) - .promise(); - } - }); + await Promise.all( + Object.keys(awsParameterStoreSecretsObj).map(async (key) => { + if (!(key in secrets)) { + // case: + // -> delete secret + await ssm + .deleteParameter({ + Name: awsParameterStoreSecretsObj[key].Name as string + }) + .promise(); + } + }) + ); AWS.config.update({ region: undefined, @@ -564,7 +561,7 @@ const syncSecretsAWSSecretManager = async ({ }) ); - let awsSecretManagerSecretObj: { [key: string]: any } = {}; + let awsSecretManagerSecretObj: { [key: string]: AWS.SecretsManager } = {}; if (awsSecretManagerSecret?.SecretString) { awsSecretManagerSecretObj = JSON.parse(awsSecretManagerSecret.SecretString); @@ -680,25 +677,24 @@ const syncSecretsVercel = async ({ : {}) }; - const vercelSecrets: VercelSecret[] = ( - await request.get(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env`, { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + const vercelSecrets = ( + await request.get<{ envs: VercelSecret[] }>( + `${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env`, + { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - }) - ).data.envs.filter((secret: VercelSecret) => { + ) + ).data.envs.filter((secret) => { if (!secret.target.includes(integration.targetEnvironment as string)) { // case: secret does not have the same target environment return false; } - if ( - integration.targetEnvironment === "preview" && - secret.gitBranch && - integration.path !== secret.gitBranch - ) { + if (integration.targetEnvironment === "preview" && secret.gitBranch && integration.path !== secret.gitBranch) { // case: secret on preview environment does not have same target git branch return false; } @@ -712,16 +708,13 @@ const syncSecretsVercel = async ({ if (vercelSecret.type === "encrypted") { // case: secret is encrypted -> need to decrypt const decryptedSecret = ( - await request.get( - `${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${vercelSecret.id}`, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } + await request.get(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${vercelSecret.id}`, { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ) + }) ).data; res[vercelSecret.key] = decryptedSecret; @@ -791,47 +784,36 @@ const syncSecretsVercel = async ({ // Sync/push new secrets if (newSecrets.length > 0) { - await request.post( - `${IntegrationUrls.VERCEL_API_URL}/v10/projects/${integration.app}/env`, - newSecrets, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } + await request.post(`${IntegrationUrls.VERCEL_API_URL}/v10/projects/${integration.app}/env`, newSecrets, { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ); + }); } for await (const secret of updateSecrets) { if (secret.type !== "sensitive") { const { id, ...updatedSecret } = secret; - await request.patch( - `${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${id}`, - updatedSecret, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" - } - } - ); - } - } - - for await (const secret of deleteSecrets) { - await request.delete( - `${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, - { + await request.patch(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${id}`, updatedSecret, { params, headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" } + }); + } + } + + for await (const secret of deleteSecrets) { + await request.delete(`${IntegrationUrls.VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" } - ); + }); } }; @@ -866,7 +848,7 @@ const syncSecretsNetlify = async ({ }); const res = ( - await request.get( + await request.get( `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`, { params: getParams, @@ -877,11 +859,11 @@ const syncSecretsNetlify = async ({ } ) ).data.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: secret }), - {} + {} as Record ); const newSecrets: NetlifySecret[] = []; // createEnvVars @@ -905,16 +887,16 @@ const syncSecretsNetlify = async ({ } else { // case: Infisical secret exists in Netlify const contexts = res[key].values.reduce( - (obj: any, value: NetlifyValue) => ({ + (obj, value) => ({ ...obj, [value.context]: value }), - {} + {} as Record ); if ((integration.targetEnvironment as string) in contexts) { // case: Netlify secret value exists in integration context - if (secrets[key] !== contexts[integration.targetEnvironment as string].value) { + if (secrets[key].value !== contexts[integration.targetEnvironment as string].value) { // case: Infisical and Netlify secret values are different // -> update Netlify secret context and value updateSecrets.push({ @@ -994,62 +976,63 @@ const syncSecretsNetlify = async ({ } if (updateSecrets.length > 0) { - updateSecrets.forEach(async (secret: NetlifySecret) => { - await request.patch( - `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`, - { - context: secret.values[0].context, - value: secret.values[0].value - }, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + await Promise.all( + updateSecrets.map(async (secret: NetlifySecret) => { + await request.patch( + `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`, + { + context: secret.values[0].context, + value: secret.values[0].value + }, + { + params: syncParams, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - } - ); - }); + ); + }) + ); } if (deleteSecrets.length > 0) { - deleteSecrets.forEach(async (key: string) => { - await request.delete( - `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + await Promise.all( + deleteSecrets.map(async (key: string) => { + await request.delete( + `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`, + { + params: syncParams, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - } - ); - }); + ); + }) + ); } if (deleteSecretValues.length > 0) { - deleteSecretValues.forEach(async (secret: NetlifySecret) => { - await request.delete( - `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}`, - "Accept-Encoding": "application/json" + await Promise.all( + deleteSecretValues.map(async (secret: NetlifySecret) => { + await request.delete( + `${IntegrationUrls.NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`, + { + params: syncParams, + headers: { + Authorization: `Bearer ${accessToken}`, + "Accept-Encoding": "application/json" + } } - } - ); - }); + ); + }) + ); } }; /** * Sync/push [secrets] to GitHub repo with name [integration.app] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {TIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for GitHub integration */ const syncSecretsGitHub = async ({ integration, @@ -1096,7 +1079,7 @@ const syncSecretsGitHub = async ({ repo: integration.app as string }) ).data.secrets.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.name]: secret }), @@ -1121,46 +1104,46 @@ const syncSecretsGitHub = async ({ {} ); - Object.keys(encryptedSecrets).map(async (key) => { - if (!(key in secrets)) { - await octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", { - owner: integration.owner as string, - repo: integration.app as string, - secret_name: key + await Promise.all( + Object.keys(encryptedSecrets).map(async (key) => { + if (!(key in secrets)) { + return octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", { + owner: integration.owner as string, + repo: integration.app as string, + secret_name: key + }); + } + }) + ); + + await Promise.all( + Object.keys(secrets).map((key) => { + // let encryptedSecret; + return sodium.ready.then(async () => { + // convert secret & base64 key to Uint8Array. + const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL); + const binsec = sodium.from_string(secrets[key].value); + + // encrypt secret using libsodium + const encBytes = sodium.crypto_box_seal(binsec, binkey); + + // convert encrypted Uint8Array to base64 + const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL); + + await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", { + owner: integration.owner as string, + repo: integration.app as string, + secret_name: key, + encrypted_value: encryptedSecret, + key_id: repoPublicKey.key_id + }); }); - } - }); - - Object.keys(secrets).forEach((key) => { - // let encryptedSecret; - sodium.ready.then(async () => { - // convert secret & base64 key to Uint8Array. - const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL); - const binsec = sodium.from_string(secrets[key].value); - - // encrypt secret using libsodium - const encBytes = sodium.crypto_box_seal(binsec, binkey); - - // convert encrypted Uint8Array to base64 - const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL); - - await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", { - owner: integration.owner as string, - repo: integration.app as string, - secret_name: key, - encrypted_value: encryptedSecret, - key_id: repoPublicKey.key_id - }); - }); - }); + }) + ); }; /** * Sync/push [secrets] to Render service with id [integration.appId] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Render integration */ const syncSecretsRender = async ({ integration, @@ -1188,10 +1171,6 @@ const syncSecretsRender = async ({ /** * Sync/push [secrets] to Laravel Forge sites with id [integration.appId] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Laravel Forge integration */ const syncSecretsLaravelForge = async ({ integration, @@ -1204,13 +1183,11 @@ const syncSecretsLaravelForge = async ({ accessId: string | null; accessToken: string; }) => { - function transformObjectToString(obj: any) { + function transformObjectToString(obj: Record) { let result = ""; - for (const key in obj) { - if (obj.hasOwnPropery(key)) { - result += `${key}=${obj[key].value}\n`; - } - } + Object.keys(obj).forEach((key) => { + result += `${key}=${obj[key].value}\n`; + }); return result; } @@ -1231,10 +1208,6 @@ const syncSecretsLaravelForge = async ({ /** * Sync/push [secrets] to Railway project with id [integration.appId] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Railway integration */ const syncSecretsRailway = async ({ integration, @@ -1279,10 +1252,6 @@ const syncSecretsRailway = async ({ /** * Sync/push [secrets] to Fly.io app - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Render integration */ const syncSecretsFlyio = async ({ integration, @@ -1354,7 +1323,7 @@ const syncSecretsFlyio = async ({ }`; const getSecretsRes = ( - await request.post( + await request.post<{ data: { app: { secrets: FlyioSecret[] } } }>( IntegrationUrls.FLYIO_API_URL, { query: GetSecrets, @@ -1372,9 +1341,7 @@ const syncSecretsFlyio = async ({ ) ).data.data.app.secrets; - const deleteSecretsKeys = getSecretsRes - .filter((secret: FlyioSecret) => !(secret.name in secrets)) - .map((secret: FlyioSecret) => secret.name); + const deleteSecretsKeys = getSecretsRes.filter((secret) => !(secret.name in secrets)).map((secret) => secret.name); // unset (delete) secrets const DeleteSecrets = `mutation($input: UnsetSecretsInput!) { @@ -1460,7 +1427,7 @@ const syncSecretsCircleCI = async ({ // get secrets from CircleCI const getSecretsRes = ( - await request.get( + await request.get<{ items: { name: string }[] }>( `${IntegrationUrls.CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar`, { headers: { @@ -1472,19 +1439,21 @@ const syncSecretsCircleCI = async ({ ).data?.items; // delete secrets from CircleCI - getSecretsRes.forEach(async (sec: any) => { - if (!(sec.name in secrets)) { - await request.delete( - `${IntegrationUrls.CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar/${sec.name}`, - { - headers: { - "Circle-Token": accessToken, - "Content-Type": "application/json" + await Promise.all( + getSecretsRes.map(async (sec) => { + if (!(sec.name in secrets)) { + return request.delete( + `${IntegrationUrls.CIRCLECI_API_URL}/v2/project/${slug}/${integration.app}/envvar/${sec.name}`, + { + headers: { + "Circle-Token": accessToken, + "Content-Type": "application/json" + } } - } - ); - } - }); + ); + } + }) + ); }; /** @@ -1501,21 +1470,20 @@ const syncSecretsTravisCI = async ({ }) => { // get secrets from travis-ci const getSecretsRes = ( - await request.get( - `${IntegrationUrls.TRAVISCI_API_URL}/settings/env_vars?repository_id=${integration.appId}`, - { - headers: { - Authorization: `token ${accessToken}`, - "Accept-Encoding": "application/json" - } + await request.get<{ + env_vars: { name: string; value: string; repository_id: string; id: string }[]; + }>(`${IntegrationUrls.TRAVISCI_API_URL}/settings/env_vars?repository_id=${integration.appId}`, { + headers: { + Authorization: `token ${accessToken}`, + "Accept-Encoding": "application/json" } - ) + }) ).data?.env_vars.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.name]: secret }), - {} + {} as Record ); // add secrets @@ -1598,9 +1566,7 @@ const syncSecretsGitLab = async ({ environment_scope: string; } - const gitLabApiUrl = integrationAuth.url - ? `${integrationAuth.url}/api` - : IntegrationUrls.GITLAB_API_URL; + const gitLabApiUrl = integrationAuth.url ? `${integrationAuth.url}/api` : IntegrationUrls.GITLAB_API_URL; const getAllEnvVariables = async (integrationAppId: string, accToken: string) => { const headers = { @@ -1610,14 +1576,13 @@ const syncSecretsGitLab = async ({ }; let allEnvVariables: GitLabSecret[] = []; - let url: string | null = - `${gitLabApiUrl}/v4/projects/${integrationAppId}/variables?per_page=100`; + let url: string | null = `${gitLabApiUrl}/v4/projects/${integrationAppId}/variables?per_page=100`; while (url) { - const response: any = await request.get(url, { headers }); + const response = await request.get(url, { headers }); allEnvVariables = [...allEnvVariables, ...response.data]; - const linkHeader = response.headers.link; + const linkHeader = response.headers.link as string; const nextLink = linkHeader?.split(",").find((part: string) => part.includes('rel="next"')); if (nextLink) { @@ -1649,7 +1614,7 @@ const syncSecretsGitLab = async ({ }); for await (const key of Object.keys(secrets)) { - const existingSecret = getSecretsRes.find((s: any) => s.key === key); + const existingSecret = getSecretsRes.find((s) => s.key === key); if (!existingSecret) { await request.post( `${gitLabApiUrl}/v4/projects/${integration?.appId}/variables`, @@ -1714,7 +1679,7 @@ const syncSecretsSupabase = async ({ secrets: Record; accessToken: string; }) => { - const { data: getSecretsRes } = await request.get( + const { data: getSecretsRes } = await request.get<{ name: string; value: string }[]>( `${IntegrationUrls.SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`, { headers: { @@ -1741,33 +1706,25 @@ const syncSecretsSupabase = async ({ } ); - const secretsToDelete: any = []; - getSecretsRes?.forEach((secretObj: any) => { + const secretsToDelete = getSecretsRes?.flatMap((secretObj) => { if ( !(secretObj.name in secrets) && // supbase reserved secret ref: https://supabase.com/docs/guides/functions/secrets#default-secrets - ![ - "SUPABASE_ANON_KEY", - "SUPABASE_SERVICE_ROLE_KEY", - "SUPABASE_DB_URL", - "SUPABASE_URL" - ].includes(secretObj.name) + !["SUPABASE_ANON_KEY", "SUPABASE_SERVICE_ROLE_KEY", "SUPABASE_DB_URL", "SUPABASE_URL"].includes(secretObj.name) ) { - secretsToDelete.push(secretObj.name); + return secretObj.name; } + return []; }); - await request.delete( - `${IntegrationUrls.SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - "Accept-Encoding": "application/json" - }, - data: secretsToDelete - } - ); + await request.delete(`${IntegrationUrls.SUPABASE_API_URL}/v1/projects/${integration.appId}/secrets`, { + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + "Accept-Encoding": "application/json" + }, + data: secretsToDelete + }); }; /** @@ -1788,7 +1745,7 @@ const syncSecretsCheckly = async ({ // sync secrets to checkly group envars let getGroupSecretsRes = ( - await request.get( + await request.get<{ environmentVariables: { key: string; value: string }[] }>( `${IntegrationUrls.CHECKLY_API_URL}/v1/check-groups/${integration.targetServiceId}`, { headers: { @@ -1799,11 +1756,11 @@ const syncSecretsCheckly = async ({ } ) ).data.environmentVariables.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: secret.value }), - {} + {} as Record ); getGroupSecretsRes = Object.keys(getGroupSecretsRes).reduce( @@ -1846,7 +1803,7 @@ const syncSecretsCheckly = async ({ // sync secrets to checkly global envars let getSecretsRes = ( - await request.get(`${IntegrationUrls.CHECKLY_API_URL}/v1/variables`, { + await request.get<{ key: string; value: string }[]>(`${IntegrationUrls.CHECKLY_API_URL}/v1/variables`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json", @@ -1854,11 +1811,11 @@ const syncSecretsCheckly = async ({ } }) ).data.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: secret.value }), - {} + {} as Record ); getSecretsRes = Object.keys(getSecretsRes).reduce( @@ -1901,7 +1858,7 @@ const syncSecretsCheckly = async ({ ); // case: secret exists in checkly // -> update/set secret - } else if (secrets[key] !== getSecretsRes[key]) { + } else if (secrets[key].value !== getSecretsRes[key]) { await request.put( `${IntegrationUrls.CHECKLY_API_URL}/v1/variables/${key}`, { @@ -1951,7 +1908,7 @@ const syncSecretsQovery = async ({ accessToken: string; }) => { const getSecretsRes = ( - await request.get( + await request.get<{ results: { id: string; value: string; key: string }[] }>( `${IntegrationUrls.QOVERY_API_URL}/${integration.scope}/${integration.appId}/environmentVariable`, { headers: { @@ -1961,11 +1918,11 @@ const syncSecretsQovery = async ({ } ) ).data.results.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: { id: secret.id, value: secret.value } }), - {} + {} as Record ); // add secrets @@ -2042,7 +1999,7 @@ const syncSecretsTerraformCloud = async ({ }) => { // get secrets from Terraform Cloud const getSecretsRes = ( - await request.get( + await request.get<{ data: { attributes: { key: string; value: string }; id: string }[] }>( `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars`, { headers: { @@ -2052,11 +2009,11 @@ const syncSecretsTerraformCloud = async ({ } ) ).data.data.reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.attributes.key]: secret }), - {} + {} as Record ); // create or update secrets on Terraform Cloud @@ -2138,7 +2095,7 @@ const syncSecretsTeamCity = async ({ }: { integrationAuth: TIntegrationAuths; integration: TIntegrations; - secrets: any; + secrets: Record; accessToken: string; }) => { interface TeamCitySecret { @@ -2171,13 +2128,16 @@ const syncSecretsTeamCity = async ({ ) ).data.property .filter((parameter) => !parameter.inherited) - .reduce((obj: any, secret: TeamCitySecret) => { - const secretName = secret.name.replace(/^env\./, ""); - return { - ...obj, - [secretName]: secret.value - }; - }, {}); + .reduce( + (obj, secret) => { + const secretName = secret.name.replace(/^env\./, ""); + return { + ...obj, + [secretName]: secret.value + }; + }, + {} as Record + ); for await (const key of Object.keys(secrets)) { if (!(key in res) || (key in res && secrets[key].value !== res[key])) { @@ -2216,7 +2176,7 @@ const syncSecretsTeamCity = async ({ } else { // case: sync to TeamCity project const res = ( - await request.get( + await request.get<{ property: TeamCitySecret[] }>( `${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters`, { headers: { @@ -2225,16 +2185,19 @@ const syncSecretsTeamCity = async ({ } } ) - ).data.property.reduce((obj: any, secret: TeamCitySecret) => { - const secretName = secret.name.replace(/^env\./, ""); - return { - ...obj, - [secretName]: secret.value - }; - }, {}); + ).data.property.reduce( + (obj, secret) => { + const secretName = secret.name.replace(/^env\./, ""); + return { + ...obj, + [secretName]: secret.value + }; + }, + {} as Record + ); for await (const key of Object.keys(secrets)) { - if (!(key in res) || (key in res && secrets[key] !== res[key])) { + if (!(key in res) || (key in res && secrets[key].value !== res[key])) { // case: secret does not exist in TeamCity or secret value has changed // -> create/update secret await request.post( @@ -2256,15 +2219,12 @@ const syncSecretsTeamCity = async ({ for await (const key of Object.keys(res)) { if (!(key in secrets)) { // delete secret - await request.delete( - `${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters/env.${key}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } + await request.delete(`${integrationAuth.url}/app/rest/projects/id:${integration.appId}/parameters/env.${key}`, { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" } - ); + }); } } } @@ -2347,33 +2307,23 @@ const syncSecretsCloudflarePages = async ({ }) => { // get secrets from cloudflare pages const getSecretsRes = ( - await request.get( - `${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } + await request.get<{ + result: { deployment_configs: Record }> }; + }>(`${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`, { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" } - ) + }) ).data.result.deployment_configs[integration.targetEnvironment as string].env_vars; // copy the secrets object, so we can set deleted keys to null - const secretsObj: any = getSecretKeyValuePair(secrets); - - for (const [key, val] of Object.entries(secretsObj)) { - secretsObj[key] = { type: "secret_text", value: val }; - } - - if (getSecretsRes) { - for await (const key of Object.keys(getSecretsRes)) { - if (!(key in secrets)) { - // case: secret does not exist in infisical - // -> delete secret from cloudflare pages - secretsObj[key] = null; - } - } - } + const secretsObj = Object.fromEntries( + Object.entries(getSecretKeyValuePair(secrets)).map(([key, val]) => [ + key, + key in Object.keys(getSecretsRes) ? { type: "secret_text", value: val } : null + ]) + ); const data = { deployment_configs: { @@ -2411,7 +2361,7 @@ const syncSecretsCloudflareWorkers = async ({ }) => { // get secrets from cloudflare workers const getSecretsRes = ( - await request.get( + await request.get<{ result: { name: string }[] }>( `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets`, { headers: { @@ -2422,33 +2372,33 @@ const syncSecretsCloudflareWorkers = async ({ ) ).data.result; - const secretsObj: any = getSecretKeyValuePair(secrets); - - for (const [key, val] of Object.entries(secretsObj)) { - secretsObj[key] = { type: "secret_text", value: val }; - } + const secretsObj = Object.fromEntries( + Object.entries(getSecretKeyValuePair(secrets)).map(([key, val]) => [key, { type: "secret_text", value: val }]) + ); // get deleted secrets list const deletedSecretKeys: string[] = []; if (getSecretsRes) { - getSecretsRes.forEach((secretRes: any) => { + getSecretsRes.forEach((secretRes) => { if (!Object.keys(secrets).includes(secretRes.name)) { deletedSecretKeys.push(secretRes.name); } }); } - deletedSecretKeys.forEach(async (secretKey) => { - await request.delete( - `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets/${secretKey}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" + await Promise.all( + deletedSecretKeys.map(async (secretKey) => { + return request.delete( + `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets/${secretKey}`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" + } } - } - ); - }); + ); + }) + ); interface ConvertedSecret { name: string; @@ -2463,35 +2413,30 @@ const syncSecretsCloudflareWorkers = async ({ }; } - const data: ConvertedSecret[] = Object.entries(secretsObj as SecretsObj).map( - ([name, secret]) => ({ - name, - text: secret.value, - type: "secret_text" + const data: ConvertedSecret[] = Object.entries(secretsObj as SecretsObj).map(([name, secret]) => ({ + name, + text: secret.value, + type: "secret_text" + })); + + await Promise.all( + data.map(async (secret) => { + return request.put( + `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets`, + secret, + { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" + } + } + ); }) ); - - data.forEach(async (secret) => { - await request.put( - `${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accessId}/workers/scripts/${integration.app}/secrets`, - secret, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } - } - ); - }); }; /** * Sync/push [secrets] to BitBucket repo with name [integration.app] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {TIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for BitBucket integration */ const syncSecretsBitBucket = async ({ integration, @@ -2700,18 +2645,18 @@ const syncSecretsWindmill = async ({ // get secrets stored in windmill workspace const res = ( - await request.get(`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/list`, { + await request.get(`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/list`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" } }) ).data.reduce( - (obj: any, secret: WindmillSecret) => ({ + (obj, secret) => ({ ...obj, [secret.path]: secret }), - {} + {} as Record ); // eslint-disable-next-line @@ -2778,11 +2723,6 @@ const syncSecretsWindmill = async ({ /** * Sync/push [secrets] to Cloud66 application with name [integration.app] - * @param {Object} obj - * @param {TIntegrations} obj.integration - integration details - * @param {TIntegrationAuth} obj.integrationAuth - integration auth details - * @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values) - * @param {String} obj.accessToken - access token for Cloud66 integration */ const syncSecretsCloud66 = async ({ integration, @@ -2802,12 +2742,12 @@ const syncSecretsCloud66 = async ({ updated_at: string; is_password: boolean; is_generated: boolean; - history: any[]; + history: unknown[]; } // get all current secrets const res = ( - await request.get( + await request.get<{ response: Cloud66Secret[] }>( `${IntegrationUrls.CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments`, { headers: { @@ -2817,9 +2757,9 @@ const syncSecretsCloud66 = async ({ } ) ).data.response - .filter((secret: Cloud66Secret) => !secret.readonly || !secret.is_generated) + .filter((secret) => !secret.readonly || !secret.is_generated) .reduce( - (obj: any, secret: any) => ({ + (obj, secret) => ({ ...obj, [secret.key]: secret }), @@ -2863,15 +2803,12 @@ const syncSecretsCloud66 = async ({ for await (const key of Object.keys(res)) { if (!(key in secrets)) { // delete secret - await request.delete( - `${IntegrationUrls.CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments/${key}`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - Accept: "application/json" - } + await request.delete(`${IntegrationUrls.CLOUD_66_API_URL}/3/stacks/${integration.appId}/environments/${key}`, { + headers: { + Authorization: `Bearer ${accessToken}`, + Accept: "application/json" } - ); + }); } } }; @@ -2925,8 +2862,7 @@ const syncSecretsHasuraCloud = async ({ const res = await request.post( IntegrationUrls.HASURA_CLOUD_API_URL, { - query: - "query MyQuery($tenantId: uuid!) { getTenantEnv(tenantId: $tenantId) { hash envVars } }", + query: "query MyQuery($tenantId: uuid!) { getTenantEnv(tenantId: $tenantId) { hash envVars } }", variables: { tenantId: integration.appId } diff --git a/backend/src/services/integration-auth/integration-team.ts b/backend/src/services/integration-auth/integration-team.ts index 287e7cc7a..81ef9b70c 100644 --- a/backend/src/services/integration-auth/integration-team.ts +++ b/backend/src/services/integration-auth/integration-team.ts @@ -1,7 +1,7 @@ import { request } from "@app/lib/config/request"; import { BadRequestError } from "@app/lib/errors"; -import { Integrations,IntegrationUrls } from "./integration-list"; +import { Integrations, IntegrationUrls } from "./integration-list"; type Team = { name: string; @@ -12,7 +12,7 @@ const getTeamsGitLab = async ({ url, accessToken }: { url: string; accessToken: let teams: Team[] = []; const res = ( - await request.get(`${gitLabApiUrl}/v4/groups`, { + await request.get<{ name: string; id: string }[]>(`${gitLabApiUrl}/v4/groups`, { headers: { Authorization: `Bearer ${accessToken}`, "Accept-Encoding": "application/json" @@ -20,7 +20,7 @@ const getTeamsGitLab = async ({ url, accessToken }: { url: string; accessToken: }) ).data; - teams = res.map((t: any) => ({ + teams = res.map((t) => ({ name: t.name, teamId: t.id })); diff --git a/backend/src/services/integration-auth/integration-token.ts b/backend/src/services/integration-auth/integration-token.ts index f67b13a92..0907bd074 100644 --- a/backend/src/services/integration-auth/integration-token.ts +++ b/backend/src/services/integration-auth/integration-token.ts @@ -80,8 +80,8 @@ const exchangeCodeGCP = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeGCPResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.GCP_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -108,8 +108,8 @@ const exchangeCodeAzure = async ({ code }: { code: string }) => { if (!appCfg.CLIENT_ID_AZURE || !appCfg.CLIENT_SECRET_AZURE) { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeAzureResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.AZURE_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -118,7 +118,7 @@ const exchangeCodeAzure = async ({ code }: { code: string }) => { client_id: appCfg.CLIENT_ID_AZURE, client_secret: appCfg.CLIENT_SECRET_AZURE, redirect_uri: `${appCfg.SITE_URL}/integrations/azure-key-vault/oauth2/callback` - } as any) + }) ) ).data; @@ -138,8 +138,8 @@ const exchangeCodeHeroku = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeHerokuResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.HEROKU_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -160,12 +160,6 @@ const exchangeCodeHeroku = async ({ code }: { code: string }) => { /** * Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel * code-token exchange - * @param {Object} obj1 - * @param {Object} obj1.code - code for code-token exchange - * @returns {Object} obj2 - * @returns {String} obj2.accessToken - access token for Heroku API - * @returns {String} obj2.refreshToken - refresh token for Heroku API - * @returns {Date} obj2.accessExpiresAt - date of expiration for access token */ const exchangeCodeVercel = async ({ code }: { code: string }) => { const appCfg = getConfig(); @@ -173,15 +167,15 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeVercelResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.VERCEL_TOKEN_URL, new URLSearchParams({ code, client_id: appCfg.CLIENT_ID_VERCEL, client_secret: appCfg.CLIENT_SECRET_VERCEL, redirect_uri: `${appCfg.SITE_URL}/integrations/vercel/oauth2/callback` - } as any) + }) ) ).data; @@ -196,12 +190,6 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => { /** * Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel * code-token exchange - * @param {Object} obj1 - * @param {Object} obj1.code - code for code-token exchange - * @returns {Object} obj2 - * @returns {String} obj2.accessToken - access token for Heroku API - * @returns {String} obj2.refreshToken - refresh token for Heroku API - * @returns {Date} obj2.accessExpiresAt - date of expiration for access token */ const exchangeCodeNetlify = async ({ code }: { code: string }) => { const appCfg = getConfig(); @@ -209,8 +197,8 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeNetlifyResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.NETLIFY_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -218,7 +206,7 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => { client_id: appCfg.CLIENT_ID_NETLIFY, client_secret: appCfg.CLIENT_SECRET_NETLIFY, redirect_uri: `${appCfg.SITE_URL}/integrations/netlify/oauth2/callback` - } as any) + }) ) ).data; @@ -230,7 +218,7 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => { // }); const res3 = ( - await request.get("https://api.netlify.com/api/v1/accounts", { + await request.get>("https://api.netlify.com/api/v1/accounts", { headers: { Authorization: `Bearer ${res.access_token}` } @@ -252,8 +240,8 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeGithubResponse = ( - await request.get(IntegrationUrls.GITHUB_TOKEN_URL, { + const res = ( + await request.get(IntegrationUrls.GITHUB_TOKEN_URL, { params: { client_id: appCfg.CLIENT_ID_GITHUB, client_secret: appCfg.CLIENT_SECRET_GITHUB, @@ -285,8 +273,8 @@ const exchangeCodeGitlab = async ({ code, url }: { code: string; url?: string }) throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeGitlabResponse = ( - await request.post( + const res = ( + await request.post( url ? `${url}/oauth/token` : IntegrationUrls.GITLAB_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -294,7 +282,7 @@ const exchangeCodeGitlab = async ({ code, url }: { code: string; url?: string }) client_id: appCfg.CLIENT_ID_GITLAB, client_secret: appCfg.CLIENT_SECRET_GITLAB, redirect_uri: `${appCfg.SITE_URL}/integrations/gitlab/oauth2/callback` - } as any), + }), { headers: { "Accept-Encoding": "application/json" @@ -324,8 +312,8 @@ const exchangeCodeBitBucket = async ({ code }: { code: string }) => { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const res: ExchangeCodeBitBucketResponse = ( - await request.post( + const res = ( + await request.post( IntegrationUrls.BITBUCKET_TOKEN_URL, new URLSearchParams({ grant_type: "authorization_code", @@ -333,7 +321,7 @@ const exchangeCodeBitBucket = async ({ code }: { code: string }) => { client_id: appCfg.CLIENT_ID_BITBUCKET, client_secret: appCfg.CLIENT_SECRET_BITBUCKET, redirect_uri: `${appCfg.SITE_URL}/integrations/bitbucket/oauth2/callback` - } as any), + }), { headers: { "Accept-Encoding": "application/json" @@ -530,13 +518,7 @@ const exchangeRefreshHeroku = async ({ refreshToken }: { refreshToken: string }) * @param {String} obj.refreshToken - refresh token to use to get new access token for GitLab * @returns */ -const exchangeRefreshGitLab = async ({ - refreshToken, - url -}: { - url?: string | null; - refreshToken: string; -}) => { +const exchangeRefreshGitLab = async ({ refreshToken, url }: { url?: string | null; refreshToken: string }) => { const accessExpiresAt = new Date(); const appCfg = getConfig(); if (!appCfg.CLIENT_ID_GITLAB || !appCfg.CLIENT_SECRET_GITLAB) { @@ -593,7 +575,7 @@ const exchangeRefreshBitBucket = async ({ refreshToken }: { refreshToken: string client_id: appCfg.CLIENT_ID_BITBUCKET, client_secret: appCfg.CLIENT_SECRET_BITBUCKET, redirect_uri: `${appCfg.SITE_URL}/integrations/bitbucket/oauth2/callback` - } as any), + }), { headers: { "Accept-Encoding": "application/json" @@ -624,7 +606,11 @@ const exchangeRefreshGCPSecretManager = async ({ const accessExpiresAt = new Date(); if (metadata?.authMethod === "serviceAccount") { - const serviceAccount = JSON.parse(refreshToken); + const serviceAccount = JSON.parse(refreshToken) as { + client_email: string; + token_uri: string; + private_key: string; + }; const payload = { iss: serviceAccount.client_email, @@ -636,19 +622,18 @@ const exchangeRefreshGCPSecretManager = async ({ const token = jwt.sign(payload, serviceAccount.private_key, { algorithm: "RS256" }); - const { data }: { data: ServiceAccountAccessTokenGCPSecretManagerResponse } = - await request.post( - IntegrationUrls.GCP_TOKEN_URL, - new URLSearchParams({ - grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", - assertion: token - }).toString(), - { - headers: { - "Content-Type": "application/x-www-form-urlencoded" - } + const { data }: { data: ServiceAccountAccessTokenGCPSecretManagerResponse } = await request.post( + IntegrationUrls.GCP_TOKEN_URL, + new URLSearchParams({ + grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", + assertion: token + }).toString(), + { + headers: { + "Content-Type": "application/x-www-form-urlencoded" } - ); + } + ); accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in); @@ -663,14 +648,14 @@ const exchangeRefreshGCPSecretManager = async ({ if (!appCfg.CLIENT_SECRET_GCP_SECRET_MANAGER || !appCfg.CLIENT_ID_GCP_SECRET_MANAGER) { throw new BadRequestError({ message: "Missing client id and client secret" }); } - const { data }: { data: RefreshTokenGCPSecretManagerResponse } = await request.post( + const { data } = await request.post( IntegrationUrls.GCP_TOKEN_URL, new URLSearchParams({ client_id: appCfg.CLIENT_ID_GCP_SECRET_MANAGER, client_secret: appCfg.CLIENT_SECRET_GCP_SECRET_MANAGER, refresh_token: refreshToken, grant_type: "refresh_token" - } as any) + }) ); accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in); diff --git a/backend/src/services/integration/integration-dal.ts b/backend/src/services/integration/integration-dal.ts index 446af0bea..bada253c5 100644 --- a/backend/src/services/integration/integration-dal.ts +++ b/backend/src/services/integration/integration-dal.ts @@ -66,11 +66,7 @@ export const integrationDALFactory = (db: TDbClient) => { try { const integrations = await (tx || db)(TableName.Integration) .where(`${TableName.Environment}.projectId`, projectId) - .join( - TableName.Environment, - `${TableName.Integration}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.Integration}.envId`, `${TableName.Environment}.id`) .select(db.ref("name").withSchema(TableName.Environment).as("envName")) .select(db.ref("slug").withSchema(TableName.Environment).as("envSlug")) .select(db.ref("id").withSchema(TableName.Environment).as("envId")) @@ -99,11 +95,7 @@ export const integrationDALFactory = (db: TDbClient) => { .where("isActive", true) .where(`${TableName.Environment}.slug`, environment) .join(TableName.Environment, `${TableName.Integration}.envId`, `${TableName.Environment}.id`) - .join( - TableName.IntegrationAuth, - `${TableName.IntegrationAuth}.id`, - `${TableName.Integration}.integrationAuthId` - ) + .join(TableName.IntegrationAuth, `${TableName.IntegrationAuth}.id`, `${TableName.Integration}.integrationAuthId`) .select(db.ref("name").withSchema(TableName.Environment).as("envName")) .select(db.ref("slug").withSchema(TableName.Environment).as("envSlug")) .select(db.ref("id").withSchema(TableName.Environment).as("envId")) @@ -119,10 +111,7 @@ export const integrationDALFactory = (db: TDbClient) => { db.ref("refreshCiphertext").withSchema(TableName.IntegrationAuth).as("refreshCiphertextAu"), db.ref("refreshIV").withSchema(TableName.IntegrationAuth).as("refreshIVAu"), db.ref("refreshTag").withSchema(TableName.IntegrationAuth).as("refreshTagAu"), - db - .ref("accessIdCiphertext") - .withSchema(TableName.IntegrationAuth) - .as("accessIdCiphertextAu"), + db.ref("accessIdCiphertext").withSchema(TableName.IntegrationAuth).as("accessIdCiphertextAu"), db.ref("accessIdIV").withSchema(TableName.IntegrationAuth).as("accessIdIVAu"), db.ref("accessIdTag").withSchema(TableName.IntegrationAuth).as("accessIdTagAu"), db.ref("accessIV").withSchema(TableName.IntegrationAuth).as("accessIVAu"), diff --git a/backend/src/services/integration/integration-service.ts b/backend/src/services/integration/integration-service.ts index ba93b37fa..b7f74966e 100644 --- a/backend/src/services/integration/integration-service.ts +++ b/backend/src/services/integration/integration-service.ts @@ -1,10 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { TProjectPermission } from "@app/lib/types"; @@ -12,11 +9,7 @@ import { TIntegrationAuthDALFactory } from "../integration-auth/integration-auth import { TSecretQueueFactory } from "../secret/secret-queue"; import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TIntegrationDALFactory } from "./integration-dal"; -import { - TCreateIntegrationDTO, - TDeleteIntegrationDTO, - TUpdateIntegrationDTO -} from "./integration-types"; +import { TCreateIntegrationDTO, TDeleteIntegrationDTO, TUpdateIntegrationDTO } from "./integration-types"; type TIntegrationServiceFactoryDep = { integrationDAL: TIntegrationDALFactory; @@ -57,21 +50,10 @@ export const integrationServiceFactory = ({ const integrationAuth = await integrationAuthDAL.findById(integrationAuthId); if (!integrationAuth) throw new BadRequestError({ message: "Integration auth not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integrationAuth.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integrationAuth.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); - const folder = await folderDAL.findBySecretPath( - integrationAuth.projectId, - sourceEnvironment, - secretPath - ); + const folder = await folderDAL.findBySecretPath(integrationAuth.projectId, sourceEnvironment, secretPath); if (!folder) throw new BadRequestError({ message: "Folder path not found" }); const integration = await integrationDAL.create({ @@ -116,15 +98,8 @@ export const integrationServiceFactory = ({ const integration = await integrationDAL.findById(id); if (!integration) throw new BadRequestError({ message: "Integration auth not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integration.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integration.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations); const folder = await folderDAL.findBySecretPath(integration.projectId, environment, secretPath); if (!folder) throw new BadRequestError({ message: "Folder path not found" }); @@ -146,15 +121,8 @@ export const integrationServiceFactory = ({ const integration = await integrationDAL.findById(id); if (!integration) throw new BadRequestError({ message: "Integration auth not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - integration.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, integration.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); const deletedIntegration = await integrationDAL.deleteById(id); return { ...integration, ...deletedIntegration }; @@ -162,10 +130,7 @@ export const integrationServiceFactory = ({ const listIntegrationByProject = async ({ actor, actorId, projectId }: TProjectPermission) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const integrations = await integrationDAL.findByProjectId(projectId); return integrations; diff --git a/backend/src/services/org/incident-contacts-dal.ts b/backend/src/services/org/incident-contacts-dal.ts index c17516241..1979a9c3e 100644 --- a/backend/src/services/org/incident-contacts-dal.ts +++ b/backend/src/services/org/incident-contacts-dal.ts @@ -7,9 +7,7 @@ export type TIncidentContactsDALFactory = ReturnType { const create = async (orgId: string, email: string) => { try { - const [incidentContact] = await db(TableName.IncidentContact) - .insert({ orgId, email }) - .returning("*"); + const [incidentContact] = await db(TableName.IncidentContact).insert({ orgId, email }).returning("*"); return incidentContact; } catch (error) { throw new DatabaseError({ name: "Incident contact create", error }); @@ -38,10 +36,7 @@ export const incidentContactDALFactory = (db: TDbClient) => { const deleteById = async (id: string, orgId: string) => { try { - const [incidentContact] = await db(TableName.IncidentContact) - .where({ orgId, id }) - .delete() - .returning("*"); + const [incidentContact] = await db(TableName.IncidentContact).where({ orgId, id }).delete().returning("*"); return incidentContact; } catch (error) { throw new DatabaseError({ name: "Incident contact delete", error }); diff --git a/backend/src/services/org/org-dal.ts b/backend/src/services/org/org-dal.ts index 51ab59f68..6629030d2 100644 --- a/backend/src/services/org/org-dal.ts +++ b/backend/src/services/org/org-dal.ts @@ -7,16 +7,11 @@ import { TOrganizationsInsert, TOrgMemberships, TOrgMembershipsInsert, - TOrgMembershipsUpdate + TOrgMembershipsUpdate, + TUserEncryptionKeys } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { - buildFindFilter, - selectAllTableCols, - TFindFilter, - TFindOpt, - withTransaction -} from "@app/lib/knex"; +import { buildFindFilter, selectAllTableCols, TFindFilter, TFindOpt, withTransaction } from "@app/lib/knex"; export type TOrgDALFactory = ReturnType; @@ -35,12 +30,8 @@ export const orgDALFactory = (db: TDbClient) => { try { const org = await db(TableName.OrgMembership) .where({ userId }) - .join( - TableName.Organization, - `${TableName.OrgMembership}.orgId`, - `${TableName.Organization}.id` - ) - .select(`${TableName.Organization}.*`); + .join(TableName.Organization, `${TableName.OrgMembership}.orgId`, `${TableName.Organization}.id`) + .select(selectAllTableCols(TableName.Organization)); return org; } catch (error) { throw new DatabaseError({ error, name: "Find all org by user id" }); @@ -50,9 +41,9 @@ export const orgDALFactory = (db: TDbClient) => { const findOrgByProjectId = async (projectId: string): Promise => { try { const [org] = await db(TableName.Project) - .where({ [`${[TableName.Project]}.id`]: projectId }) + .where({ [`${TableName.Project}.id` as "id"]: projectId }) .join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`) - .select(`${TableName.Organization}.*`); + .select(selectAllTableCols(TableName.Organization)); return org; } catch (error) { @@ -66,7 +57,7 @@ export const orgDALFactory = (db: TDbClient) => { const members = await db(TableName.OrgMembership) .where({ orgId }) .join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) - .leftJoin( + .leftJoin( TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id` @@ -104,10 +95,7 @@ export const orgDALFactory = (db: TDbClient) => { const deleteById = async (orgId: string, tx?: Knex) => { try { - const [org] = await (tx || db)(TableName.Organization) - .where({ id: orgId }) - .delete() - .returning("*"); + const [org] = await (tx || db)(TableName.Organization).where({ id: orgId }).delete().returning("*"); return org; } catch (error) { throw new DatabaseError({ error, name: "Update organization" }); @@ -141,26 +129,16 @@ export const orgDALFactory = (db: TDbClient) => { const updateMembershipById = async (id: string, data: TOrgMembershipsUpdate, tx?: Knex) => { try { - const [membership] = await (tx || db)(TableName.OrgMembership) - .where({ id }) - .update(data) - .returning("*"); + const [membership] = await (tx || db)(TableName.OrgMembership).where({ id }).update(data).returning("*"); return membership; } catch (error) { throw new DatabaseError({ error, name: "Update org membership" }); } }; - const updateMembership = async ( - filter: Partial, - data: TOrgMembershipsUpdate, - tx?: Knex - ) => { + const updateMembership = async (filter: Partial, data: TOrgMembershipsUpdate, tx?: Knex) => { try { - const membership = await (tx || db)(TableName.OrgMembership) - .where(filter) - .update(data) - .returning("*"); + const membership = await (tx || db)(TableName.OrgMembership).where(filter).update(data).returning("*"); return membership; } catch (error) { throw new DatabaseError({ error, name: "Update org memberships" }); @@ -169,10 +147,7 @@ export const orgDALFactory = (db: TDbClient) => { const deleteMembershipById = async (id: string, orgId: string, tx?: Knex) => { try { - const [membership] = await (tx || db)(TableName.OrgMembership) - .where({ id, orgId }) - .delete() - .returning("*"); + const [membership] = await (tx || db)(TableName.OrgMembership).where({ id, orgId }).delete().returning("*"); return membership; } catch (error) { throw new DatabaseError({ error, name: "Delete org membership" }); @@ -185,18 +160,14 @@ export const orgDALFactory = (db: TDbClient) => { ) => { try { const query = (tx || db)(TableName.OrgMembership) + // eslint-disable-next-line .where(buildFindFilter(filter)) .join(TableName.Users, `${TableName.Users}.id`, `${TableName.OrgMembership}.userId`) - .select( - selectAllTableCols(TableName.OrgMembership), - db.ref("email").withSchema(TableName.Users) - ); - if (limit) query.limit(limit); - if (offset) query.offset(offset); + .select(selectAllTableCols(TableName.OrgMembership), db.ref("email").withSchema(TableName.Users)); + if (limit) void query.limit(limit); + if (offset) void query.offset(offset); if (sort) { - query.orderBy( - sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls })) - ); + void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls }))); } const res = await query; return res; diff --git a/backend/src/services/org/org-role-service.ts b/backend/src/services/org/org-role-service.ts index 8dfd0e0af..c48002d89 100644 --- a/backend/src/services/org/org-role-service.ts +++ b/backend/src/services/org/org-role-service.ts @@ -21,20 +21,10 @@ type TOrgRoleServiceFactoryDep = { export type TOrgRoleServiceFactory = ReturnType; -export const orgRoleServiceFactory = ({ - orgRoleDAL, - permissionService -}: TOrgRoleServiceFactoryDep) => { - const createRole = async ( - userId: string, - orgId: string, - data: Omit - ) => { +export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRoleServiceFactoryDep) => { + const createRole = async (userId: string, orgId: string, data: Omit) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Role - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Role); const existingRole = await orgRoleDAL.findOne({ slug: data.slug, orgId }); if (existingRole) throw new BadRequestError({ name: "Create Role", message: "Duplicate role" }); const role = await orgRoleDAL.create({ @@ -45,17 +35,9 @@ export const orgRoleServiceFactory = ({ return role; }; - const updateRole = async ( - userId: string, - orgId: string, - roleId: string, - data: Omit - ) => { + const updateRole = async (userId: string, orgId: string, roleId: string, data: Omit) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Role - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Role); if (data?.slug) { const existingRole = await orgRoleDAL.findOne({ slug: data.slug, orgId }); if (existingRole && existingRole.id !== roleId) @@ -71,10 +53,7 @@ export const orgRoleServiceFactory = ({ const deleteRole = async (userId: string, orgId: string, roleId: string) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Role - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Role); const [deletedRole] = await orgRoleDAL.delete({ id: roleId, orgId }); if (!deleteRole) throw new BadRequestError({ message: "Role not found", name: "Update role" }); @@ -83,10 +62,7 @@ export const orgRoleServiceFactory = ({ const listRoles = async (userId: string, orgId: string) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Role - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Role); const customRoles = await orgRoleDAL.find({ orgId }); const roles = [ { diff --git a/backend/src/services/org/org-service.ts b/backend/src/services/org/org-service.ts index 4587c3814..a393edcb5 100644 --- a/backend/src/services/org/org-service.ts +++ b/backend/src/services/org/org-service.ts @@ -3,11 +3,9 @@ import slugify from "@sindresorhus/slugify"; import jwt from "jsonwebtoken"; import { OrgMembershipRole, OrgMembershipStatus } from "@app/db/schemas"; +import { TProjects } from "@app/db/schemas/projects"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "@app/ee/services/permission/org-permission"; +import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TSamlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal"; import { getConfig } from "@app/lib/config/env"; @@ -17,9 +15,10 @@ import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { isDisposableEmail } from "@app/lib/validator"; -import { AuthMethod, AuthTokenType } from "../auth/auth-type"; +import { ActorType, AuthMethod, AuthTokenType } from "../auth/auth-type"; import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service"; import { TokenType } from "../auth-token/auth-token-types"; +import { TProjectDALFactory } from "../project/project-dal"; import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service"; import { TUserDALFactory } from "../user/user-dal"; import { TIncidentContactsDALFactory } from "./incident-contacts-dal"; @@ -28,6 +27,7 @@ import { TOrgDALFactory } from "./org-dal"; import { TOrgRoleDALFactory } from "./org-role-dal"; import { TDeleteOrgMembershipDTO, + TFindAllWorkspacesDTO, TInviteUserToOrgDTO, TUpdateOrgMembershipDTO, TVerifyUserToOrgDTO @@ -38,6 +38,7 @@ type TOrgServiceFactoryDep = { orgBotDAL: TOrgBotDALFactory; orgRoleDAL: TOrgRoleDALFactory; userDAL: TUserDALFactory; + projectDAL: TProjectDALFactory; incidentContactDAL: TIncidentContactsDALFactory; samlConfigDAL: Pick; smtpService: TSmtpService; @@ -58,6 +59,7 @@ export const orgServiceFactory = ({ incidentContactDAL, permissionService, smtpService, + projectDAL, tokenService, orgBotDAL, licenseService, @@ -69,8 +71,7 @@ export const orgServiceFactory = ({ const findOrganizationById = async (userId: string, orgId: string) => { await permissionService.getUserOrgPermission(userId, orgId); const org = await orgDAL.findOrgById(orgId); - if (!org) - throw new BadRequestError({ name: "Org not found", message: "Organization not found" }); + if (!org) throw new BadRequestError({ name: "Org not found", message: "Organization not found" }); return org; }; /* @@ -85,26 +86,45 @@ export const orgServiceFactory = ({ * */ const findAllOrgMembers = async (userId: string, orgId: string) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Member - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Member); const members = await orgDAL.findAllOrgMembers(orgId); return members; }; + + const findAllWorkspaces = async ({ actor, actorId, orgId }: TFindAllWorkspacesDTO) => { + const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Workspace); + + const organizationWorkspaceIds = new Set((await projectDAL.find({ orgId })).map((workspace) => workspace.id)); + + let workspaces: (TProjects & { organization: string } & { + environments: { + id: string; + slug: string; + name: string; + }[]; + })[]; + + if (actor === ActorType.USER) { + workspaces = await projectDAL.findAllProjects(actorId); + } else if (actor === ActorType.IDENTITY) { + workspaces = await projectDAL.findAllProjectsByIdentity(actorId); + } else { + throw new BadRequestError({ message: "Invalid actor type" }); + } + + return workspaces.filter((workspace) => organizationWorkspaceIds.has(workspace.id)); + }; + /* * Update organization settings * */ const updateOrgName = async (userId: string, orgId: string, name: string) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Settings - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings); const org = await orgDAL.updateById(orgId, { name }); - if (!org) - throw new BadRequestError({ name: "Org not found", message: "Organization not found" }); + if (!org) throw new BadRequestError({ name: "Org not found", message: "Organization not found" }); return org; }; /* @@ -173,7 +193,7 @@ export const orgServiceFactory = ({ * */ const deleteOrganizationById = async (userId: string, orgId: string) => { const { membership } = await permissionService.getUserOrgPermission(userId, orgId); - if (membership.role !== OrgMembershipRole.Admin) + if ((membership.role as OrgMembershipRole) !== OrgMembershipRole.Admin) throw new UnauthorizedError({ name: "Delete org by id", message: "Not an admin" }); const organization = await orgDAL.deleteById(orgId); @@ -186,29 +206,19 @@ export const orgServiceFactory = ({ * Org membership management * Not another service because it has close ties with how an org works doesn't make sense to seperate them * */ - const updateOrgMembership = async ({ - role, - orgId, - userId, - membershipId - }: TUpdateOrgMembershipDTO) => { + const updateOrgMembership = async ({ role, orgId, userId, membershipId }: TUpdateOrgMembershipDTO) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Member - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Member); const isCustomRole = !Object.values(OrgMembershipRole).includes(role as OrgMembershipRole); if (isCustomRole) { const customRole = await orgRoleDAL.findOne({ slug: role, orgId }); - if (!customRole) - throw new BadRequestError({ name: "Update membership", message: "Role not found" }); + if (!customRole) throw new BadRequestError({ name: "Update membership", message: "Role not found" }); const plan = await licenseService.getPlan(orgId); if (!plan?.rbac) throw new BadRequestError({ - message: - "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member." + message: "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member." }); const [membership] = await orgDAL.updateMembership( @@ -221,10 +231,7 @@ export const orgServiceFactory = ({ return membership; } - const [membership] = await orgDAL.updateMembership( - { id: membershipId, orgId }, - { role, roleId: null } - ); + const [membership] = await orgDAL.updateMembership({ id: membershipId, orgId }, { role, roleId: null }); return membership; }; /* @@ -232,10 +239,7 @@ export const orgServiceFactory = ({ */ const inviteUserToOrganization = async ({ orgId, userId, inviteeEmail }: TInviteUserToOrgDTO) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Member - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member); const samlCfg = await samlConfigDAL.findOne({ orgId }); if (samlCfg && samlCfg.isActive) { @@ -248,8 +252,7 @@ export const orgServiceFactory = ({ // case: limit imposed on number of members allowed // case: number of members used exceeds the number of members allowed throw new BadRequestError({ - message: - "Failed to invite member due to member limit reached. Upgrade plan to invite more members." + message: "Failed to invite member due to member limit reached. Upgrade plan to invite more members." }); } const invitee = await orgDAL.transaction(async (tx) => { @@ -257,10 +260,7 @@ export const orgServiceFactory = ({ if (inviteeUser) { // if user already exist means its already part of infisical // Thus the signup flow is not needed anymore - const [inviteeMembership] = await orgDAL.findMembership( - { orgId, userId: inviteeUser.id }, - { tx } - ); + const [inviteeMembership] = await orgDAL.findMembership({ orgId, userId: inviteeUser.id }, { tx }); if (inviteeMembership && inviteeMembership.status === OrgMembershipStatus.Accepted) { throw new BadRequestError({ message: "Failed to invite an existing member of org", @@ -396,10 +396,7 @@ export const orgServiceFactory = ({ const deleteOrgMembership = async ({ orgId, userId, membershipId }: TDeleteOrgMembershipDTO) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Member - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Member); const membership = await orgDAL.deleteMembershipById(membershipId, orgId); @@ -412,20 +409,14 @@ export const orgServiceFactory = ({ * */ const findIncidentContacts = async (userId: string, orgId: string) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.IncidentAccount - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.IncidentAccount); const incidentContacts = await incidentContactDAL.findByOrgId(orgId); return incidentContacts; }; const createIncidentContact = async (userId: string, orgId: string, email: string) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.IncidentAccount - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.IncidentAccount); const doesIncidentContactExist = await incidentContactDAL.findOne(orgId, { email }); if (doesIncidentContactExist) { throw new BadRequestError({ @@ -440,10 +431,7 @@ export const orgServiceFactory = ({ const deleteIncidentContact = async (userId: string, orgId: string, id: string) => { const { permission } = await permissionService.getUserOrgPermission(userId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.IncidentAccount - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.IncidentAccount); const incidentContact = await incidentContactDAL.deleteById(id, orgId); return incidentContact; @@ -459,6 +447,7 @@ export const orgServiceFactory = ({ createOrganization, deleteOrganizationById, deleteOrgMembership, + findAllWorkspaces, updateOrgMembership, // incident contacts findIncidentContacts, diff --git a/backend/src/services/org/org-types.ts b/backend/src/services/org/org-types.ts index 6456e5de1..b9dbd74ad 100644 --- a/backend/src/services/org/org-types.ts +++ b/backend/src/services/org/org-types.ts @@ -1,3 +1,5 @@ +import { ActorType } from "../auth/auth-type"; + export type TUpdateOrgMembershipDTO = { userId: string; orgId: string; @@ -22,3 +24,9 @@ export type TVerifyUserToOrgDTO = { orgId: string; code: string; }; + +export type TFindAllWorkspacesDTO = { + actor: ActorType; + actorId: string; + orgId: string; +}; diff --git a/backend/src/services/project-bot/project-bot-dal.ts b/backend/src/services/project-bot/project-bot-dal.ts index 39a8628b8..7f342f0ae 100644 --- a/backend/src/services/project-bot/project-bot-dal.ts +++ b/backend/src/services/project-bot/project-bot-dal.ts @@ -15,11 +15,7 @@ export const projectBotDALFactory = (db: TDbClient) => { const bot = await (tx || db)(TableName.ProjectBot) .where(filter) .leftJoin(TableName.Users, `${TableName.ProjectBot}.senderId`, `${TableName.Users}.id`) - .leftJoin( - TableName.UserEncryptionKey, - `${TableName.UserEncryptionKey}.userId`, - `${TableName.Users}.id` - ) + .leftJoin(TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id`) .select(selectAllTableCols(TableName.ProjectBot)) .select(db.ref("publicKey").withSchema(TableName.UserEncryptionKey).as("senderPubKey")) .first(); diff --git a/backend/src/services/project-bot/project-bot-service.ts b/backend/src/services/project-bot/project-bot-service.ts index 8a3fd19d9..5478aadfa 100644 --- a/backend/src/services/project-bot/project-bot-service.ts +++ b/backend/src/services/project-bot/project-bot-service.ts @@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { decryptAsymmetric, @@ -28,10 +25,7 @@ type TProjectBotServiceFactoryDep = { export type TProjectBotServiceFactory = ReturnType; -export const projectBotServiceFactory = ({ - projectBotDAL, - permissionService -}: TProjectBotServiceFactoryDep) => { +export const projectBotServiceFactory = ({ projectBotDAL, permissionService }: TProjectBotServiceFactoryDep) => { const getBotKey = async (projectId: string) => { const appCfg = getConfig(); const encryptionKey = appCfg.ENCRYPTION_KEY; @@ -43,7 +37,7 @@ export const projectBotServiceFactory = ({ if (!bot.encryptedProjectKeyNonce || !bot.encryptedProjectKey) throw new BadRequestError({ message: "Encryption key missing" }); - if (rootEncryptionKey && bot.keyEncoding === SecretKeyEncoding.BASE64) { + if (rootEncryptionKey && (bot.keyEncoding as SecretKeyEncoding) === SecretKeyEncoding.BASE64) { const privateKeyBot = decryptSymmetric({ iv: bot.iv, tag: bot.tag, @@ -57,7 +51,7 @@ export const projectBotServiceFactory = ({ publicKey: bot.sender.publicKey }); } - if (encryptionKey && bot.keyEncoding === SecretKeyEncoding.UTF8) { + if (encryptionKey && (bot.keyEncoding as SecretKeyEncoding) === SecretKeyEncoding.UTF8) { const privateKeyBot = decryptSymmetric128BitHexKeyUTF8({ iv: bot.iv, tag: bot.tag, @@ -79,10 +73,7 @@ export const projectBotServiceFactory = ({ const findBotByProjectId = async ({ actorId, actor, projectId }: TProjectPermission) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Integrations - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); const appCfg = getConfig(); const bot = await projectBotDAL.transaction(async (tx) => { @@ -108,10 +99,7 @@ export const projectBotServiceFactory = ({ ); } if (appCfg.ENCRYPTION_KEY) { - const { iv, tag, ciphertext } = encryptSymmetric128BitHexKeyUTF8( - privateKey, - appCfg.ENCRYPTION_KEY - ); + const { iv, tag, ciphertext } = encryptSymmetric128BitHexKeyUTF8(privateKey, appCfg.ENCRYPTION_KEY); return projectBotDAL.create( { name: "Infisical Bot", @@ -132,25 +120,12 @@ export const projectBotServiceFactory = ({ return bot; }; - const setBotActiveState = async ({ - actor, - botId, - botKey, - actorId, - isActive - }: TSetActiveStateDTO) => { + const setBotActiveState = async ({ actor, botId, botKey, actorId, isActive }: TSetActiveStateDTO) => { const bot = await projectBotDAL.findById(botId); if (!bot) throw new BadRequestError({ message: "Bot not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - bot.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Integrations - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, bot.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations); if (isActive) { if (!botKey?.nonce || !botKey?.encryptedKey) { diff --git a/backend/src/services/project-env/project-env-dal.ts b/backend/src/services/project-env/project-env-dal.ts index c74e8803c..42a234298 100644 --- a/backend/src/services/project-env/project-env-dal.ts +++ b/backend/src/services/project-env/project-env-dal.ts @@ -12,9 +12,7 @@ export const projectEnvDALFactory = (db: TDbClient) => { const findBySlugs = async (projectId: string, env: string[], tx?: Knex) => { try { - const envs = await (tx || db)(TableName.Environment) - .where("projectId", projectId) - .whereIn("slug", env); + const envs = await (tx || db)(TableName.Environment).where("projectId", projectId).whereIn("slug", env); return envs; } catch (error) { throw new DatabaseError({ error, name: "Find by slugs" }); @@ -26,17 +24,12 @@ export const projectEnvDALFactory = (db: TDbClient) => { const findLastEnvPosition = async (projectId: string, tx?: Knex) => { const lastPos = await (tx || db)(TableName.Environment) .where({ projectId }) - .max({ position: "position" }) + .max("position", { as: "position" }) .first(); return lastPos?.position || 0; }; - const updateAllPosition = async ( - projectId: string, - pos: number, - targetPos: number, - tx?: Knex - ) => { + const updateAllPosition = async (projectId: string, pos: number, targetPos: number, tx?: Knex) => { try { if (targetPos === -1) { // this means delete diff --git a/backend/src/services/project-env/project-env-service.ts b/backend/src/services/project-env/project-env-service.ts index d95bb7950..49657204e 100644 --- a/backend/src/services/project-env/project-env-service.ts +++ b/backend/src/services/project-env/project-env-service.ts @@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { TProjectDALFactory } from "../project/project-dal"; @@ -32,10 +29,7 @@ export const projectEnvServiceFactory = ({ }: TProjectEnvServiceFactoryDep) => { const createEnvironment = async ({ projectId, actorId, actor, name, slug }: TCreateEnvDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Environments - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Environments); const envs = await projectEnvDAL.find({ projectId }); const existingEnv = envs.find(({ slug: envSlug }) => envSlug === slug); @@ -65,26 +59,15 @@ export const projectEnvServiceFactory = ({ return env; }; - const updateEnvironment = async ({ - projectId, - slug, - actor, - actorId, - name, - id, - position - }: TUpdateEnvDTO) => { + const updateEnvironment = async ({ projectId, slug, actor, actorId, name, id, position }: TUpdateEnvDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Environments - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Environments); const oldEnv = await projectEnvDAL.findOne({ id, projectId }); if (!oldEnv) throw new BadRequestError({ message: "Environment not found" }); if (slug) { - const existingEnv = await projectEnvDAL.findOne({ slug }); + const existingEnv = await projectEnvDAL.findOne({ slug, projectId }); if (existingEnv && existingEnv.id !== id) { throw new BadRequestError({ message: "Environment with slug already exist", @@ -104,10 +87,7 @@ export const projectEnvServiceFactory = ({ const deleteEnvironment = async ({ projectId, actor, actorId, id }: TDeleteEnvDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Environments - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Environments); const env = await projectEnvDAL.transaction(async (tx) => { const [doc] = await projectEnvDAL.delete({ id, projectId }, tx); diff --git a/backend/src/services/project-key/project-key-dal.ts b/backend/src/services/project-key/project-key-dal.ts index b183a933b..7423a48de 100644 --- a/backend/src/services/project-key/project-key-dal.ts +++ b/backend/src/services/project-key/project-key-dal.ts @@ -1,7 +1,7 @@ import { TDbClient } from "@app/db"; import { TableName, TProjectKeys } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { ormify } from "@app/lib/knex"; +import { ormify, selectAllTableCols } from "@app/lib/knex"; export type TProjectKeyDALFactory = ReturnType; @@ -11,25 +11,19 @@ export const projectKeyDALFactory = (db: TDbClient) => { const findLatestProjectKey = async ( userId: string, projectId: string - ): Promise => { + ): Promise<(TProjectKeys & { sender: { publicKey: string } }) | undefined> => { try { const projectKey = await db(TableName.ProjectKeys) - .where({ projectId, receiverId: userId }) .join(TableName.Users, `${TableName.ProjectKeys}.senderId`, `${TableName.Users}.id`) - .join( - TableName.UserEncryptionKey, - `${TableName.UserEncryptionKey}.userId`, - `${TableName.Users}.id` - ) + .join(TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id`) + .where({ projectId, receiverId: userId }) .orderBy("createdAt", "desc", "last") - .select(`${TableName.ProjectKeys}.*`, `${TableName.UserEncryptionKey}.publicKey`) + .select(selectAllTableCols(TableName.ProjectKeys)) + .select(db.ref("publicKey").withSchema(TableName.UserEncryptionKey)) .first(); if (projectKey) { - projectKey.sender = { - publicKey: projectKey.publicKey - }; + return { ...projectKey, sender: { publicKey: projectKey.publicKey } }; } - return projectKey; } catch (error) { throw new DatabaseError({ error, name: "Find latest project key" }); } @@ -40,11 +34,7 @@ export const projectKeyDALFactory = (db: TDbClient) => { const pubKeys = await db(TableName.ProjectMembership) .where({ projectId }) .join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) - .join( - TableName.UserEncryptionKey, - `${TableName.Users}.id`, - `${TableName.UserEncryptionKey}.userId` - ) + .join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`) .select("userId", "publicKey"); return pubKeys; } catch (error) { diff --git a/backend/src/services/project-key/project-key-service.ts b/backend/src/services/project-key/project-key-service.ts index 76694217f..e7ebc23ee 100644 --- a/backend/src/services/project-key/project-key-service.ts +++ b/backend/src/services/project-key/project-key-service.ts @@ -1,10 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal"; @@ -33,10 +30,7 @@ export const projectKeyServiceFactory = ({ encryptedKey }: TUploadProjectKeyDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Member - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member); const receiverMembership = await projectMembershipDAL.findOne({ userId: receiverId, @@ -59,10 +53,7 @@ export const projectKeyServiceFactory = ({ const getProjectPublicKeys = async ({ actor, actorId, projectId }: TGetLatestProjectKeyDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Member - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member); return projectKeyDAL.findAllProjectUserPubKeys(projectId); }; diff --git a/backend/src/services/project-membership/project-membership-dal.ts b/backend/src/services/project-membership/project-membership-dal.ts index 86cfa55b4..22b9937a9 100644 --- a/backend/src/services/project-membership/project-membership-dal.ts +++ b/backend/src/services/project-membership/project-membership-dal.ts @@ -1,5 +1,5 @@ import { TDbClient } from "@app/db"; -import { TableName } from "@app/db/schemas"; +import { TableName, TUserEncryptionKeys } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; @@ -14,7 +14,7 @@ export const projectMembershipDALFactory = (db: TDbClient) => { const members = await db(TableName.ProjectMembership) .where({ projectId }) .join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) - .join( + .join( TableName.UserEncryptionKey, `${TableName.UserEncryptionKey}.userId`, `${TableName.Users}.id` @@ -25,10 +25,10 @@ export const projectMembershipDALFactory = (db: TDbClient) => { db.ref("role").withSchema(TableName.ProjectMembership), db.ref("roleId").withSchema(TableName.ProjectMembership), db.ref("email").withSchema(TableName.Users), + db.ref("publicKey").withSchema(TableName.UserEncryptionKey), db.ref("firstName").withSchema(TableName.Users), db.ref("lastName").withSchema(TableName.Users), - db.ref("id").withSchema(TableName.Users).as("userId"), - db.ref("publicKey").withSchema(TableName.UserEncryptionKey) + db.ref("id").withSchema(TableName.Users).as("userId") ); return members.map(({ email, firstName, lastName, publicKey, ...data }) => ({ ...data, diff --git a/backend/src/services/project-membership/project-membership-service.ts b/backend/src/services/project-membership/project-membership-service.ts index d2b4901a0..862c19384 100644 --- a/backend/src/services/project-membership/project-membership-service.ts +++ b/backend/src/services/project-membership/project-membership-service.ts @@ -3,10 +3,7 @@ import { ForbiddenError } from "@casl/ability"; import { OrgMembershipStatus, ProjectMembershipRole, TableName } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError } from "@app/lib/errors"; import { groupBy } from "@app/lib/fn"; @@ -53,25 +50,14 @@ export const projectMembershipServiceFactory = ({ }: TProjectMembershipServiceFactoryDep) => { const getProjectMemberships = async ({ actorId, actor, projectId }: TGetProjectMembershipDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Member - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member); return projectMembershipDAL.findAllProjectMembers(projectId); }; - const inviteUserToProject = async ({ - actorId, - actor, - projectId, - email - }: TInviteUserToProjectDTO) => { + const inviteUserToProject = async ({ actorId, actor, projectId, email }: TInviteUserToProjectDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Member - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Member); const invitee = await userDAL.findOne({ email }); if (!invitee || !invitee.isAccepted) @@ -126,37 +112,25 @@ export const projectMembershipServiceFactory = ({ return { invitee, latestKey }; }; - const addUsersToProject = async ({ - projectId, - actorId, - actor, - members - }: TAddUsersToWorkspaceDTO) => { + const addUsersToProject = async ({ projectId, actorId, actor, members }: TAddUsersToWorkspaceDTO) => { const project = await projectDAL.findById(projectId); if (!project) throw new BadRequestError({ message: "Project not found" }); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Member - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Member); const orgMembers = await orgDAL.findMembership({ orgId: project.orgId, $in: { - [`${TableName.OrgMembership}.id` as "id"]: members.map( - ({ orgMembershipId }) => orgMembershipId - ) + [`${TableName.OrgMembership}.id` as "id"]: members.map(({ orgMembershipId }) => orgMembershipId) } }); - if (orgMembers.length !== members.length) - throw new BadRequestError({ message: "Some users are not part of org" }); + if (orgMembers.length !== members.length) throw new BadRequestError({ message: "Some users are not part of org" }); const existingMembers = await projectMembershipDAL.find({ projectId, $in: { userId: orgMembers.map(({ userId }) => userId).filter(Boolean) as string[] } }); - if (existingMembers.length) - throw new BadRequestError({ message: "Some users are already part of project" }); + if (existingMembers.length) throw new BadRequestError({ message: "Some users are already part of project" }); await projectMembershipDAL.transaction(async (tx) => { await projectMembershipDAL.insertMany( @@ -184,7 +158,7 @@ export const projectMembershipServiceFactory = ({ await smtpService.sendMail({ template: SmtpTemplates.WorkspaceInvite, subjectLine: "Infisical workspace invitation", - recipients: orgMembers.map(({ email }) => email).filter(Boolean) as string[], + recipients: orgMembers.map(({ email }) => email).filter(Boolean), substitutions: { inviterFirstName: sender.firstName, inviterEmail: sender.email, @@ -203,24 +177,17 @@ export const projectMembershipServiceFactory = ({ role }: TUpdateProjectMembershipDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Member - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member); - const isCustomRole = !Object.values(ProjectMembershipRole).includes( - role as ProjectMembershipRole - ); + const isCustomRole = !Object.values(ProjectMembershipRole).includes(role as ProjectMembershipRole); if (isCustomRole) { const customRole = await projectRoleDAL.findOne({ slug: role, projectId }); - if (!customRole) - throw new BadRequestError({ name: "Update project membership", message: "Role not found" }); + if (!customRole) throw new BadRequestError({ name: "Update project membership", message: "Role not found" }); const project = await projectDAL.findById(customRole.projectId); const plan = await licenseService.getPlan(project.orgId); if (!plan?.rbac) throw new BadRequestError({ - message: - "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member." + message: "Failed to assign custom role due to RBAC restriction. Upgrade plan to assign custom role to member." }); const [membership] = await projectMembershipDAL.update( @@ -233,30 +200,16 @@ export const projectMembershipServiceFactory = ({ return membership; } - const [membership] = await projectMembershipDAL.update( - { id: membershipId, projectId }, - { role, roleId: null } - ); + const [membership] = await projectMembershipDAL.update({ id: membershipId, projectId }, { role, roleId: null }); return membership; }; - const deleteProjectMembership = async ({ - actorId, - actor, - projectId, - membershipId - }: TDeleteProjectMembershipDTO) => { + const deleteProjectMembership = async ({ actorId, actor, projectId, membershipId }: TDeleteProjectMembershipDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Member - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Member); const membership = await projectMembershipDAL.transaction(async (tx) => { - const [deletedMembership] = await projectMembershipDAL.delete( - { projectId, id: membershipId }, - tx - ); + const [deletedMembership] = await projectMembershipDAL.delete({ projectId, id: membershipId }, tx); await projectKeyDAL.delete({ receiverId: deletedMembership.userId, projectId }, tx); return deletedMembership; }); diff --git a/backend/src/services/project-role/project-role-service.ts b/backend/src/services/project-role/project-role-service.ts index 8e86b45fb..4c0633712 100644 --- a/backend/src/services/project-role/project-role-service.ts +++ b/backend/src/services/project-role/project-role-service.ts @@ -18,18 +18,12 @@ import { TProjectRoleDALFactory } from "./project-role-dal"; type TProjectRoleServiceFactoryDep = { projectRoleDAL: TProjectRoleDALFactory; - permissionService: Pick< - TPermissionServiceFactory, - "getProjectPermission" | "getUserProjectPermission" - >; + permissionService: Pick; }; export type TProjectRoleServiceFactory = ReturnType; -export const projectRoleServiceFactory = ({ - projectRoleDAL, - permissionService -}: TProjectRoleServiceFactoryDep) => { +export const projectRoleServiceFactory = ({ projectRoleDAL, permissionService }: TProjectRoleServiceFactoryDep) => { const createRole = async ( actor: ActorType, actorId: string, @@ -37,10 +31,7 @@ export const projectRoleServiceFactory = ({ data: Omit ) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Role - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Role); const existingRole = await projectRoleDAL.findOne({ slug: data.slug, projectId }); if (existingRole) throw new BadRequestError({ name: "Create Role", message: "Duplicate role" }); const role = await projectRoleDAL.create({ @@ -59,10 +50,7 @@ export const projectRoleServiceFactory = ({ data: Omit ) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Role - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Role); if (data?.slug) { const existingRole = await projectRoleDAL.findOne({ slug: data.slug, projectId }); if (existingRole && existingRole.id !== roleId) @@ -76,17 +64,9 @@ export const projectRoleServiceFactory = ({ return updatedRole; }; - const deleteRole = async ( - actor: ActorType, - actorId: string, - projectId: string, - roleId: string - ) => { + const deleteRole = async (actor: ActorType, actorId: string, projectId: string, roleId: string) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Role - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Role); const [deletedRole] = await projectRoleDAL.delete({ id: roleId, projectId }); if (!deleteRole) throw new BadRequestError({ message: "Role not found", name: "Update role" }); @@ -95,10 +75,7 @@ export const projectRoleServiceFactory = ({ const listRoles = async (actor: ActorType, actorId: string, projectId: string) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Role - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role); const customRoles = await projectRoleDAL.find({ projectId }); const roles = [ { @@ -151,10 +128,7 @@ export const projectRoleServiceFactory = ({ }; const getUserPermission = async (userId: string, projectId: string) => { - const { permission, membership } = await permissionService.getUserProjectPermission( - userId, - projectId - ); + const { permission, membership } = await permissionService.getUserProjectPermission(userId, projectId); return { permissions: packRules(permission.rules), membership }; }; diff --git a/backend/src/services/project/project-dal.ts b/backend/src/services/project/project-dal.ts index a82fbc3af..e937a8e0f 100644 --- a/backend/src/services/project/project-dal.ts +++ b/backend/src/services/project/project-dal.ts @@ -12,16 +12,8 @@ export const projectDALFactory = (db: TDbClient) => { try { const workspaces = await db(TableName.ProjectMembership) .where({ userId }) - .join( - TableName.Project, - `${TableName.ProjectMembership}.projectId`, - `${TableName.Project}.id` - ) - .leftJoin( - TableName.Environment, - `${TableName.Environment}.projectId`, - `${TableName.Project}.id` - ) + .join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) + .leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`) .select( selectAllTableCols(TableName.Project), db.ref("id").withSchema(TableName.Project).as("_id"), @@ -30,7 +22,7 @@ export const projectDALFactory = (db: TDbClient) => { db.ref("name").withSchema(TableName.Environment).as("envName") ) .orderBy("createdAt", "asc", "last"); - return sqlNestRelationships({ + const nestedWorkspaces = sqlNestRelationships({ data: workspaces, key: "id", parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }), @@ -46,25 +38,64 @@ export const projectDALFactory = (db: TDbClient) => { } ] }); + + return nestedWorkspaces.map((workspace) => ({ + ...workspace, + organization: workspace.orgId + })); } catch (error) { throw new DatabaseError({ error, name: "Find all projects" }); } }; + const findAllProjectsByIdentity = async (identityId: string) => { + try { + const workspaces = await db(TableName.IdentityProjectMembership) + .where({ identityId }) + .join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`) + .leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`) + .select( + 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"); + + const nestedWorkspaces = 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 + }) + } + ] + }); + + // We need to add the organization field, as it's required for one of our API endpoint responses. + return nestedWorkspaces.map((workspace) => ({ + ...workspace, + organization: workspace.orgId + })); + } catch (error) { + throw new DatabaseError({ error, name: "Find all projects by identity" }); + } + }; + const findProjectById = async (id: string) => { try { const workspaces = await db(TableName.ProjectMembership) .where(`${TableName.Project}.id`, id) - .join( - TableName.Project, - `${TableName.ProjectMembership}.projectId`, - `${TableName.Project}.id` - ) - .join( - TableName.Environment, - `${TableName.Environment}.projectId`, - `${TableName.Project}.id` - ) + .join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) + .join(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`) .select( selectAllTableCols(TableName.Project), db.ref("id").withSchema(TableName.Project).as("_id"), @@ -96,6 +127,7 @@ export const projectDALFactory = (db: TDbClient) => { return { ...projectOrm, findAllProjects, + findAllProjectsByIdentity, findProjectById }; }; diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index df1168578..08d75b0dd 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -3,15 +3,9 @@ import slugify from "@sindresorhus/slugify"; import { ProjectMembershipRole } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "@app/ee/services/permission/org-permission"; +import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { createSecretBlindIndex } from "@app/lib/crypto"; import { BadRequestError } from "@app/lib/errors"; @@ -56,10 +50,7 @@ export const projectServiceFactory = ({ * */ const createProject = async ({ orgId, actor, actorId, workspaceName }: TCreateProjectDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); - ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Workspace - ); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace); const appCfg = getConfig(); const blindIndex = createSecretBlindIndex(appCfg.ROOT_ENCRYPTION_KEY, appCfg.ENCRYPTION_KEY); @@ -69,8 +60,7 @@ export const projectServiceFactory = ({ // case: limit imposed on number of workspaces allowed // case: number of workspaces used exceeds the number of workspaces allowed throw new BadRequestError({ - message: - "Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces." + message: "Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces." }); } @@ -118,10 +108,7 @@ export const projectServiceFactory = ({ const deleteProject = async ({ actor, actorId, projectId }: TDeleteProjectDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Project - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Project); const deletedProject = await projectDAL.deleteById(projectId); return deletedProject; @@ -144,26 +131,15 @@ export const projectServiceFactory = ({ autoCapitalization }: TGetProjectDTO & { autoCapitalization: boolean }) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Settings - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); const updatedProject = await projectDAL.updateById(projectId, { autoCapitalization }); return updatedProject; }; - const updateName = async ({ - projectId, - actor, - actorId, - name - }: TGetProjectDTO & { name: string }) => { + const updateName = async ({ projectId, actor, actorId, name }: TGetProjectDTO & { name: string }) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Settings - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings); const updatedProject = await projectDAL.updateById(projectId, { name }); return updatedProject; diff --git a/backend/src/services/secret-blind-index/secret-blind-index-dal.ts b/backend/src/services/secret-blind-index/secret-blind-index-dal.ts index c508728af..8fa60cde7 100644 --- a/backend/src/services/secret-blind-index/secret-blind-index-dal.ts +++ b/backend/src/services/secret-blind-index/secret-blind-index-dal.ts @@ -13,20 +13,12 @@ export const secretBlindIndexDALFactory = (db: TDbClient) => { const countOfSecretsWithNullSecretBlindIndex = async (projectId: string, tx?: Knex) => { try { const doc = await (tx || db)(TableName.Secret) - .leftJoin( - TableName.SecretFolder, - `${TableName.SecretFolder}.id`, - `${TableName.Secret}.folderId` - ) - .leftJoin( - TableName.Environment, - `${TableName.Environment}.id`, - `${TableName.SecretFolder}.envId` - ) + .leftJoin(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.Secret}.folderId`) + .leftJoin(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretFolder}.envId`) .where({ projectId }) .whereNull("secretBlindIndex") - .count(`${TableName.Secret}.id`); - return (doc as any)?.[0]?.count || 0; + .count(`${TableName.Secret}.id` as "id"); + return doc?.[0]?.count || 0; } catch (error) { throw new DatabaseError({ error, name: "CountOfSecretWillNullSecretBlindIndex" }); } @@ -35,16 +27,8 @@ export const secretBlindIndexDALFactory = (db: TDbClient) => { const findAllSecretsByProjectId = async (projectId: string, tx?: Knex) => { try { const docs = await (tx || db)(TableName.Secret) - .leftJoin( - TableName.SecretFolder, - `${TableName.SecretFolder}.id`, - `${TableName.Secret}.folderId` - ) - .leftJoin( - TableName.Environment, - `${TableName.Environment}.id`, - `${TableName.SecretFolder}.envId` - ) + .leftJoin(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.Secret}.folderId`) + .leftJoin(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretFolder}.envId`) .where({ projectId }) .whereNull("secretBlindIndex") .select(selectAllTableCols(TableName.Secret)) @@ -61,16 +45,8 @@ export const secretBlindIndexDALFactory = (db: TDbClient) => { const findSecretsByProjectId = async (projectId: string, secretIds: string[], tx?: Knex) => { try { const docs = await (tx || db)(TableName.Secret) - .leftJoin( - TableName.SecretFolder, - `${TableName.SecretFolder}.id`, - `${TableName.Secret}.folderId` - ) - .leftJoin( - TableName.Environment, - `${TableName.Environment}.id`, - `${TableName.SecretFolder}.envId` - ) + .leftJoin(TableName.SecretFolder, `${TableName.SecretFolder}.id`, `${TableName.Secret}.folderId`) + .leftJoin(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretFolder}.envId`) .where({ projectId }) .whereIn(`${TableName.Secret}.id`, secretIds) .whereNull("secretBlindIndex") diff --git a/backend/src/services/secret-blind-index/secret-blind-index-service.ts b/backend/src/services/secret-blind-index/secret-blind-index-service.ts index 0c18ddedc..bb565e295 100644 --- a/backend/src/services/secret-blind-index/secret-blind-index-service.ts +++ b/backend/src/services/secret-blind-index/secret-blind-index-service.ts @@ -24,11 +24,7 @@ export const secretBlindIndexServiceFactory = ({ permissionService, secretDAL }: TSecretBlindIndexServiceFactoryDep) => { - const getSecretBlindIndexStatus = async ({ - actor, - projectId, - actorId - }: TGetProjectBlindIndexStatusDTO) => { + const getSecretBlindIndexStatus = async ({ actor, projectId, actorId }: TGetProjectBlindIndexStatusDTO) => { await permissionService.getProjectPermission(actor, actorId, projectId); const secretCount = await secretBlindIndexDAL.countOfSecretsWithNullSecretBlindIndex(projectId); @@ -57,15 +53,13 @@ export const secretBlindIndexServiceFactory = ({ } const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); const secrets = await secretBlindIndexDAL.findSecretsByProjectId( projectId, secretsToUpdate.map(({ secretId }) => secretId) ); - if (secrets.length !== secretsToUpdate.length) - throw new BadRequestError({ message: "Secret not found" }); + if (secrets.length !== secretsToUpdate.length) throw new BadRequestError({ message: "Secret not found" }); const operations = await Promise.all( secretsToUpdate.map(async ({ secretName, secretId: id }) => { diff --git a/backend/src/services/secret-folder/secret-folder-dal.ts b/backend/src/services/secret-folder/secret-folder-dal.ts index 3d25217cb..023d039ca 100644 --- a/backend/src/services/secret-folder/secret-folder-dal.ts +++ b/backend/src/services/secret-folder/secret-folder-dal.ts @@ -1,12 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { - TableName, - TProjectEnvironments, - TSecretFolders, - TSecretFoldersUpdate -} from "@app/db/schemas"; +import { TableName, TProjectEnvironments, TSecretFolders, TSecretFoldersUpdate } from "@app/db/schemas"; import { BadRequestError, DatabaseError } from "@app/lib/errors"; import { groupBy, removeTrailingSlash } from "@app/lib/fn"; import { ormify, selectAllTableCols } from "@app/lib/knex"; @@ -16,14 +11,10 @@ export const validateFolderName = (folderName: string) => { return validNameRegex.test(folderName); }; -const sqlFindMultipleFolderByEnvPathQuery = ( - db: Knex, - query: Array<{ envId: string; secretPath: string }> -) => { +const sqlFindMultipleFolderByEnvPathQuery = (db: Knex, query: Array<{ envId: string; secretPath: string }>) => { // this is removing an trailing slash like /folder1/folder2/ -> /folder1/folder2 const formatedQuery = query.map(({ envId, secretPath }) => { - const formatedPath = - secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath; + const formatedPath = secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath; const segments = formatedPath.split("/").filter(Boolean); if (segments.some((segment) => !validateFolderName(segment))) { throw new BadRequestError({ message: "Invalid folder name" }); @@ -42,7 +33,7 @@ const sqlFindMultipleFolderByEnvPathQuery = ( // Thus each node has connection to parent node // for a given path from root we recursively reach to the leaf path or till we get null // the below query is the base case where we select root folder which has parent folder id as null - baseQb + void baseQb .select({ depth: 1, // latestFolderVerId: db.raw("NULL::uuid"), @@ -57,49 +48,44 @@ const sqlFindMultipleFolderByEnvPathQuery = ( formatedQuery.map(({ envId }) => envId) ) .select(selectAllTableCols(TableName.SecretFolder)) - .union((qb) => - // for here on we keep going to next child node. - // we also keep a measure of depth then we check the depth matches the array path segment and folder name - // that is at depth 1 for a path /folder1/folder2 -> the name should be folder1 - qb - .select({ - depth: db.raw("parent.depth + 1"), - path: db.raw( - "CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)" - ) - }) - .select(selectAllTableCols(TableName.SecretFolder)) - .where((wb) => - formatedQuery.map(({ secretPath }) => - wb.orWhereRaw( - `depth = array_position(ARRAY[${secretPath - .map(() => "?") - .join(",")}]::varchar[], ${TableName.SecretFolder}.name,depth)`, - [...secretPath] + .union( + (qb) => + // for here on we keep going to next child node. + // we also keep a measure of depth then we check the depth matches the array path segment and folder name + // that is at depth 1 for a path /folder1/folder2 -> the name should be folder1 + void qb + .select({ + depth: db.raw("parent.depth + 1"), + path: db.raw( + "CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)" + ) + }) + .select(selectAllTableCols(TableName.SecretFolder)) + .where((wb) => + formatedQuery.map(({ secretPath }) => + wb.orWhereRaw( + `depth = array_position(ARRAY[${secretPath.map(() => "?").join(",")}]::varchar[], ${ + TableName.SecretFolder + }.name,depth)`, + [...secretPath] + ) ) ) - ) - .from(TableName.SecretFolder) - .join("parent", (bd) => - bd - .on("parent.id", `${TableName.SecretFolder}.parentId`) - .andOn("parent.envId", `${TableName.SecretFolder}.envId`) - ) + .from(TableName.SecretFolder) + .join("parent", (bd) => + bd + .on("parent.id", `${TableName.SecretFolder}.parentId`) + .andOn("parent.envId", `${TableName.SecretFolder}.envId`) + ) ); }) .select("*") .from("parent"); }; -const sqlFindFolderByPathQuery = ( - db: Knex, - projectId: string, - environment: string, - secretPath: string -) => { +const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environment: string, secretPath: string) => { // this is removing an trailing slash like /folder1/folder2/ -> /folder1/folder2 - const formatedPath = - secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath; + const formatedPath = secretPath.at(-1) === "/" && secretPath.length > 1 ? secretPath.slice(0, -1) : secretPath; // next goal to sanitize saw the raw sql query is safe // for this we ensure folder name contains only string and - nothing else const pathSegments = formatedPath.split("/").filter(Boolean); @@ -113,52 +99,45 @@ const sqlFindFolderByPathQuery = ( // Thus each node has connection to parent node // for a given path from root we recursively reach to the leaf path or till we get null // the below query is the base case where we select root folder which has parent folder id as null - baseQb + void baseQb .select({ depth: 1, // latestFolderVerId: db.raw("NULL::uuid"), path: db.raw("'/'") }) .from(TableName.SecretFolder) - .join( - TableName.Environment, - `${TableName.SecretFolder}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .where({ projectId, parentId: null }) .where(`${TableName.Environment}.slug`, environment) .select(selectAllTableCols(TableName.SecretFolder)) - .union((qb) => - // for here on we keep going to next child node. - // we also keep a measure of depth then we check the depth matches the array path segment and folder name - // that is at depth 1 for a path /folder1/folder2 -> the name should be folder1 - qb - .select({ - depth: db.raw("parent.depth + 1"), - path: db.raw( - "CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)" + .union( + (qb) => + // for here on we keep going to next child node. + // we also keep a measure of depth then we check the depth matches the array path segment and folder name + // that is at depth 1 for a path /folder1/folder2 -> the name should be folder1 + void qb + .select({ + depth: db.raw("parent.depth + 1"), + path: db.raw( + "CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)" + ) + }) + .select(selectAllTableCols(TableName.SecretFolder)) + .whereRaw( + `depth = array_position(ARRAY[${pathSegments + .map(() => "?") + .join(",")}]::varchar[], secret_folders.name,depth)`, + [...pathSegments] ) - }) - .select(selectAllTableCols(TableName.SecretFolder)) - .whereRaw( - `depth = array_position(ARRAY[${pathSegments - .map(() => "?") - .join(",")}]::varchar[], secret_folders.name,depth)`, - [...pathSegments] - ) - .from(TableName.SecretFolder) - .join("parent", "parent.id", `${TableName.SecretFolder}.parentId`) + .from(TableName.SecretFolder) + .join("parent", "parent.id", `${TableName.SecretFolder}.parentId`) ); }) .from("parent") - .leftJoin( - TableName.Environment, - `${TableName.Environment}.id`, - "parent.envId" - ) + .leftJoin(TableName.Environment, `${TableName.Environment}.id`, "parent.envId") .select< TSecretFolders & { depth: number; @@ -183,43 +162,38 @@ const sqlFindSecretPathByFolderId = (db: Knex, projectId: string, folderIds: str // first remember our folders are connected as a link list or known as adjacency list // Thus each node has connection to parent node // we first find the folder given in folder id - baseQb + void baseQb .from(TableName.SecretFolder) .select(selectAllTableCols(TableName.SecretFolder)) .select({ // this is for root condition // if the given folder id is root folder id then intial path is set as / instead of /root // if not root folder the path here will be / - path: db.raw( - `CONCAT('/', (CASE WHEN "parentId" is NULL THEN '' ELSE ${TableName.SecretFolder}.name END))` - ), + path: db.raw(`CONCAT('/', (CASE WHEN "parentId" is NULL THEN '' ELSE ${TableName.SecretFolder}.name END))`), child: db.raw("NULL::uuid") }) - .join( - TableName.Environment, - `${TableName.SecretFolder}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .where({ projectId }) .whereIn(`${TableName.SecretFolder}.id`, folderIds) - .union((qb) => - // then we keep going up - // until parent id is null - qb - .select(selectAllTableCols(TableName.SecretFolder)) - .select({ - // then we join join this folder name behind previous as we are going from child to parent - // the root folder check is used to avoid last / and also root name in folders - path: db.raw( - `CONCAT( CASE + .union( + (qb) => + // then we keep going up + // until parent id is null + void qb + .select(selectAllTableCols(TableName.SecretFolder)) + .select({ + // then we join join this folder name behind previous as we are going from child to parent + // the root folder check is used to avoid last / and also root name in folders + path: db.raw( + `CONCAT( CASE WHEN ${TableName.SecretFolder}."parentId" is NULL THEN '' ELSE CONCAT('/', secret_folders.name) END, parent.path )` - ), - child: db.raw("COALESCE(parent.child, parent.id)") - }) - .from(TableName.SecretFolder) - .join("parent", "parent.parentId", `${TableName.SecretFolder}.id`) + ), + child: db.raw("COALESCE(parent.child, parent.id)") + }) + .from(TableName.SecretFolder) + .join("parent", "parent.parentId", `${TableName.SecretFolder}.id`) ); }) .select("*") @@ -231,19 +205,9 @@ export const ROOT_FOLDER_NAME = "root"; export const secretFolderDALFactory = (db: TDbClient) => { const secretFolderOrm = ormify(db, TableName.SecretFolder); - const findBySecretPath = async ( - projectId: string, - environment: string, - path: string, - tx?: Knex - ) => { + const findBySecretPath = async (projectId: string, environment: string, path: string, tx?: Knex) => { try { - const folder = await sqlFindFolderByPathQuery( - tx || db, - projectId, - environment, - removeTrailingSlash(path) - ) + const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, removeTrailingSlash(path)) .orderBy("depth", "desc") .first(); if (folder && folder.path !== removeTrailingSlash(path)) { @@ -260,19 +224,9 @@ export const secretFolderDALFactory = (db: TDbClient) => { // used in folder creation // even if its the original given /path1/path2 // it will stop automatically at /path2 - const findClosestFolder = async ( - projectId: string, - environment: string, - path: string, - tx?: Knex - ) => { + const findClosestFolder = async (projectId: string, environment: string, path: string, tx?: Knex) => { try { - const folder = await sqlFindFolderByPathQuery( - tx || db, - projectId, - environment, - removeTrailingSlash(path) - ) + const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, removeTrailingSlash(path)) .orderBy("depth", "desc") .first(); if (!folder) return; @@ -283,10 +237,7 @@ export const secretFolderDALFactory = (db: TDbClient) => { } }; - const findByManySecretPath = async ( - query: Array<{ envId: string; secretPath: string }>, - tx?: Knex - ) => { + const findByManySecretPath = async (query: Array<{ envId: string; secretPath: string }>, tx?: Knex) => { try { const formatedQuery = query.map(({ secretPath, envId }) => ({ envId, @@ -294,10 +245,7 @@ export const secretFolderDALFactory = (db: TDbClient) => { })); const folders = await sqlFindMultipleFolderByEnvPathQuery(tx || db, formatedQuery); return formatedQuery.map(({ envId, secretPath }) => - folders.find( - ({ path: targetPath, envId: targetEnvId }) => - targetPath === secretPath && targetEnvId === envId - ) + folders.find(({ path: targetPath, envId: targetEnvId }) => targetPath === secretPath && targetEnvId === envId) ); } catch (error) { throw new DatabaseError({ error, name: "FindByManySecretPath" }); @@ -336,11 +284,7 @@ export const secretFolderDALFactory = (db: TDbClient) => { try { const folder = await (tx || db)(TableName.SecretFolder) .where({ [`${TableName.SecretFolder}.id` as "id"]: id }) - .join( - TableName.Environment, - `${TableName.SecretFolder}.envId`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`) .select(selectAllTableCols(TableName.SecretFolder)) .select( db.ref("id").withSchema(TableName.Environment).as("envId"), diff --git a/backend/src/services/secret-folder/secret-folder-service.ts b/backend/src/services/secret-folder/secret-folder-service.ts index a9e7474c8..082674485 100644 --- a/backend/src/services/secret-folder/secret-folder-service.ts +++ b/backend/src/services/secret-folder/secret-folder-service.ts @@ -4,21 +4,13 @@ import { v4 as uuidv4 } from "uuid"; import { TSecretFoldersInsert } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; import { BadRequestError } from "@app/lib/errors"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; import { TSecretFolderDALFactory } from "./secret-folder-dal"; -import { - TCreateFolderDTO, - TDeleteFolderDTO, - TGetFolderDTO, - TUpdateFolderDTO -} from "./secret-folder-types"; +import { TCreateFolderDTO, TDeleteFolderDTO, TGetFolderDTO, TUpdateFolderDTO } from "./secret-folder-types"; import { TSecretFolderVersionDALFactory } from "./secret-folder-version-dal"; type TSecretFolderServiceFactoryDep = { @@ -38,14 +30,7 @@ export const secretFolderServiceFactory = ({ projectEnvDAL, folderVersionDAL }: TSecretFolderServiceFactoryDep) => { - const createFolder = async ({ - projectId, - actor, - actorId, - name, - environment, - path: secretPath - }: TCreateFolderDTO) => { + const createFolder = async ({ projectId, actor, actorId, name, environment, path: secretPath }: TCreateFolderDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, @@ -53,8 +38,7 @@ export const secretFolderServiceFactory = ({ ); const env = await projectEnvDAL.findOne({ projectId, slug: environment }); - if (!env) - throw new BadRequestError({ message: "Environment not found", name: "Create folder" }); + if (!env) throw new BadRequestError({ message: "Environment not found", name: "Create folder" }); const folder = await folderDAL.transaction(async (tx) => { // the logic is simple we need to avoid creating same folder in same path multiple times @@ -62,12 +46,7 @@ export const secretFolderServiceFactory = ({ // so we do a tricky move. we try to find the to be created folder path if that is exactly match return that // else we get some path before that then we will start creating remaining folder const pathWithFolder = path.join(secretPath, name); - const parentFolder = await folderDAL.findClosestFolder( - projectId, - environment, - pathWithFolder, - tx - ); + const parentFolder = await folderDAL.findClosestFolder(projectId, environment, pathWithFolder, tx); // no folder found is not possible root should be their if (!parentFolder) throw new BadRequestError({ message: "Secret path not found" }); // exact folder @@ -78,24 +57,19 @@ export const secretFolderServiceFactory = ({ // this is upsert folder in a path // we are not taking snapshots of this because // snapshot will be removed from automatic for all commits to user click or cron based - const missingSegment = secretPath - .substring(parentFolder.path.length) - .split("/") - .filter(Boolean); + const missingSegment = secretPath.substring(parentFolder.path.length).split("/").filter(Boolean); if (missingSegment.length) { - const newFolders: Array = missingSegment.map( - (segment) => { - const newFolder = { - name: segment, - parentId: parentFolderId, - id: uuidv4(), - envId: env.id, - version: 1 - }; - parentFolderId = newFolder.id; - return newFolder; - } - ); + const newFolders: Array = missingSegment.map((segment) => { + const newFolder = { + name: segment, + parentId: parentFolderId, + id: uuidv4(), + envId: env.id, + version: 1 + }; + parentFolderId = newFolder.id; + return newFolder; + }); parentFolderId = newFolders.at(-1)?.id as string; const docs = await folderDAL.insertMany(newFolders, tx); await folderVersionDAL.insertMany( @@ -110,10 +84,7 @@ export const secretFolderServiceFactory = ({ } } - const doc = await folderDAL.create( - { name, envId: env.id, version: 1, parentId: parentFolderId }, - tx - ); + const doc = await folderDAL.create({ name, envId: env.id, version: 1, parentId: parentFolderId }, tx); await folderVersionDAL.create( { name: doc.name, @@ -149,8 +120,7 @@ export const secretFolderServiceFactory = ({ if (!parentFolder) throw new BadRequestError({ message: "Secret path not found" }); const env = await projectEnvDAL.findOne({ projectId, slug: environment }); - if (!env) - throw new BadRequestError({ message: "Environment not found", name: "Update folder" }); + if (!env) throw new BadRequestError({ message: "Environment not found", name: "Update folder" }); const folder = await folderDAL .findOne({ envId: env.id, id, parentId: parentFolder.id }) // now folder api accepts id based change @@ -160,11 +130,7 @@ export const secretFolderServiceFactory = ({ 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: folder.id, parentId: parentFolder.id }, - { name }, - tx - ); + const [doc] = await folderDAL.update({ envId: env.id, id: folder.id, parentId: parentFolder.id }, { name }, tx); await folderVersionDAL.create( { name: doc.name, @@ -182,14 +148,7 @@ export const secretFolderServiceFactory = ({ return { folder: newFolder, old: folder }; }; - const deleteFolder = async ({ - projectId, - actor, - actorId, - environment, - path: secretPath, - id - }: TDeleteFolderDTO) => { + const deleteFolder = async ({ projectId, actor, actorId, environment, path: secretPath, id }: TDeleteFolderDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, @@ -197,8 +156,7 @@ export const secretFolderServiceFactory = ({ ); const env = await projectEnvDAL.findOne({ projectId, slug: environment }); - if (!env) - throw new BadRequestError({ message: "Environment not found", name: "Create folder" }); + if (!env) throw new BadRequestError({ message: "Environment not found", name: "Create folder" }); const folder = await folderDAL.transaction(async (tx) => { const parentFolder = await folderDAL.findBySecretPath(projectId, environment, secretPath, tx); @@ -213,13 +171,7 @@ export const secretFolderServiceFactory = ({ return folder; }; - const getFolders = async ({ - projectId, - actor, - actorId, - environment, - path: secretPath - }: TGetFolderDTO) => { + const getFolders = async ({ projectId, actor, actorId, environment, path: secretPath }: TGetFolderDTO) => { // folder list is allowed to be read by anyone // permission to check does user has access await permissionService.getProjectPermission(actor, actorId, projectId); diff --git a/backend/src/services/secret-folder/secret-folder-version-dal.ts b/backend/src/services/secret-folder/secret-folder-version-dal.ts index b610de359..f133308cf 100644 --- a/backend/src/services/secret-folder/secret-folder-version-dal.ts +++ b/backend/src/services/secret-folder/secret-folder-version-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TSecretFolderVersions } from "@app/db/schemas"; +import { TableName, TSecretFolderVersions } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify, selectAllTableCols } from "@app/lib/knex"; @@ -14,11 +14,7 @@ export const secretFolderVersionDALFactory = (db: TDbClient) => { const findLatestVersionByFolderId = async (folderId: string, tx?: Knex) => { try { const docs = await (tx || db)(TableName.SecretFolderVersion) - .join( - TableName.SecretFolder, - `${TableName.SecretFolderVersion}.folderId`, - `${TableName.SecretFolder}.id` - ) + .join(TableName.SecretFolder, `${TableName.SecretFolderVersion}.folderId`, `${TableName.SecretFolder}.id`) .where({ parentId: folderId }) .join( (tx || db)(TableName.SecretFolderVersion) @@ -42,9 +38,7 @@ export const secretFolderVersionDALFactory = (db: TDbClient) => { const findLatestFolderVersions = async (folderIds: string[], tx?: Knex) => { try { - const docs: Array = await (tx || db)( - TableName.SecretFolderVersion - ) + const docs: Array = await (tx || db)(TableName.SecretFolderVersion) .whereIn("folderId", folderIds) .join( (tx || db)(TableName.SecretFolderVersion) diff --git a/backend/src/services/secret-import/secret-import-dal.ts b/backend/src/services/secret-import/secret-import-dal.ts index 0acbd7426..f9c6f1be7 100644 --- a/backend/src/services/secret-import/secret-import-dal.ts +++ b/backend/src/services/secret-import/secret-import-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { TableName,TSecretImports } from "@app/db/schemas"; +import { TableName, TSecretImports } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; @@ -15,7 +15,7 @@ export const secretImportDALFactory = (db: TDbClient) => { const findLastImportPosition = async (folderId: string, tx?: Knex) => { const lastPos = await (tx || db)(TableName.SecretImport) .where({ folderId }) - .max({ position: "position" }) + .max("position", { as: "position" }) .first(); return lastPos?.position || 0; }; @@ -53,11 +53,7 @@ export const secretImportDALFactory = (db: TDbClient) => { try { const docs = await (tx || db)(TableName.SecretImport) .where(filter) - .join( - TableName.Environment, - `${TableName.SecretImport}.importEnv`, - `${TableName.Environment}.id` - ) + .join(TableName.Environment, `${TableName.SecretImport}.importEnv`, `${TableName.Environment}.id`) .select( db.ref("*").withSchema(TableName.SecretImport) as unknown as keyof TSecretImports, db.ref("slug").withSchema(TableName.Environment), diff --git a/backend/src/services/secret-import/secret-import-fns.ts b/backend/src/services/secret-import/secret-import-fns.ts index a05ef964d..1fa55f214 100644 --- a/backend/src/services/secret-import/secret-import-fns.ts +++ b/backend/src/services/secret-import/secret-import-fns.ts @@ -25,10 +25,15 @@ export const fnSecretsFromImports = async ({ if (!folderIds.length) { return []; } - const importedSecrets = await secretDAL.find({ - $in: { folderId: folderIds }, - type: SecretType.Shared - }); + const importedSecrets = await secretDAL.find( + { + $in: { folderId: folderIds }, + type: SecretType.Shared + }, + { + sort: [["id", "asc"]] + } + ); const importedSecsGroupByFolderId = groupBy(importedSecrets, (i) => i.folderId); return allowedImports.map(({ importPath, importEnv }, i) => ({ diff --git a/backend/src/services/secret-import/secret-import-service.ts b/backend/src/services/secret-import/secret-import-service.ts index b4ba45d01..57142424f 100644 --- a/backend/src/services/secret-import/secret-import-service.ts +++ b/backend/src/services/secret-import/secret-import-service.ts @@ -1,10 +1,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { TProjectEnvDALFactory } from "../project-env/project-env-dal"; @@ -39,14 +36,7 @@ export const secretImportServiceFactory = ({ folderDAL, secretDAL }: TSecretImportServiceFactoryDep) => { - const createImport = async ({ - environment, - data, - actor, - actorId, - projectId, - path - }: TCreateSecretImportDTO) => { + const createImport = async ({ environment, data, actor, actorId, projectId, path }: TCreateSecretImportDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); // check if user has permission to import into destination path @@ -69,8 +59,7 @@ export const secretImportServiceFactory = ({ // TODO(akhilmhdh-pg): updated permission check add here const [importEnv] = await projectEnvDAL.findBySlugs(projectId, [data.environment]); - if (!importEnv) - throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); + if (!importEnv) throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); const secImport = await secretImportDAL.transaction(async (tx) => { const lastPos = await secretImportDAL.findLastImportPosition(folder.id, tx); @@ -88,15 +77,7 @@ export const secretImportServiceFactory = ({ return { ...secImport, importEnv }; }; - const updateImport = async ({ - path, - environment, - projectId, - actor, - actorId, - data, - id - }: TUpdateSecretImportDTO) => { + const updateImport = async ({ path, environment, projectId, actor, actorId, data, id }: TUpdateSecretImportDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, @@ -112,8 +93,7 @@ export const secretImportServiceFactory = ({ const importedEnv = data.environment // this is get env information of new one or old one ? (await projectEnvDAL.findBySlugs(projectId, [data.environment]))?.[0] : await projectEnvDAL.findById(secImpDoc.importEnv); - if (!importedEnv) - throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); + if (!importedEnv) throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); const updatedSecImport = await secretImportDAL.transaction(async (tx) => { const secImp = await secretImportDAL.findOne({ folderId: folder.id, id }); @@ -135,14 +115,7 @@ export const secretImportServiceFactory = ({ return { ...updatedSecImport, importEnv: importedEnv }; }; - const deleteImport = async ({ - path, - environment, - projectId, - actor, - actorId, - id - }: TDeleteSecretImportDTO) => { + const deleteImport = async ({ path, environment, projectId, actor, actorId, id }: TDeleteSecretImportDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, @@ -154,25 +127,17 @@ export const secretImportServiceFactory = ({ const secImport = await secretImportDAL.transaction(async (tx) => { const [doc] = await secretImportDAL.delete({ folderId: folder.id, id }, tx); - if (!doc) - throw new BadRequestError({ name: "Sec imp del", message: "Secret import doc not found" }); + if (!doc) throw new BadRequestError({ name: "Sec imp del", message: "Secret import doc not found" }); await secretImportDAL.updateAllPosition(folder.id, doc.position, -1, tx); const importEnv = await projectEnvDAL.findById(doc.importEnv); - if (!importEnv) - throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); + if (!importEnv) throw new BadRequestError({ error: "Imported env not found", name: "Create import" }); return { ...doc, importEnv }; }); return secImport; }; - const getImports = async ({ - path, - environment, - projectId, - actor, - actorId - }: TGetSecretImportsDTO) => { + const getImports = async ({ path, environment, projectId, actor, actorId }: TGetSecretImportsDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, @@ -186,13 +151,7 @@ export const secretImportServiceFactory = ({ return secImports; }; - const getSecretsFromImports = async ({ - path, - environment, - projectId, - actor, - actorId - }: TGetSecretsFromImportDTO) => { + const getSecretsFromImports = async ({ path, environment, projectId, actor, actorId }: TGetSecretsFromImportDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, diff --git a/backend/src/services/secret-tag/secret-tag-dal.ts b/backend/src/services/secret-tag/secret-tag-dal.ts index 2e6b38c02..f1ae2424a 100644 --- a/backend/src/services/secret-tag/secret-tag-dal.ts +++ b/backend/src/services/secret-tag/secret-tag-dal.ts @@ -23,11 +23,7 @@ export const secretTagDALFactory = (db: TDbClient) => { const deleteTagsManySecret = async (projectId: string, secretIds: string[], tx?: Knex) => { try { const tags = await (tx || db)(TableName.JnSecretTag) - .join( - TableName.SecretTag, - `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, - `${TableName.SecretTag}.id` - ) + .join(TableName.SecretTag, `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, `${TableName.SecretTag}.id`) .where("projectId", projectId) .whereIn(`${TableName.Secret}Id`, secretIds) .delete() diff --git a/backend/src/services/secret-tag/secret-tag-service.ts b/backend/src/services/secret-tag/secret-tag-service.ts index 8b097e595..361f2d009 100644 --- a/backend/src/services/secret-tag/secret-tag-service.ts +++ b/backend/src/services/secret-tag/secret-tag-service.ts @@ -1,10 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; import { TSecretTagDALFactory } from "./secret-tag-dal"; @@ -17,16 +14,10 @@ type TSecretTagServiceFactoryDep = { export type TSecretTagServiceFactory = ReturnType; -export const secretTagServiceFactory = ({ - secretTagDAL, - permissionService -}: TSecretTagServiceFactoryDep) => { +export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSecretTagServiceFactoryDep) => { const createTag = async ({ name, slug, actor, color, actorId, projectId }: TCreateTagDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Tags - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Tags); const existingTag = await secretTagDAL.findOne({ slug }); if (existingTag) throw new BadRequestError({ message: "Tag already exist" }); @@ -45,15 +36,8 @@ export const secretTagServiceFactory = ({ const tag = await secretTagDAL.findById(id); if (!tag) throw new BadRequestError({ message: "Tag doesn't exist" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - tag.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Tags - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, tag.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Tags); const deletedTag = await secretTagDAL.deleteById(tag.id); return deletedTag; @@ -61,12 +45,9 @@ export const secretTagServiceFactory = ({ const getProjectTags = async ({ actor, actorId, projectId }: TListProjectTagsDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Tags - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Tags); - const tags = await secretTagDAL.find({ projectId }); + const tags = await secretTagDAL.find({ projectId }, { sort: [["createdAt", "asc"]] }); return tags; }; diff --git a/backend/src/services/secret/secret-dal.ts b/backend/src/services/secret/secret-dal.ts index b207f604a..ba65033cb 100644 --- a/backend/src/services/secret/secret-dal.ts +++ b/backend/src/services/secret/secret-dal.ts @@ -11,17 +11,9 @@ export type TSecretDALFactory = ReturnType; export const secretDALFactory = (db: TDbClient) => { const secretOrm = ormify(db, TableName.Secret); - const update = async ( - filter: Partial, - data: Omit, - tx?: Knex - ) => { + const update = async (filter: Partial, data: Omit, tx?: Knex) => { try { - const sec = await (tx || db)(TableName.Secret) - .where(filter) - .update(data) - .increment("version", 1) - .returning("*"); + const sec = await (tx || db)(TableName.Secret).where(filter).update(data).increment("version", 1).returning("*"); return sec; } catch (error) { throw new DatabaseError({ error, name: "update secret" }); @@ -30,10 +22,7 @@ export const secretDALFactory = (db: TDbClient) => { // the idea is to use postgres specific function // insert with id this will cause a conflict then merge the data - const bulkUpdate = async ( - data: Array<{ filter: Partial; data: TSecretsUpdate }>, - tx?: Knex - ) => { + const bulkUpdate = async (data: Array<{ filter: Partial; data: TSecretsUpdate }>, tx?: Knex) => { try { const secs = await Promise.all( data.map(async ({ filter, data: updateData }) => { @@ -63,7 +52,7 @@ export const secretDALFactory = (db: TDbClient) => { .where({ folderId }) .where((bd) => { data.forEach((el) => { - bd.orWhere({ + void bd.orWhere({ secretBlindIndex: el.blindIndex, type: el.type, ...(el.type === SecretType.Personal ? { userId } : {}) @@ -89,23 +78,16 @@ export const secretDALFactory = (db: TDbClient) => { const secs = await (tx || db)(TableName.Secret) .where({ folderId }) .where((bd) => { - bd.whereNull("userId").orWhere({ userId: userId || null }); + void bd.whereNull("userId").orWhere({ userId: userId || null }); }) - .leftJoin( - TableName.JnSecretTag, - `${TableName.Secret}.id`, - `${TableName.JnSecretTag}.${TableName.Secret}Id` - ) - .leftJoin( - TableName.SecretTag, - `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, - `${TableName.SecretTag}.id` - ) + .leftJoin(TableName.JnSecretTag, `${TableName.Secret}.id`, `${TableName.JnSecretTag}.${TableName.Secret}Id`) + .leftJoin(TableName.SecretTag, `${TableName.JnSecretTag}.${TableName.SecretTag}Id`, `${TableName.SecretTag}.id`) .select(selectAllTableCols(TableName.Secret)) .select(db.ref("id").withSchema(TableName.SecretTag).as("tagId")) .select(db.ref("color").withSchema(TableName.SecretTag).as("tagColor")) .select(db.ref("slug").withSchema(TableName.SecretTag).as("tagSlug")) - .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName")); + .select(db.ref("name").withSchema(TableName.SecretTag).as("tagName")) + .orderBy("id", "asc"); const data = sqlNestRelationships({ data: secs, key: "id", @@ -144,7 +126,7 @@ export const secretDALFactory = (db: TDbClient) => { if (el.type === SecretType.Personal && !userId) { throw new BadRequestError({ message: "Missing personal user id" }); } - bd.orWhere({ + void bd.orWhere({ secretBlindIndex: el.blindIndex, type: el.type, userId: el.type === SecretType.Personal ? userId : null diff --git a/backend/src/services/secret/secret-fns.ts b/backend/src/services/secret/secret-fns.ts index 9abaa8424..0f6caa248 100644 --- a/backend/src/services/secret/secret-fns.ts +++ b/backend/src/services/secret/secret-fns.ts @@ -8,10 +8,7 @@ import { buildSecretBlindIndexFromName, decryptSymmetric128BitHexKeyUTF8 } from import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal"; import { TSecretDALFactory } from "./secret-dal"; -export const generateSecretBlindIndexBySalt = async ( - secretName: string, - secretBlindIndexDoc: TSecretBlindIndexes -) => { +export const generateSecretBlindIndexBySalt = async (secretName: string, secretBlindIndexDoc: TSecretBlindIndexes) => { const appCfg = getConfig(); const secretBlindIndex = await buildSecretBlindIndexFromName({ secretName, @@ -32,12 +29,7 @@ type TInterpolateSecretArg = { folderDAL: Pick; }; -export const interpolateSecrets = ({ - projectId, - secretEncKey, - secretDAL, - folderDAL -}: TInterpolateSecretArg) => { +export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderDAL }: TInterpolateSecretArg) => { const fetchSecretsCrossEnv = () => { const fetchCache: Record> = {}; @@ -197,10 +189,7 @@ export const interpolateSecrets = ({ return expandSecrets; }; -export const decryptSecretRaw = ( - secret: TSecrets & { workspace: string; environment: string }, - key: string -) => { +export const decryptSecretRaw = (secret: TSecrets & { workspace: string; environment: string }, key: string) => { const secretKey = decryptSymmetric128BitHexKeyUTF8({ ciphertext: secret.secretKeyCiphertext, iv: secret.secretKeyIV, diff --git a/backend/src/services/secret/secret-queue.ts b/backend/src/services/secret/secret-queue.ts index 71b2444ae..b797b7caf 100644 --- a/backend/src/services/secret/secret-queue.ts +++ b/backend/src/services/secret/secret-queue.ts @@ -23,11 +23,7 @@ import { TWebhookDALFactory } from "../webhook/webhook-dal"; import { fnTriggerWebhook } from "../webhook/webhook-fns"; import { TSecretDALFactory } from "./secret-dal"; import { interpolateSecrets } from "./secret-fns"; -import { - TCreateSecretReminderDTO, - THandleReminderDTO, - TRemoveSecretReminderDTO -} from "./secret-types"; +import { TCreateSecretReminderDTO, THandleReminderDTO, TRemoveSecretReminderDTO } from "./secret-types"; export type TSecretQueueFactory = ReturnType; @@ -105,20 +101,13 @@ export const secretQueueFactory = ({ QueueJobs.SecretReminder, { // on prod it this will be in days, in development this will be second - every: - appCfg.NODE_ENV === "development" - ? secondsToMillis(dto.repeatDays) - : daysToMillisecond(dto.repeatDays) + every: appCfg.NODE_ENV === "development" ? secondsToMillis(dto.repeatDays) : daysToMillisecond(dto.repeatDays) }, `reminder-${dto.secretId}` ); }; - const addSecretReminder = async ({ - oldSecret, - newSecret, - projectId - }: TCreateSecretReminderDTO) => { + const addSecretReminder = async ({ oldSecret, newSecret, projectId }: TCreateSecretReminderDTO) => { try { const appCfg = getConfig(); @@ -179,8 +168,7 @@ export const secretQueueFactory = ({ if (newSecret.type !== "personal" && secretReminderRepeatDays !== undefined) { if ( - (secretReminderRepeatDays && - oldSecret.secretReminderRepeatDays !== secretReminderRepeatDays) || + (secretReminderRepeatDays && oldSecret.secretReminderRepeatDays !== secretReminderRepeatDays) || (secretReminderNote && oldSecret.secretReminderNote !== secretReminderNote) ) { await addSecretReminder({ @@ -212,10 +200,7 @@ export const secretQueueFactory = ({ secretDAL, folderDAL }); - const content: Record< - string, - { value: string; comment?: string; skipMultilineEncoding?: boolean } - > = {}; + const content: Record = {}; importedSecrets.forEach(({ secrets: secs }) => { secs.forEach((secret) => { @@ -294,8 +279,7 @@ export const secretQueueFactory = ({ const integrations = await integrationDAL.findByProjectIdV2(projectId, environment); const toBeSyncedIntegrations = integrations.filter( - ({ secretPath: integrationSecPath, isActive }) => - isActive && isSamePath(secretPath, integrationSecPath) + ({ secretPath: integrationSecPath, isActive }) => isActive && isSamePath(secretPath, integrationSecPath) ); if (!integrations.length) return; @@ -309,16 +293,10 @@ export const secretQueueFactory = ({ }; const botKey = await projectBotService.getBotKey(projectId); - const { accessToken, accessId } = await integrationAuthService.getIntegrationAccessToken( - integrationAuth, - botKey - ); - const secrets = await getIntegrationSecrets( - { environment, projectId, secretPath, folderId: folder.id }, - botKey - ); + const { accessToken, accessId } = await integrationAuthService.getIntegrationAccessToken(integrationAuth, botKey); + const secrets = await getIntegrationSecrets({ environment, projectId, secretPath, folderId: folder.id }, botKey); const suffixedSecrets: typeof secrets = {}; - const metadata = integration.metadata as Record; + const metadata = integration.metadata as Record; if (metadata) { Object.keys(secrets).forEach((key) => { const prefix = metadata?.secretPrefix || ""; @@ -353,25 +331,19 @@ export const secretQueueFactory = ({ const project = await projectDAL.findById(projectId); if (!organization) { - logger.info( - `secretReminderQueue.process: [secretDocument=${data.secretId}] no organization found` - ); + logger.info(`secretReminderQueue.process: [secretDocument=${data.secretId}] no organization found`); return; } if (!project) { - logger.info( - `secretReminderQueue.process: [secretDocument=${data.secretId}] no project found` - ); + logger.info(`secretReminderQueue.process: [secretDocument=${data.secretId}] no project found`); return; } const projectMembers = await projectMembershipDAL.findAllProjectMembers(projectId); if (!projectMembers || !projectMembers.length) { - logger.info( - `secretReminderQueue.process: [secretDocument=${data.secretId}] no project members found` - ); + logger.info(`secretReminderQueue.process: [secretDocument=${data.secretId}] no project members found`); return; } diff --git a/backend/src/services/secret/secret-service.ts b/backend/src/services/secret/secret-service.ts index f210a72a2..71086446f 100644 --- a/backend/src/services/secret/secret-service.ts +++ b/backend/src/services/secret/secret-service.ts @@ -1,17 +1,8 @@ import { ForbiddenError, subject } from "@casl/ability"; -import { - SecretEncryptionAlgo, - SecretKeyEncoding, - SecretsSchema, - SecretType, - TableName -} from "@app/db/schemas"; +import { SecretEncryptionAlgo, SecretKeyEncoding, SecretsSchema, SecretType, TableName } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; import { getConfig } from "@app/lib/config/env"; import { buildSecretBlindIndexFromName, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; @@ -58,17 +49,11 @@ type TSecretServiceFactoryDep = { secretDAL: TSecretDALFactory; secretTagDAL: TSecretTagDALFactory; secretVersionDAL: TSecretVersionDALFactory; - folderDAL: Pick< - TSecretFolderDALFactory, - "findBySecretPath" | "updateById" | "findById" | "findByManySecretPath" - >; + folderDAL: Pick; secretBlindIndexDAL: TSecretBlindIndexDALFactory; permissionService: Pick; snapshotService: Pick; - secretQueueService: Pick< - TSecretQueueFactory, - "syncSecrets" | "handleSecretReminder" | "removeSecretReminder" - >; + secretQueueService: Pick; projectBotService: Pick; secretImportDAL: Pick; secretVersionTagDAL: Pick; @@ -93,8 +78,7 @@ export const secretServiceFactory = ({ const appCfg = getConfig(); const secretBlindIndexDoc = await secretBlindIndexDAL.findOne({ projectId }); - if (!secretBlindIndexDoc) - throw new BadRequestError({ message: "Blind index not found", name: "Create secret" }); + if (!secretBlindIndexDoc) throw new BadRequestError({ message: "Blind index not found", name: "Create secret" }); const secretBlindIndex = await buildSecretBlindIndexFromName({ secretName, @@ -116,15 +100,11 @@ export const secretServiceFactory = ({ inputSecrets.map(({ tags, ...el }) => ({ ...el, folderId })), tx ); - const newSecretGroupByBlindIndex = groupBy( - newSecrets, - (item) => item.secretBlindIndex as string - ); + const newSecretGroupByBlindIndex = groupBy(newSecrets, (item) => item.secretBlindIndex as string); const newSecretTags = inputSecrets.flatMap(({ tags: secretTags = [], secretBlindIndex }) => secretTags.map((tag) => ({ [`${TableName.SecretTag}Id` as const]: tag, - [`${TableName.Secret}Id` as const]: - newSecretGroupByBlindIndex[secretBlindIndex as string][0].id + [`${TableName.Secret}Id` as const]: newSecretGroupByBlindIndex[secretBlindIndex as string][0].id })) ); const secretVersions = await secretVersionDAL.insertMany( @@ -148,12 +128,7 @@ export const secretServiceFactory = ({ return newSecrets.map((secret) => ({ ...secret, _id: secret.id })); }; - const fnSecretBulkUpdate = async ({ - tx, - inputSecrets, - folderId, - projectId - }: TFnSecretBulkUpdate) => { + const fnSecretBulkUpdate = async ({ tx, inputSecrets, folderId, projectId }: TFnSecretBulkUpdate) => { const newSecrets = await secretDAL.bulkUpdate( inputSecrets.map(({ filter, data: { tags, ...data } }) => ({ filter: { ...filter, folderId }, @@ -197,12 +172,7 @@ export const secretServiceFactory = ({ return newSecrets.map((secret) => ({ ...secret, _id: secret.id })); }; - const fnSecretBulkDelete = async ({ - folderId, - inputSecrets, - tx, - actorId - }: TFnSecretBulkDelete) => { + const fnSecretBulkDelete = async ({ folderId, inputSecrets, tx, actorId }: TFnSecretBulkDelete) => { const deletedSecrets = await secretDAL.deleteMany( inputSecrets.map(({ type, secretBlindIndex }) => ({ blindIndex: secretBlindIndex, @@ -245,9 +215,7 @@ export const secretServiceFactory = ({ }: TFnSecretBlindIndexCheck) => { const blindIndex2KeyName: Record = {}; // used at audit log point const keyName2BlindIndex = await Promise.all( - inputSecrets.map(({ secretName }) => - generateSecretBlindIndexBySalt(secretName, blindIndexCfg) - ) + inputSecrets.map(({ secretName }) => generateSecretBlindIndexBySalt(secretName, blindIndexCfg)) ).then((blindIndexes) => blindIndexes.reduce>((prev, curr, i) => { // eslint-disable-next-line @@ -282,11 +250,7 @@ export const secretServiceFactory = ({ // this is used when secret blind index already exist // mainly for secret approval - const fnSecretBlindIndexCheckV2 = async ({ - inputSecrets, - folderId, - userId - }: TFnSecretBlindIndexCheckV2) => { + const fnSecretBlindIndexCheckV2 = async ({ inputSecrets, folderId, userId }: TFnSecretBlindIndexCheckV2) => { if (inputSecrets.some(({ type }) => type === SecretType.Personal) && !userId) { throw new BadRequestError({ message: "Missing user id for personal secret" }); } @@ -303,14 +267,7 @@ export const secretServiceFactory = ({ return { secsGroupedByBlindIndex, secrets }; }; - const createSecret = async ({ - path, - actor, - actorId, - environment, - projectId, - ...inputSecret - }: TCreateSecretDTO) => { + const createSecret = async ({ path, actor, actorId, environment, projectId, ...inputSecret }: TCreateSecretDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, @@ -322,8 +279,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); if (ActorType.USER !== actor && inputSecret.type === SecretType.Personal) { throw new BadRequestError({ message: "Must be user to create personal secret" }); @@ -352,11 +308,8 @@ export const secretServiceFactory = ({ // validate tags // fetch all tags and if not same count throw error meaning one was invalid tags - const tags = inputSecret.tags - ? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags) - : []; - if ((inputSecret.tags || []).length !== tags.length) - throw new BadRequestError({ message: "Tag not found" }); + const tags = inputSecret.tags ? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags) : []; + if ((inputSecret.tags || []).length !== tags.length) throw new BadRequestError({ message: "Tag not found" }); const { secretName, type, ...el } = inputSecret; const secret = await secretDAL.transaction((tx) => @@ -384,14 +337,7 @@ export const secretServiceFactory = ({ return { ...secret[0], environment, workspace: projectId, tags }; }; - const updateSecret = async ({ - path, - actor, - actorId, - environment, - projectId, - ...inputSecret - }: TUpdateSecretDTO) => { + const updateSecret = async ({ path, actor, actorId, environment, projectId, ...inputSecret }: TUpdateSecretDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, @@ -403,8 +349,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); if (ActorType.USER !== actor && inputSecret.type === SecretType.Personal) { throw new BadRequestError({ message: "Must be user to create personal secret" }); @@ -441,11 +386,8 @@ export const secretServiceFactory = ({ projectId }); - const tags = inputSecret.tags - ? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags) - : []; - if ((inputSecret.tags || []).length !== tags.length) - throw new BadRequestError({ message: "Tag not found" }); + const tags = inputSecret.tags ? await secretTagDAL.findManyTagsById(projectId, inputSecret.tags) : []; + if ((inputSecret.tags || []).length !== tags.length) throw new BadRequestError({ message: "Tag not found" }); const { secretName, ...el } = inputSecret; const updatedSecret = await secretDAL.transaction(async (tx) => @@ -487,14 +429,7 @@ export const secretServiceFactory = ({ return { ...updatedSecret[0], workspace: projectId, environment }; }; - const deleteSecret = async ({ - path, - actor, - actorId, - environment, - projectId, - ...inputSecret - }: TDeleteSecretDTO) => { + const deleteSecret = async ({ path, actor, actorId, environment, projectId, ...inputSecret }: TDeleteSecretDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, @@ -506,8 +441,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "CreateSecret" }); if (ActorType.USER !== actor && inputSecret.type === SecretType.Personal) { throw new BadRequestError({ message: "Must be user to create personal secret" }); @@ -542,14 +476,7 @@ export const secretServiceFactory = ({ return { ...deletedSecret[0], _id: deletedSecret[0].id, workspace: projectId, environment }; }; - const getSecrets = async ({ - actorId, - path, - environment, - projectId, - actor, - includeImports - }: TGetSecretsDTO) => { + const getSecrets = async ({ actorId, path, environment, projectId, actor, includeImports }: TGetSecretsDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, @@ -683,8 +610,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({ inputSecrets, @@ -738,8 +664,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({ inputSecrets, @@ -809,8 +734,7 @@ export const secretServiceFactory = ({ const folderId = folder.id; const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); - if (!blindIndexCfg) - throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); + if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({ inputSecrets, @@ -838,46 +762,22 @@ export const secretServiceFactory = ({ return secretsDeleted; }; - const listSecretVersionsBySecretId = async ({ - actorId, - actor, - limit, - offset, - secretId - }: TListSecretVersionDTO) => { + const listSecretVersionsBySecretId = async ({ actorId, actor, limit, offset, secretId }: TListSecretVersionDTO) => { const secret = await secretDAL.findById(secretId); if (!secret) throw new BadRequestError({ message: "Failed to find secret" }); const folder = await folderDAL.findById(secret.folderId); if (!folder) throw new BadRequestError({ message: "Folder not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - folder.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, folder.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); - const secretVersions = await secretVersionDAL.find( - { secretId }, - { limit, offset, sort: [["createdAt", "desc"]] } - ); + const secretVersions = await secretVersionDAL.find({ secretId }, { limit, offset, sort: [["createdAt", "desc"]] }); return secretVersions; }; - const getSecretsRaw = async ({ - projectId, - path, - actor, - actorId, - environment, - includeImports - }: TGetSecretsRawDTO) => { + const getSecretsRaw = async ({ projectId, path, actor, actorId, environment, includeImports }: TGetSecretsRawDTO) => { const botKey = await projectBotService.getBotKey(projectId); - if (!botKey) - throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); const { secrets, imports } = await getSecrets({ actorId, @@ -911,8 +811,7 @@ export const secretServiceFactory = ({ version }: TGetASecretRawDTO) => { const botKey = await projectBotService.getBotKey(projectId); - if (!botKey) - throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); const secret = await getSecretByName({ actorId, @@ -941,8 +840,7 @@ export const secretServiceFactory = ({ skipMultilineEncoding }: TCreateSecretRawDTO) => { const botKey = await projectBotService.getBotKey(projectId); - if (!botKey) - throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secretName, botKey); const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey); @@ -986,8 +884,7 @@ export const secretServiceFactory = ({ skipMultilineEncoding }: TUpdateSecretRawDTO) => { const botKey = await projectBotService.getBotKey(projectId); - if (!botKey) - throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey); @@ -1021,8 +918,7 @@ export const secretServiceFactory = ({ secretPath }: TDeleteSecretRawDTO) => { const botKey = await projectBotService.getBotKey(projectId); - if (!botKey) - throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); const secret = await deleteSecret({ secretName, @@ -1040,33 +936,17 @@ export const secretServiceFactory = ({ return decryptSecretRaw(secret, botKey); }; - const getSecretVersions = async ({ - actorId, - actor, - limit = 20, - offset = 0, - secretId - }: TGetSecretVersionsDTO) => { + const getSecretVersions = async ({ actorId, actor, limit = 20, offset = 0, secretId }: TGetSecretVersionsDTO) => { const secret = await secretDAL.findById(secretId); if (!secret) throw new BadRequestError({ message: "Failed to find secret" }); const folder = await folderDAL.findById(secret.folderId); if (!folder) throw new BadRequestError({ message: "Failed to find secret" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - folder.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.SecretRollback - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, folder.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); - const secretVersions = await secretVersionDAL.find( - { secretId }, - { offset, limit, sort: [["createdAt", "desc"]] } - ); + const secretVersions = await secretVersionDAL.find({ secretId }, { offset, limit, sort: [["createdAt", "desc"]] }); return secretVersions; }; diff --git a/backend/src/services/secret/secret-types.ts b/backend/src/services/secret/secret-types.ts index f5fdf32a2..89e16d77b 100644 --- a/backend/src/services/secret/secret-types.ts +++ b/backend/src/services/secret/secret-types.ts @@ -1,20 +1,11 @@ import { Knex } from "knex"; -import { - SecretType, - TSecretBlindIndexes, - TSecrets, - TSecretsInsert, - TSecretsUpdate -} from "@app/db/schemas"; +import { SecretType, TSecretBlindIndexes, TSecrets, TSecretsInsert, TSecretsUpdate } from "@app/db/schemas"; import { TProjectPermission } from "@app/lib/types"; type TPartialSecret = Pick; -type TPartialInputSecret = Pick< - TSecrets, - "type" | "secretReminderNote" | "secretReminderRepeatDays" | "id" ->; +type TPartialInputSecret = Pick; export type TCreateSecretDTO = { secretName: string; diff --git a/backend/src/services/secret/secret-version-dal.ts b/backend/src/services/secret/secret-version-dal.ts index 339b8346b..7a6695e18 100644 --- a/backend/src/services/secret/secret-version-dal.ts +++ b/backend/src/services/secret/secret-version-dal.ts @@ -38,17 +38,11 @@ export const secretVersionDALFactory = (db: TDbClient) => { const findLatestVersionMany = async (folderId: string, secretIds: string[], tx?: Knex) => { try { - const docs: Array = await (tx || db)( - TableName.SecretVersion - ) + const docs: Array = await (tx || db)(TableName.SecretVersion) .where("folderId", folderId) .whereIn(`${TableName.SecretVersion}.secretId`, secretIds) .join( - (tx || db)(TableName.SecretVersion) - .groupBy("secretId") - .max("version") - .select("secretId") - .as("latestVersion"), + (tx || db)(TableName.SecretVersion).groupBy("secretId").max("version").select("secretId").as("latestVersion"), (bd) => { bd.on(`${TableName.SecretVersion}.secretId`, "latestVersion.secretId").andOn( `${TableName.SecretVersion}.version`, diff --git a/backend/src/services/service-token/service-token-service.ts b/backend/src/services/service-token/service-token-service.ts index 63d5f6aac..9a4fd6afe 100644 --- a/backend/src/services/service-token/service-token-service.ts +++ b/backend/src/services/service-token/service-token-service.ts @@ -4,10 +4,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import bcrypt from "bcrypt"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; @@ -50,10 +47,7 @@ export const serviceTokenServiceFactory = ({ encryptedKey }: TCreateServiceTokenDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.ServiceTokens - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens); scopes.forEach(({ environment, secretPath }) => { ForbiddenError.from(permission).throwUnlessCan( @@ -67,8 +61,7 @@ export const serviceTokenServiceFactory = ({ // validates env const scopeEnvs = [...new Set(scopes.map(({ environment }) => environment))]; const inputEnvs = await projectEnvDAL.findBySlugs(projectId, scopeEnvs); - if (inputEnvs.length !== scopeEnvs.length) - throw new BadRequestError({ message: "Environment not found" }); + if (inputEnvs.length !== scopeEnvs.length) throw new BadRequestError({ message: "Environment not found" }); const secret = crypto.randomBytes(16).toString("hex"); const secretHash = await bcrypt.hash(secret, appCfg.SALT_ROUNDS); @@ -101,23 +94,15 @@ export const serviceTokenServiceFactory = ({ const serviceToken = await serviceTokenDAL.findById(id); if (!serviceToken) throw new BadRequestError({ message: "Token not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - serviceToken.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.ServiceTokens - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, serviceToken.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.ServiceTokens); const deletedServiceToken = await serviceTokenDAL.deleteById(id); return deletedServiceToken; }; const getServiceToken = async ({ actor, actorId }: TGetServiceTokenInfoDTO) => { - if (actor !== ActorType.SERVICE) - throw new BadRequestError({ message: "Service token not found" }); + if (actor !== ActorType.SERVICE) throw new BadRequestError({ message: "Service token not found" }); const serviceToken = await serviceTokenDAL.findById(actorId); if (!serviceToken) throw new BadRequestError({ message: "Token not found" }); @@ -128,18 +113,11 @@ export const serviceTokenServiceFactory = ({ return { serviceToken, user: serviceTokenUser }; }; - const getProjectServiceTokens = async ({ - actorId, - actor, - projectId - }: TProjectServiceTokensDTO) => { + const getProjectServiceTokens = async ({ actorId, actor, projectId }: TProjectServiceTokensDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.ServiceTokens - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens); - const tokens = await serviceTokenDAL.find({ projectId }); + const tokens = await serviceTokenDAL.find({ projectId }, { sort: [["createdAt", "desc"]] }); return tokens; }; diff --git a/backend/src/services/smtp/smtp-service.ts b/backend/src/services/smtp/smtp-service.ts index 37c5a98a9..142993ceb 100644 --- a/backend/src/services/smtp/smtp-service.ts +++ b/backend/src/services/smtp/smtp-service.ts @@ -41,8 +41,8 @@ export const getTlsOption = (host?: SmtpHost | string, secure?: boolean) => { if (!secure) return { secure: false }; if (!host) return { secure: true }; - if (host === SmtpHost.Sendgrid) { - return { secure: true, port: 465}; // more details here https://nodemailer.com/smtp/ + if ((host as SmtpHost) === SmtpHost.Sendgrid) { + return { secure: true, port: 465 }; // more details here https://nodemailer.com/smtp/ } if (host.includes("amazonaws.com")) { return { tls: { ciphers: "TLSv1.2" } }; diff --git a/backend/src/services/super-admin/super-admin-service.ts b/backend/src/services/super-admin/super-admin-service.ts index a549cfac5..c31d55d46 100644 --- a/backend/src/services/super-admin/super-admin-service.ts +++ b/backend/src/services/super-admin/super-admin-service.ts @@ -19,8 +19,7 @@ export type TSuperAdminServiceFactory = ReturnType; export const getServerCfg = () => { - if (!serverCfg) - throw new BadRequestError({ name: "Get server cfg", message: "Server cfg not initialized" }); + if (!serverCfg) throw new BadRequestError({ name: "Get server cfg", message: "Server cfg not initialized" }); return serverCfg; }; @@ -64,8 +63,7 @@ export const superAdminServiceFactory = ({ userAgent }: TAdminSignUpDTO) => { const existingUser = await userDAL.findOne({ email }); - if (existingUser) - throw new BadRequestError({ name: "Admin sign up", message: "User already exist" }); + if (existingUser) throw new BadRequestError({ name: "Admin sign up", message: "User already exist" }); const userInfo = await userDAL.transaction(async (tx) => { const newUser = await userDAL.create( diff --git a/backend/src/services/telemetry/telemetry-service.ts b/backend/src/services/telemetry/telemetry-service.ts index 82e912deb..c386abd95 100644 --- a/backend/src/services/telemetry/telemetry-service.ts +++ b/backend/src/services/telemetry/telemetry-service.ts @@ -51,7 +51,7 @@ To opt into telemetry, you can set "TELEMETRY_ENABLED=true" within the environme } }; - const sendPostHogEvents = async (event: TPostHogEvent) => { + const sendPostHogEvents = (event: TPostHogEvent) => { if (postHog) { postHog.capture({ event: event.event, diff --git a/backend/src/services/user/user-dal.ts b/backend/src/services/user/user-dal.ts index 8051cee28..0de490399 100644 --- a/backend/src/services/user/user-dal.ts +++ b/backend/src/services/user/user-dal.ts @@ -24,11 +24,7 @@ export const userDALFactory = (db: TDbClient) => { try { return await db(TableName.Users) .where({ email }) - .join( - TableName.UserEncryptionKey, - `${TableName.Users}.id`, - `${TableName.UserEncryptionKey}.userId` - ) + .join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`) .first(); } catch (error) { throw new DatabaseError({ error, name: "Find user enc by email" }); @@ -39,11 +35,7 @@ export const userDALFactory = (db: TDbClient) => { try { const user = await db(TableName.Users) .where(`${TableName.Users}.id`, userId) - .join( - TableName.UserEncryptionKey, - `${TableName.Users}.id`, - `${TableName.UserEncryptionKey}.userId` - ) + .join(TableName.UserEncryptionKey, `${TableName.Users}.id`, `${TableName.UserEncryptionKey}.userId`) .first(); if (user?.id) { // change to user id @@ -64,11 +56,7 @@ export const userDALFactory = (db: TDbClient) => { } }; - const updateUserEncryptionByUserId = async ( - userId: string, - data: TUserEncryptionKeysUpdate, - tx?: Knex - ) => { + const updateUserEncryptionByUserId = async (userId: string, data: TUserEncryptionKeysUpdate, tx?: Knex) => { try { const [userEnc] = await (tx || db)(TableName.UserEncryptionKey) .where({ userId }) @@ -86,10 +74,7 @@ export const userDALFactory = (db: TDbClient) => { tx?: Knex ) => { try { - const [userEnc] = await (tx - ? tx(TableName.UserEncryptionKey) - : db(TableName.UserEncryptionKey) - ) + const [userEnc] = await (tx ? tx(TableName.UserEncryptionKey) : db(TableName.UserEncryptionKey)) // if user insert make sure to pass all required data .insert({ userId, ...data } as TUserEncryptionKeys) .onConflict("userId") diff --git a/backend/src/services/user/user-service.ts b/backend/src/services/user/user-service.ts index 81c7ea9d2..b700869c2 100644 --- a/backend/src/services/user/user-service.ts +++ b/backend/src/services/user/user-service.ts @@ -31,9 +31,7 @@ export const userServiceFactory = ({ userDAL }: TUserServiceFactoryDep) => { if (!user) throw new BadRequestError({ name: "Update auth methods" }); const hasSamlEnabled = user?.authMethods?.some((method) => - [AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes( - method as AuthMethod - ) + [AuthMethod.OKTA_SAML, AuthMethod.AZURE_SAML, AuthMethod.JUMPCLOUD_SAML].includes(method as AuthMethod) ); if (hasSamlEnabled) throw new BadRequestError({ diff --git a/backend/src/services/webhook/webhook-dal.ts b/backend/src/services/webhook/webhook-dal.ts index 14abcac5a..c33d79fdb 100644 --- a/backend/src/services/webhook/webhook-dal.ts +++ b/backend/src/services/webhook/webhook-dal.ts @@ -63,21 +63,16 @@ export const webhookDALFactory = (db: TDbClient) => { } }; - const findAllWebhooks = async ( - projectId: string, - environment?: string, - secretPath?: string, - tx?: Knex - ) => { + const findAllWebhooks = async (projectId: string, environment?: string, secretPath?: string, tx?: Knex) => { try { const webhooks = await (tx || db)(TableName.Webhook) .where(`${TableName.Environment}.projectId`, projectId) .where((qb) => { if (environment) { - qb.where("slug", environment); + void qb.where("slug", environment); } if (secretPath) { - qb.where("secretPath", secretPath); + void qb.where("secretPath", secretPath); } }) .join(TableName.Environment, `${TableName.Webhook}.envId`, `${TableName.Environment}.id`) @@ -85,7 +80,8 @@ export const webhookDALFactory = (db: TDbClient) => { .select(db.ref("slug").withSchema(TableName.Environment).as("envSlug")) .select(db.ref("id").withSchema(TableName.Environment).as("envId")) .select(db.ref("projectId").withSchema(TableName.Environment)) - .select(selectAllTableCols(TableName.Webhook)); + .select(selectAllTableCols(TableName.Webhook)) + .orderBy(`${TableName.Webhook}.createdAt`, "asc"); return webhooks.map(({ envId, envSlug, envName, ...el }) => ({ ...el, @@ -103,9 +99,7 @@ export const webhookDALFactory = (db: TDbClient) => { const bulkUpdate = async (data: Array, tx?: Knex) => { try { - const queries = data.map(({ id, ...el }) => - (tx || db)(TableName.Webhook).where({ id }).update(el) - ); + const queries = data.map(({ id, ...el }) => (tx || db)(TableName.Webhook).where({ id }).update(el)); const docs = await Promise.all(queries); return docs; } catch (error) { diff --git a/backend/src/services/webhook/webhook-fns.ts b/backend/src/services/webhook/webhook-fns.ts index a43d1c969..35d2ba7fc 100644 --- a/backend/src/services/webhook/webhook-fns.ts +++ b/backend/src/services/webhook/webhook-fns.ts @@ -1,5 +1,6 @@ import crypto from "node:crypto"; +import { AxiosError } from "axios"; import picomatch from "picomatch"; import { SecretKeyEncoding, TWebhooks } from "@app/db/schemas"; @@ -43,10 +44,7 @@ export const triggerWebhookRequest = async ( }); } if (secretKey) { - const webhookSign = crypto - .createHmac("sha256", secretKey) - .update(JSON.stringify(payload)) - .digest("hex"); + const webhookSign = crypto.createHmac("sha256", secretKey).update(JSON.stringify(payload)).digest("hex"); headers["x-infisical-signature"] = `t=${payload.timestamp};${webhookSign}`; } } @@ -97,10 +95,7 @@ export const fnTriggerWebhook = async ({ logger.info("Secret webhook job started", { environment, secretPath, projectId }); const webhooksTriggered = await Promise.allSettled( toBeTriggeredHooks.map((hook) => - triggerWebhookRequest( - hook, - getWebhookPayload("secrets.modified", projectId, environment, secretPath) - ) + triggerWebhookRequest(hook, getWebhookPayload("secrets.modified", projectId, environment, secretPath)) ) ); // filter hooks by status @@ -111,7 +106,7 @@ export const fnTriggerWebhook = async ({ .filter(({ status }) => status === "rejected") .map((data, i) => ({ id: toBeTriggeredHooks[i].id, - error: data.status === "rejected" && data.reason.message + error: data.status === "rejected" ? (data.reason as AxiosError).message : "" })); await webhookDAL.transaction(async (tx) => { diff --git a/backend/src/services/webhook/webhook-service.ts b/backend/src/services/webhook/webhook-service.ts index e176cc865..c208ba472 100644 --- a/backend/src/services/webhook/webhook-service.ts +++ b/backend/src/services/webhook/webhook-service.ts @@ -2,10 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import { SecretEncryptionAlgo, SecretKeyEncoding, TWebhooksInsert } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { - ProjectPermissionActions, - ProjectPermissionSub -} from "@app/ee/services/permission/project-permission"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; import { encryptSymmetric, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; import { BadRequestError } from "@app/lib/errors"; @@ -29,11 +26,7 @@ type TWebhookServiceFactoryDep = { export type TWebhookServiceFactory = ReturnType; -export const webhookServiceFactory = ({ - webhookDAL, - projectEnvDAL, - permissionService -}: TWebhookServiceFactoryDep) => { +export const webhookServiceFactory = ({ webhookDAL, projectEnvDAL, permissionService }: TWebhookServiceFactoryDep) => { const createWebhook = async ({ actor, actorId, @@ -44,10 +37,7 @@ export const webhookServiceFactory = ({ webhookSecretKey }: TCreateWebhookDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, - ProjectPermissionSub.Webhooks - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Webhooks); const env = await projectEnvDAL.findOne({ projectId, slug: environment }); if (!env) throw new BadRequestError({ message: "Env not found" }); @@ -69,10 +59,7 @@ export const webhookServiceFactory = ({ insertDoc.algorithm = SecretEncryptionAlgo.AES_256_GCM; insertDoc.keyEncoding = SecretKeyEncoding.BASE64; } else if (encryptionKey) { - const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8( - webhookSecretKey, - encryptionKey - ); + const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8(webhookSecretKey, encryptionKey); insertDoc.encryptedSecretKey = ciphertext; insertDoc.iv = iv; insertDoc.tag = tag; @@ -89,15 +76,8 @@ export const webhookServiceFactory = ({ const webhook = await webhookDAL.findById(id); if (!webhook) throw new BadRequestError({ message: "Webhook not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - webhook.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Edit, - ProjectPermissionSub.Webhooks - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, webhook.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks); const updatedWebhook = await webhookDAL.updateById(id, { isDisabled }); return { ...webhook, ...updatedWebhook }; @@ -107,15 +87,8 @@ export const webhookServiceFactory = ({ const webhook = await webhookDAL.findById(id); if (!webhook) throw new BadRequestError({ message: "Webhook not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - webhook.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Delete, - ProjectPermissionSub.Webhooks - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, webhook.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks); const deletedWebhook = await webhookDAL.deleteById(id); return { ...webhook, ...deletedWebhook }; @@ -125,15 +98,8 @@ export const webhookServiceFactory = ({ const webhook = await webhookDAL.findById(id); if (!webhook) throw new BadRequestError({ message: "Webhook not found" }); - const { permission } = await permissionService.getProjectPermission( - actor, - actorId, - webhook.projectId - ); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Webhooks - ); + const { permission } = await permissionService.getProjectPermission(actor, actorId, webhook.projectId); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks); let webhookError: string | undefined; try { @@ -152,18 +118,9 @@ export const webhookServiceFactory = ({ return { ...webhook, ...updatedWebhook }; }; - const listWebhooks = async ({ - actorId, - actor, - projectId, - secretPath, - environment - }: TListWebhookDTO) => { + const listWebhooks = async ({ actorId, actor, projectId, secretPath, environment }: TListWebhookDTO) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.Webhooks - ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks); return webhookDAL.findAllWebhooks(projectId, environment, secretPath); }; diff --git a/docker-compose.pg.yml b/docker-compose.pg.yml index 353b6177b..8ef538f08 100644 --- a/docker-compose.pg.yml +++ b/docker-compose.pg.yml @@ -34,6 +34,17 @@ services: volumes: - redis_data:/data + redis-commander: + container_name: infisical-dev-redis-commander + image: rediscommander/redis-commander + restart: always + depends_on: + - redis + environment: + - REDIS_HOSTS=local:redis:6379 + ports: + - "8085:8081" + db-test: profiles: ["test"] image: postgres:14-alpine @@ -96,32 +107,32 @@ services: - 1025:1025 # SMTP server - 8025:8025 # Web UI - mongo: - image: mongo - container_name: infisical-dev-mongo - restart: always - env_file: .env - environment: - - MONGO_INITDB_ROOT_USERNAME=root - - MONGO_INITDB_ROOT_PASSWORD=example - volumes: - - mongo-data:/data/db - ports: - - 27017:27017 - - mongo-express: - container_name: infisical-dev-mongo-express - image: mongo-express - restart: always - depends_on: - - mongo - env_file: .env - environment: - - ME_CONFIG_MONGODB_ADMINUSERNAME=root - - ME_CONFIG_MONGODB_ADMINPASSWORD=example - - ME_CONFIG_MONGODB_URL=mongodb://root:example@mongo:27017/ - ports: - - 8081:8081 + # mongo: + # image: mongo + # container_name: infisical-dev-mongo + # restart: always + # env_file: .env + # environment: + # - MONGO_INITDB_ROOT_USERNAME=root + # - MONGO_INITDB_ROOT_PASSWORD=example + # volumes: + # - mongo-data:/data/db + # ports: + # - 27017:27017 + # + # mongo-express: + # container_name: infisical-dev-mongo-express + # image: mongo-express + # restart: always + # depends_on: + # - mongo + # env_file: .env + # environment: + # - ME_CONFIG_MONGODB_ADMINUSERNAME=root + # - ME_CONFIG_MONGODB_ADMINPASSWORD=example + # - ME_CONFIG_MONGODB_URL=mongodb://root:example@mongo:27017/ + # ports: + # - 8081:8081 volumes: postgres-data: diff --git a/docs/cli/faq.mdx b/docs/cli/faq.mdx index 529ec70ab..cf95457c9 100644 --- a/docs/cli/faq.mdx +++ b/docs/cli/faq.mdx @@ -23,3 +23,12 @@ Yes. If you have previously retrieved secrets for a specific project and environ Yes. This is simply a configuration file and contains no sensitive data. + + + + Visit the Infisical website and navigate to a project of your choice. Once on the project page, access the **Project Settings** from the sidebar. Within the Project name section, click the "Copy Project ID" button for copying the current Project ID to clipboard, or simply obtain it from the URL of the current page. + + ``` + https://app.infisical.com/project//settings + ``` + diff --git a/docs/mint.json b/docs/mint.json index 9e3701519..1f8dc618b 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -1,9 +1,8 @@ { "name": "Infisical", - "basePath": "/docs", "logo": { - "dark": "/docs/logo/dark.svg", - "light": "/docs/logo/light.svg", + "dark": "/logo/dark.svg", + "light": "/logo/light.svg", "href": "https://infisical.com" }, "favicon": "/favicon.png", diff --git a/frontend/src/hooks/api/organization/queries.tsx b/frontend/src/hooks/api/organization/queries.tsx index 5b8b145c2..0edaf2c2f 100644 --- a/frontend/src/hooks/api/organization/queries.tsx +++ b/frontend/src/hooks/api/organization/queries.tsx @@ -328,7 +328,7 @@ export const useCreateCustomerPortalSession = () => { return useMutation({ mutationFn: async (organizationId: string) => { const { data } = await apiRequest.post( - `/api/v1/organization/${organizationId}/customer-portal-session` + `/api/v1/organizations/${organizationId}/customer-portal-session` ); return data; } diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/ProjectNameChangeSection/ProjectNameChangeSection.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/ProjectNameChangeSection/ProjectNameChangeSection.tsx index 6055a9f33..719129ebf 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/ProjectNameChangeSection/ProjectNameChangeSection.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/ProjectNameChangeSection/ProjectNameChangeSection.tsx @@ -1,5 +1,7 @@ import { useEffect } from "react"; import { Controller, useForm } from "react-hook-form"; +import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; @@ -7,6 +9,7 @@ import { useNotificationContext } from "@app/components/context/Notifications/No import { ProjectPermissionCan } from "@app/components/permissions"; import { Button, FormControl, Input } from "@app/components/v2"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; +import { useToggle } from "@app/hooks"; import { useRenameWorkspace } from "@app/hooks/api"; const formSchema = yup.object({ @@ -19,6 +22,7 @@ export const ProjectNameChangeSection = () => { const { createNotification } = useNotificationContext(); const { currentWorkspace } = useWorkspace(); const { mutateAsync, isLoading } = useRenameWorkspace(); + const [isProjectIdCopied, setIsProjectIdCopied] = useToggle(false); const { handleSubmit, control, reset } = useForm({ resolver: yupResolver(formSchema) }); @@ -30,6 +34,16 @@ export const ProjectNameChangeSection = () => { } }, [currentWorkspace]); + useEffect(() => { + let timer: NodeJS.Timeout; + + if (isProjectIdCopied) { + timer = setTimeout(() => setIsProjectIdCopied.off(), 2000); + } + + return () => clearTimeout(timer); +}, [setIsProjectIdCopied]); + const onFormSubmit = async ({ name }: FormData) => { try { if (!currentWorkspace?.id) return; @@ -52,12 +66,38 @@ export const ProjectNameChangeSection = () => { } }; + const copyProjectIdToClipboard = () => { + navigator.clipboard.writeText(currentWorkspace?.id || ""); + setIsProjectIdCopied.on(); + + createNotification({ + text: "Copied Project ID to clipboard", + type: "success" + }); + } + return (
-

Project Name

+
+

Project Name

+
+ +
+
+