From fe2f2f972e7755a187395bf5a28c2654be12508a Mon Sep 17 00:00:00 2001 From: Jon Insley <5669143+jinsley8@users.noreply.github.com> Date: Wed, 25 Oct 2023 13:54:09 -0400 Subject: [PATCH 001/525] fix(frontend): Remove max-width to match other views This commit removes the max-width constraint on the WebhooksTab.tsx component, aligning it with the full-width layout consistency seen in other views. The previous max-width of 1024px resulted in unused space on larger screens. --- .../ProjectSettingsPage/components/WebhooksTab/WebhooksTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/WebhooksTab/WebhooksTab.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/WebhooksTab/WebhooksTab.tsx index 6fc6b230c1..3f24d7c200 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/WebhooksTab/WebhooksTab.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/WebhooksTab/WebhooksTab.tsx @@ -139,7 +139,7 @@ export const WebhooksTab = withProjectPermission( }; return ( -
+

{t("settings.webhooks.title")}

Date: Wed, 10 Jan 2024 22:33:38 +0000 Subject: [PATCH 002/525] docs: cover ansible forking error --- docs/integrations/platforms/ansible.mdx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/integrations/platforms/ansible.mdx b/docs/integrations/platforms/ansible.mdx index efb9e4f638..93f1e52027 100644 --- a/docs/integrations/platforms/ansible.mdx +++ b/docs/integrations/platforms/ansible.mdx @@ -5,6 +5,20 @@ description: "How to use Infisical for secret management in Ansible" The documentation for using Infisical to manage secrets in Ansible is currently available [here](https://galaxy.ansible.com/ui/repo/published/infisical/vault/). + +If you get this Python error when you running the lookup plugin:- + +``` +objc[72832]: +[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug. +Fatal Python error: Aborted +``` + +You will need to add this to your shell environment or ansible wrapper script:- + +``` +export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES +``` + Have any questions? Join Infisical's [community Slack](https://infisical.com/slack) for quick support. From 4057e2c6ab02c26c31e25d05396118219531f6c3 Mon Sep 17 00:00:00 2001 From: quinton Date: Wed, 24 Jan 2024 19:05:16 +0000 Subject: [PATCH 003/525] feat: cli export allow filtering with tags --- cli/packages/cmd/export.go | 16 ++++++---------- cli/packages/util/secrets.go | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/cli/packages/cmd/export.go b/cli/packages/cmd/export.go index d0db485b38..26cc45f65e 100644 --- a/cli/packages/cmd/export.go +++ b/cli/packages/cmd/export.go @@ -87,16 +87,12 @@ var exportCmd = &cobra.Command{ var output string if shouldExpandSecrets { - substitutions := util.ExpandSecrets(secrets, infisicalToken, "") - output, err = formatEnvs(substitutions, format) - if err != nil { - util.HandleError(err) - } - } else { - output, err = formatEnvs(secrets, format) - if err != nil { - util.HandleError(err) - } + secrets = util.ExpandSecrets(secrets, infisicalToken, "") + } + secrets = util.FilterSecretsByTag(secrets, tagSlugs) + output, err = formatEnvs(secrets, format) + if err != nil { + util.HandleError(err) } fmt.Print(output) diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index a260aaaec4..36cddbef8c 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -220,6 +220,30 @@ func InjectImportedSecret(plainTextWorkspaceKey []byte, secrets []models.SingleE return secrets, nil } +func FilterSecretsByTag(plainTextSecrets []models.SingleEnvironmentVariable, tagSlugs string) []models.SingleEnvironmentVariable { + if tagSlugs == "" { + return plainTextSecrets + } + + tagSlugsMap := make(map[string]bool) + tagSlugsList := strings.Split(tagSlugs, ",") + for _, slug := range tagSlugsList { + tagSlugsMap[slug] = true + } + + filteredSecrets := []models.SingleEnvironmentVariable{} + for _, secret := range plainTextSecrets { + for _, tag := range secret.Tags { + if tagSlugsMap[tag.Slug] { + filteredSecrets = append(filteredSecrets, secret) + break + } + } + } + + return filteredSecrets +} + func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectConfigFilePath string) ([]models.SingleEnvironmentVariable, error) { var infisicalToken string if params.InfisicalToken == "" { From 6ff5fb69d45583a4746223e9b6a06edc4f54a5e1 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Sat, 27 Jan 2024 15:06:50 +0000 Subject: [PATCH 004/525] fix: backend/package.json & backend/package-lock.json to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-AXIOS-6124857 - https://snyk.io/vuln/SNYK-JS-AXIOS-6144788 - https://snyk.io/vuln/SNYK-JS-FASTIFYSWAGGERUI-6157561 - https://snyk.io/vuln/SNYK-JS-INFLIGHT-6095116 --- backend/package-lock.json | 1256 ++++++------------------------------- backend/package.json | 6 +- 2 files changed, 207 insertions(+), 1055 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 040b4619f7..a34b075897 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -19,7 +19,7 @@ "@fastify/rate-limit": "^9.0.0", "@fastify/session": "^10.7.0", "@fastify/swagger": "^8.12.0", - "@fastify/swagger-ui": "^1.10.1", + "@fastify/swagger-ui": "^2.1.0", "@node-saml/passport-saml": "^4.0.4", "@octokit/rest": "^20.0.2", "@octokit/webhooks-types": "^7.3.1", @@ -28,7 +28,7 @@ "ajv": "^8.12.0", "argon2": "^0.31.2", "aws-sdk": "^2.1532.0", - "axios": "^1.6.2", + "axios": "^1.6.4", "axios-retry": "^4.0.0", "bcrypt": "^5.1.1", "bullmq": "^5.1.1", @@ -56,7 +56,7 @@ "picomatch": "^3.0.1", "pino": "^8.16.2", "posthog-node": "^3.6.0", - "probot": "^12.3.3", + "probot": "^13.0.0", "smee-client": "^2.0.0", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1", @@ -1299,9 +1299,9 @@ } }, "node_modules/@fastify/swagger-ui": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-1.10.1.tgz", - "integrity": "sha512-u3EJqNKvVr3X+6jY5i6pbs6/tXCrSlqc2Y+PVjnHBTOGh/d36uHMz+z4jPFy9gie2my6iHUrAdM8itlVmoUjog==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-2.1.0.tgz", + "integrity": "sha512-mu0C28kMEQDa3miE8f3LmI/OQSmqaKS3dYhZVFO5y4JdgBIPbzZj6COCoRU/P/9nu7UogzzcCJtg89wwLwKtWg==", "dependencies": { "@fastify/static": "^6.0.0", "fastify-plugin": "^4.0.0", @@ -1694,297 +1694,77 @@ } }, "node_modules/@octokit/auth-app": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-4.0.13.tgz", - "integrity": "sha512-NBQkmR/Zsc+8fWcVIFrwDgNXS7f4XDrkd9LHdi9DPQw1NdGHLviLzRO2ZBwTtepnwHXW5VTrVU9eFGijMUqllg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.0.3.tgz", + "integrity": "sha512-9N7IlBAKEJR3tJgPSubCxIDYGXSdc+2xbkjYpk9nCyqREnH8qEMoMhiEB1WgoA9yTFp91El92XNXAi+AjuKnfw==", "dependencies": { - "@octokit/auth-oauth-app": "^5.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", + "@octokit/auth-oauth-app": "^7.0.0", + "@octokit/auth-oauth-user": "^4.0.0", + "@octokit/request": "^8.0.2", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", "deprecation": "^2.3.1", - "lru-cache": "^9.0.0", - "universal-github-app-jwt": "^1.1.1", + "lru-cache": "^10.0.0", + "universal-github-app-jwt": "^1.1.2", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-app/node_modules/@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", - "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-app/node_modules/@octokit/openapi-types": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz", - "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==" - }, - "node_modules/@octokit/auth-app/node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", - "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-app/node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", - "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-app/node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", - "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "node": ">= 18" } }, "node_modules/@octokit/auth-app/node_modules/lru-cache": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", - "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "engines": { "node": "14 || >=16.14" } }, "node_modules/@octokit/auth-oauth-app": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-5.0.6.tgz", - "integrity": "sha512-SxyfIBfeFcWd9Z/m1xa4LENTQ3l1y6Nrg31k2Dcb1jS5ov7pmwMJZ6OGX8q3K9slRgVpeAjNA1ipOAMHkieqyw==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.0.1.tgz", + "integrity": "sha512-RE0KK0DCjCHXHlQBoubwlLijXEKfhMhKm9gO56xYvFmP1QTMb+vvwRPmQLLx0V+5AvV9N9I3lr1WyTzwL3rMDg==", "dependencies": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", + "@octokit/auth-oauth-device": "^6.0.0", + "@octokit/auth-oauth-user": "^4.0.0", + "@octokit/request": "^8.0.2", + "@octokit/types": "^12.0.0", "@types/btoa-lite": "^1.0.0", "btoa-lite": "^1.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", - "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/openapi-types": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz", - "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==" - }, - "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", - "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", - "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", - "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "node": ">= 18" } }, "node_modules/@octokit/auth-oauth-device": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-4.0.5.tgz", - "integrity": "sha512-XyhoWRTzf2ZX0aZ52a6Ew5S5VBAfwwx1QnC2Np6Et3MWQpZjlREIcbcvVZtkNuXp6Z9EeiSLSDUqm3C+aMEHzQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.0.1.tgz", + "integrity": "sha512-yxU0rkL65QkjbqQedgVx3gmW7YM5fF+r5uaSj9tM/cQGVqloXcqP2xK90eTyYvl29arFVCW8Vz4H/t47mL0ELw==", "dependencies": { - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", + "@octokit/oauth-methods": "^4.0.0", + "@octokit/request": "^8.0.0", + "@octokit/types": "^12.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", - "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/openapi-types": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz", - "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==" - }, - "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", - "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", - "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", - "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "node": ">= 18" } }, "node_modules/@octokit/auth-oauth-user": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-2.1.2.tgz", - "integrity": "sha512-kkRqNmFe7s5GQcojE3nSlF+AzYPpPv7kvP/xYEnE57584pixaFBH8Vovt+w5Y3E4zWUEOxjdLItmBTFAWECPAg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.0.1.tgz", + "integrity": "sha512-N94wWW09d0hleCnrO5wt5MxekatqEJ4zf+1vSe8MKMrhZ7gAXKFOKrDEZW2INltvBWJCyDUELgGRv8gfErH1Iw==", "dependencies": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", + "@octokit/auth-oauth-device": "^6.0.0", + "@octokit/oauth-methods": "^4.0.0", + "@octokit/request": "^8.0.2", + "@octokit/types": "^12.0.0", "btoa-lite": "^1.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", - "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/openapi-types": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz", - "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==" - }, - "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", - "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", - "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", - "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "node": ">= 18" } }, "node_modules/@octokit/auth-token": { @@ -1996,41 +1776,15 @@ } }, "node_modules/@octokit/auth-unauthenticated": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-3.0.5.tgz", - "integrity": "sha512-yH2GPFcjrTvDWPwJWWCh0tPPtTL5SMgivgKPA+6v/XmYN6hGQkAto8JtZibSKOpf8ipmeYhLNWQ2UgW0GYILCw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-5.0.1.tgz", + "integrity": "sha512-oxeWzmBFxWd+XolxKTc4zr+h3mt+yofn4r7OfoIkR/Cj/o70eEGmPsFbueyJE2iBAGpjgTnEOKM3pnuEGVmiqg==", "dependencies": { - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0" + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/openapi-types": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz", - "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==" - }, - "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", - "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", - "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "node": ">= 18" } }, "node_modules/@octokit/core": { @@ -2076,81 +1830,26 @@ } }, "node_modules/@octokit/oauth-authorization-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-5.0.0.tgz", - "integrity": "sha512-y1WhN+ERDZTh0qZ4SR+zotgsQUE1ysKnvBt1hvDRB2WRzYtVKQjn97HEPzoehh66Fj9LwNdlZh+p6TJatT0zzg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz", + "integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA==", "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/oauth-methods": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-2.0.6.tgz", - "integrity": "sha512-l9Uml2iGN2aTWLZcm8hV+neBiFXAQ9+3sKiQe/sgumHlL6HDg0AQ8/l16xX/5jJvfxueqTW5CWbzd0MjnlfHZw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.0.1.tgz", + "integrity": "sha512-1NdTGCoBHyD6J0n2WGXg9+yDLZrRNZ0moTEex/LSPr49m530WNKcCfXDghofYptr3st3eTii+EHoG5k/o+vbtw==", "dependencies": { - "@octokit/oauth-authorization-url": "^5.0.0", - "@octokit/request": "^6.2.3", - "@octokit/request-error": "^3.0.3", - "@octokit/types": "^9.0.0", + "@octokit/oauth-authorization-url": "^6.0.2", + "@octokit/request": "^8.0.2", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", "btoa-lite": "^1.0.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/oauth-methods/node_modules/@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", - "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/oauth-methods/node_modules/@octokit/openapi-types": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz", - "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==" - }, - "node_modules/@octokit/oauth-methods/node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", - "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/oauth-methods/node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", - "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/oauth-methods/node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", - "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "node": ">= 18" } }, "node_modules/@octokit/openapi-types": { @@ -2159,35 +1858,15 @@ "integrity": "sha512-6G+ywGClliGQwRsjvqVYpklIfa7oRPA0vyhPQG/1Feh+B+wU0vGH1JiJ5T25d3g1JZYBHzR2qefLi9x8Gt+cpw==" }, "node_modules/@octokit/plugin-enterprise-compatibility": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-compatibility/-/plugin-enterprise-compatibility-1.3.0.tgz", - "integrity": "sha512-h34sMGdEOER/OKrZJ55v26ntdHb9OPfR1fwOx6Q4qYyyhWA104o11h9tFxnS/l41gED6WEI41Vu2G2zHDVC5lQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-compatibility/-/plugin-enterprise-compatibility-4.0.1.tgz", + "integrity": "sha512-d5cqeO0F/xZsTxOPOTYdw+0x8p+9GuTGGPj7oGj3y9vLluGnd7q97PTEzeJnOSERrhS4DguihQmrGu+7PhVP9Q==", "dependencies": { - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.0.3" - } - }, - "node_modules/@octokit/plugin-enterprise-compatibility/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/@octokit/plugin-enterprise-compatibility/node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/@octokit/plugin-enterprise-compatibility/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0" + }, + "engines": { + "node": ">= 18" } }, "node_modules/@octokit/plugin-paginate-rest": { @@ -2230,25 +1909,34 @@ } }, "node_modules/@octokit/plugin-retry": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz", - "integrity": "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.1.tgz", + "integrity": "sha512-SKs+Tz9oj0g4p28qkZwl/topGcb0k0qPNX/i7vBKmDsjoeqnVfFUquqrE/O9oJY7+oLzdCtkiWSXLpLjvl6uog==", "dependencies": { - "@octokit/types": "^6.0.3", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" } }, - "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "node_modules/@octokit/plugin-throttling": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.1.3.tgz", + "integrity": "sha512-pfyqaqpc0EXh5Cn4HX9lWYsZ4gGbjnSmUILeu4u2gnuM50K/wIk9s1Pxt3lVeVwekmITgN/nJdoh43Ka+vye8A==", "dependencies": { - "@octokit/openapi-types": "^12.11.0" + "@octokit/types": "^12.2.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5.0.0" } }, "node_modules/@octokit/request": { @@ -2301,53 +1989,36 @@ } }, "node_modules/@octokit/webhooks": { - "version": "9.26.3", - "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.3.tgz", - "integrity": "sha512-DLGk+gzeVq5oK89Bo601txYmyrelMQ7Fi5EnjHE0Xs8CWicy2xkmnJMKptKJrBJpstqbd/9oeDFi/Zj2pudBDQ==", + "version": "12.0.11", + "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-12.0.11.tgz", + "integrity": "sha512-YEQOb7v0TZ662nh5jsbY1CMgJyMajCEagKrHWC30LTCwCtnuIrLtEpE20vq4AtH0SuZI90+PtV66/Bnnw0jkvg==", "dependencies": { - "@octokit/request-error": "^2.0.2", - "@octokit/webhooks-methods": "^2.0.0", - "@octokit/webhooks-types": "5.8.0", + "@octokit/request-error": "^5.0.0", + "@octokit/webhooks-methods": "^4.0.0", + "@octokit/webhooks-types": "7.1.0", "aggregate-error": "^3.1.0" + }, + "engines": { + "node": ">= 18" } }, "node_modules/@octokit/webhooks-methods": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-2.0.0.tgz", - "integrity": "sha512-35cfQ4YWlnZnmZKmIxlGPUPLtbkF8lr/A/1Sk1eC0ddLMwQN06dOuLc+dI3YLQS+T+MoNt3DIQ0NynwgKPilig==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-4.0.0.tgz", + "integrity": "sha512-M8mwmTXp+VeolOS/kfRvsDdW+IO0qJ8kYodM/sAysk093q6ApgmBXwK1ZlUvAwXVrp/YVHp6aArj4auAxUAOFw==", + "engines": { + "node": ">= 18" + } }, "node_modules/@octokit/webhooks-types": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.3.1.tgz", "integrity": "sha512-u6355ZsZnHwmxen30SrqnYb1pXieBFkYgkNzt+Ed4Ao5tupN1OErHfzwiV6hq6duGkDAYASbq7/uVJQ69PjLEg==" }, - "node_modules/@octokit/webhooks/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/@octokit/webhooks/node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/@octokit/webhooks/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, "node_modules/@octokit/webhooks/node_modules/@octokit/webhooks-types": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-5.8.0.tgz", - "integrity": "sha512-8adktjIb76A7viIdayQSFuBEwOzwhDC+9yxZpKNHjfzrlostHCw0/N7JWpWMObfElwvJMk2fY2l1noENCk9wmw==" + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.1.0.tgz", + "integrity": "sha512-y92CpG4kFFtBBjni8LHoV12IegJ+KFxLgKRengrVjKmGE5XMeCuGvlfRe75lTRrgXaG6XIWJlFpIDTlkoJsU8w==" }, "node_modules/@phc/format": { "version": "1.0.0", @@ -2385,15 +2056,17 @@ "integrity": "sha512-yVgyCdTyooGX6+czDLkJahEcwgBWZsKH9xbjvjDNVFjY3QtiI/tHRiB3zjgJCQMZehXxv2CFHZQSpWRXdr6CeQ==" }, "node_modules/@probot/octokit-plugin-config": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@probot/octokit-plugin-config/-/octokit-plugin-config-1.1.6.tgz", - "integrity": "sha512-L29wmnFvilzSfWn9tUgItxdLv0LJh2ICjma3FmLr80Spu3wZ9nHyRrKMo9R5/K2m7VuWmgoKnkgRt2zPzAQBEQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@probot/octokit-plugin-config/-/octokit-plugin-config-2.0.1.tgz", + "integrity": "sha512-aWQYzPY2xiKscTVTKveghtbglqZ+W4eBLIdK1C/cNiFIofy3AxKogWgEZj29PjIe5ZRYx0sRHAPc/pkcXyOmTQ==", "dependencies": { - "@types/js-yaml": "^4.0.5", "js-yaml": "^4.1.0" }, + "engines": { + "node": ">=18" + }, "peerDependencies": { - "@octokit/core": ">=3" + "@octokit/core": ">=5" } }, "node_modules/@probot/pino": { @@ -3644,25 +3317,12 @@ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, - "node_modules/@types/ioredis": { - "version": "4.28.10", - "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.10.tgz", - "integrity": "sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/jmespath": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/@types/jmespath/-/jmespath-0.15.2.tgz", "integrity": "sha512-pegh49FtNsC389Flyo9y8AfkVIZn9MMPE9yJrO9svhq6Fks2MwymULWjZqySuxmctd3ZH4/n7Mr98D+1Qo5vGA==", "dev": true }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==" - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -3868,51 +3528,6 @@ "integrity": "sha512-Yll76ZHikRFCyz/pffKGjrCwe/le2CDwOP5F210KQo27kpRE46U2rDnzikNlVn6/ezH3Mhn46bJMTfeVTtcYMg==", "dev": true }, - "node_modules/@types/pino": { - "version": "6.3.12", - "resolved": "https://registry.npmjs.org/@types/pino/-/pino-6.3.12.tgz", - "integrity": "sha512-dsLRTq8/4UtVSpJgl9aeqHvbh6pzdmjYD3C092SYgLD2TyoCqHpTJk6vp8DvCTGGc7iowZ2MoiYiVUUCcu7muw==", - "dependencies": { - "@types/node": "*", - "@types/pino-pretty": "*", - "@types/pino-std-serializers": "*", - "sonic-boom": "^2.1.0" - } - }, - "node_modules/@types/pino-http": { - "version": "5.8.4", - "resolved": "https://registry.npmjs.org/@types/pino-http/-/pino-http-5.8.4.tgz", - "integrity": "sha512-UTYBQ2acmJ2eK0w58vVtgZ9RAicFFndfrnWC1w5cBTf8zwn/HEy8O+H7psc03UZgTzHmlcuX8VkPRnRDEj+FUQ==", - "dependencies": { - "@types/pino": "6.3" - } - }, - "node_modules/@types/pino-pretty": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/pino-pretty/-/pino-pretty-5.0.0.tgz", - "integrity": "sha512-N1uzqSzioqz8R3AkDbSJwcfDWeI3YMPNapSQQhnB2ISU4NYgUIcAh+hYT5ygqBM+klX4htpEhXMmoJv3J7GrdA==", - "deprecated": "This is a stub types definition. pino-pretty provides its own type definitions, so you do not need this installed.", - "dependencies": { - "pino-pretty": "*" - } - }, - "node_modules/@types/pino-std-serializers": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", - "integrity": "sha512-gXfUZx2xIBbFYozGms53fT0nvkacx/+62c8iTxrEqH5PkIGAQvDbXg2774VWOycMPbqn5YJBQ3BMsg4Li3dWbg==", - "deprecated": "This is a stub types definition. pino-std-serializers provides its own type definitions, so you do not need this installed.", - "dependencies": { - "pino-std-serializers": "*" - } - }, - "node_modules/@types/pino/node_modules/sonic-boom": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", - "integrity": "sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, "node_modules/@types/prompt-sync": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/@types/prompt-sync/-/prompt-sync-4.2.3.tgz", @@ -4945,11 +4560,11 @@ } }, "node_modules/axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.4.tgz", + "integrity": "sha512-heJnIs6N4aa1eSthhN9M5ioILu8Wi8vmQW9iHQ9NUvfkJb0lEEDUiIdQNAuBtfUt3FxReaKdpQA5DbmMOqzF/A==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.4", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -5403,7 +5018,8 @@ "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true }, "node_modules/combined-stream": { "version": "1.0.8", @@ -6343,18 +5959,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -6466,19 +6070,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express-handlebars": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-6.0.7.tgz", - "integrity": "sha512-iYeMFpc/hMD+E6FNAZA5fgWeXnXr4rslOSPkeEV6TwdmpJ5lEXuWX0u9vFYs31P2MURctQq2batR09oeNj0LIg==", - "dependencies": { - "glob": "^8.1.0", - "graceful-fs": "^4.2.10", - "handlebars": "^4.7.7" - }, - "engines": { - "node": ">=v12.22.9" - } - }, "node_modules/express/node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -6505,7 +6096,8 @@ "node_modules/fast-copy": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.1.tgz", - "integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==" + "integrity": "sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==", + "dev": true }, "node_modules/fast-decode-uri-component": { "version": "1.0.1", @@ -6588,19 +6180,6 @@ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.3.0.tgz", "integrity": "sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==" }, - "node_modules/fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", - "dependencies": { - "punycode": "^1.3.2" - } - }, - "node_modules/fast-url-parser/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" - }, "node_modules/fast-xml-parser": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", @@ -6784,11 +6363,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/flatstr": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz", - "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==" - }, "node_modules/flatted": { "version": "3.2.9", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", @@ -7016,6 +6590,14 @@ "is-property": "^1.0.2" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-func-name": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", @@ -7350,6 +6932,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-4.2.0.tgz", "integrity": "sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA==", + "dev": true, "dependencies": { "glob": "^8.0.0", "readable-stream": "^3.6.0" @@ -7359,6 +6942,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -7778,14 +7362,6 @@ "node": ">=8" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", @@ -8319,11 +7895,6 @@ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" - }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -9078,38 +8649,20 @@ "dev": true }, "node_modules/octokit-auth-probot": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/octokit-auth-probot/-/octokit-auth-probot-1.2.9.tgz", - "integrity": "sha512-mMjw6Y760EwJnW2tSVooJK8BMdsG6D40SoCclnefVf/5yWjaNVquEu8NREBVWb60OwbpnMEz4vREXHB5xdMFYQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/octokit-auth-probot/-/octokit-auth-probot-2.0.0.tgz", + "integrity": "sha512-bxidVIyxYJ+hWkG24pchPrN6mJdQrklZ2Acu+oGmZlh9aRONsIrw0KNW5W7QC2VlkxsFQwb9lnV+vH0BcEhnLQ==", "dependencies": { - "@octokit/auth-app": "^4.0.2", - "@octokit/auth-token": "^3.0.0", - "@octokit/auth-unauthenticated": "^3.0.0", - "@octokit/types": "^8.0.0" + "@octokit/auth-app": "^6.0.1", + "@octokit/auth-token": "^4.0.0", + "@octokit/auth-unauthenticated": "^5.0.1", + "@octokit/types": "^12.0.0" + }, + "engines": { + "node": ">=18" }, "peerDependencies": { - "@octokit/core": ">=3.2" - } - }, - "node_modules/octokit-auth-probot/node_modules/@octokit/auth-token": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz", - "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/octokit-auth-probot/node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==" - }, - "node_modules/octokit-auth-probot/node_modules/@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "dependencies": { - "@octokit/openapi-types": "^14.0.0" + "@octokit/core": ">=5" } }, "node_modules/on-exit-leak-free": { @@ -9224,14 +8777,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "engines": { - "node": ">=6" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -9563,16 +9108,16 @@ } }, "node_modules/pino": { - "version": "8.16.2", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.16.2.tgz", - "integrity": "sha512-2advCDGVEvkKu9TTVSa/kWW7Z3htI/sBKEZpqiHk6ive0i/7f5b1rsU8jn0aimxqfnSz5bj/nOYkwhBUn5xxvg==", + "version": "8.17.2", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.17.2.tgz", + "integrity": "sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "v1.1.0", "pino-std-serializers": "^6.0.0", - "process-warning": "^2.0.0", + "process-warning": "^3.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", @@ -9593,60 +9138,26 @@ } }, "node_modules/pino-http": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/pino-http/-/pino-http-5.8.0.tgz", - "integrity": "sha512-YwXiyRb9y0WCD1P9PcxuJuh3Dc5qmXde/paJE86UGYRdiFOi828hR9iUGmk5gaw6NBT9gLtKANOHFimvh19U5w==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/pino-http/-/pino-http-8.6.1.tgz", + "integrity": "sha512-J0hiJgUExtBXP2BjrK4VB305tHXS31sCmWJ9XJo2wPkLHa1NFPuW4V9wjG27PAc2fmBCigiNhQKpvrx+kntBPA==", "dependencies": { - "fast-url-parser": "^1.1.3", - "pino": "^6.13.0", - "pino-std-serializers": "^4.0.0" + "get-caller-file": "^2.0.5", + "pino": "^8.17.1", + "pino-std-serializers": "^6.2.2", + "process-warning": "^3.0.0" } }, - "node_modules/pino-http/node_modules/pino": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-6.14.0.tgz", - "integrity": "sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==", - "dependencies": { - "fast-redact": "^3.0.0", - "fast-safe-stringify": "^2.0.8", - "flatstr": "^1.0.12", - "pino-std-serializers": "^3.1.0", - "process-warning": "^1.0.0", - "quick-format-unescaped": "^4.0.3", - "sonic-boom": "^1.0.2" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-http/node_modules/pino-std-serializers": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", - "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==" - }, - "node_modules/pino-http/node_modules/pino/node_modules/pino-std-serializers": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz", - "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==" - }, "node_modules/pino-http/node_modules/process-warning": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", - "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==" - }, - "node_modules/pino-http/node_modules/sonic-boom": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", - "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", - "dependencies": { - "atomic-sleep": "^1.0.0", - "flatstr": "^1.0.12" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" }, "node_modules/pino-pretty": { "version": "10.2.3", "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.2.3.tgz", "integrity": "sha512-4jfIUc8TC1GPUfDyMSlW1STeORqkoxec71yhxIpLDQapUu8WOuoz2TTCoidrIssyz78LZC69whBMPIKCMbi3cw==", + "dev": true, "dependencies": { "colorette": "^2.0.7", "dateformat": "^4.6.3", @@ -9672,6 +9183,11 @@ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" }, + "node_modules/pino/node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" + }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -9957,413 +9473,59 @@ } }, "node_modules/probot": { - "version": "12.3.3", - "resolved": "https://registry.npmjs.org/probot/-/probot-12.3.3.tgz", - "integrity": "sha512-cdtKd+xISzi8sw6++BYBXleRknCA6hqUMoHj/sJqQBrjbNxQLhfeFCq9O2d0Z4eShsy5YFRR3MWwDKJ9uAE0CA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/probot/-/probot-13.0.0.tgz", + "integrity": "sha512-3ht9kAJ+ISjLyWLLCKVdrLE5xs/x+zUx07J5kYTxAyIxUvwF6Acr8xT5fiNihbBHAsEl4+A4CMYZQvZ5hx5bgw==", "dependencies": { - "@octokit/core": "^3.2.4", - "@octokit/plugin-enterprise-compatibility": "^1.2.8", - "@octokit/plugin-paginate-rest": "^2.6.2", - "@octokit/plugin-rest-endpoint-methods": "^5.0.1", - "@octokit/plugin-retry": "^3.0.6", - "@octokit/plugin-throttling": "^3.3.4", - "@octokit/types": "^8.0.0", - "@octokit/webhooks": "^9.26.3", - "@probot/get-private-key": "^1.1.0", - "@probot/octokit-plugin-config": "^1.0.0", - "@probot/pino": "^2.2.0", - "@types/express": "^4.17.9", - "@types/ioredis": "^4.27.1", - "@types/pino": "^6.3.4", - "@types/pino-http": "^5.0.6", - "commander": "^6.2.0", - "deepmerge": "^4.2.2", - "deprecation": "^2.3.1", - "dotenv": "^8.2.0", + "@octokit/core": "^5.0.2", + "@octokit/plugin-enterprise-compatibility": "^4.0.1", + "@octokit/plugin-paginate-rest": "^9.1.4", + "@octokit/plugin-rest-endpoint-methods": "^10.1.5", + "@octokit/plugin-retry": "^6.0.1", + "@octokit/plugin-throttling": "^8.1.3", + "@octokit/request": "^8.1.6", + "@octokit/types": "^12.3.0", + "@octokit/webhooks": "^12.0.10", + "@probot/get-private-key": "^1.1.2", + "@probot/octokit-plugin-config": "^2.0.1", + "@probot/pino": "^2.3.5", + "@types/express": "^4.17.21", + "commander": "^11.1.0", + "deepmerge": "^4.3.1", + "dotenv": "^16.3.1", "eventsource": "^2.0.2", - "express": "^4.17.1", - "express-handlebars": "^6.0.3", - "ioredis": "^4.27.8", - "js-yaml": "^3.14.1", - "lru-cache": "^6.0.0", - "octokit-auth-probot": "^1.2.2", - "pino": "^6.7.0", - "pino-http": "^5.3.0", + "express": "^4.18.2", + "ioredis": "^5.3.2", + "js-yaml": "^4.1.0", + "lru-cache": "^10.0.3", + "octokit-auth-probot": "^2.0.0", + "pino": "^8.16.1", + "pino-http": "^8.5.1", "pkg-conf": "^3.1.0", - "resolve": "^1.19.0", - "semver": "^7.3.4", - "update-dotenv": "^1.1.1", - "uuid": "^8.3.2" + "resolve": "^1.22.8", + "update-dotenv": "^1.1.1" }, "bin": { "probot": "bin/probot.js" }, "engines": { - "node": ">=10.21" - } - }, - "node_modules/probot/node_modules/@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dependencies": { - "@octokit/types": "^6.0.3" - } - }, - "node_modules/probot/node_modules/@octokit/auth-token/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/auth-token/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/probot/node_modules/@octokit/core/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/core/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dependencies": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/probot/node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/endpoint/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dependencies": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/probot/node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/graphql/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==" - }, - "node_modules/probot/node_modules/@octokit/plugin-paginate-rest": { - "version": "2.21.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", - "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", - "dependencies": { - "@octokit/types": "^6.40.0" - }, - "peerDependencies": { - "@octokit/core": ">=2" - } - }, - "node_modules/probot/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", - "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", - "dependencies": { - "@octokit/types": "^6.39.0", - "deprecation": "^2.3.1" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/probot/node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/plugin-throttling": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-3.7.0.tgz", - "integrity": "sha512-qrKT1Yl/KuwGSC6/oHpLBot3ooC9rq0/ryDYBCpkRtoj+R8T47xTMDT6Tk2CxWopFota/8Pi/2SqArqwC0JPow==", - "dependencies": { - "@octokit/types": "^6.0.1", - "bottleneck": "^2.15.3" - }, - "peerDependencies": { - "@octokit/core": "^3.5.0" - } - }, - "node_modules/probot/node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/probot/node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/probot/node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/request-error/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/request/node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "node_modules/probot/node_modules/@octokit/request/node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/probot/node_modules/@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "dependencies": { - "@octokit/openapi-types": "^14.0.0" - } - }, - "node_modules/probot/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" + "node": ">=18" } }, "node_modules/probot/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "engines": { - "node": ">= 6" + "node": ">=16" } }, - "node_modules/probot/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, + "node_modules/probot/node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/probot/node_modules/denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/probot/node_modules/dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/probot/node_modules/ioredis": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.5.tgz", - "integrity": "sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==", - "dependencies": { - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.1", - "denque": "^1.1.0", - "lodash.defaults": "^4.2.0", - "lodash.flatten": "^4.4.0", - "lodash.isarguments": "^3.1.0", - "p-map": "^2.1.0", - "redis-commands": "1.7.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, - "node_modules/probot/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/probot/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/probot/node_modules/pino": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-6.14.0.tgz", - "integrity": "sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==", - "dependencies": { - "fast-redact": "^3.0.0", - "fast-safe-stringify": "^2.0.8", - "flatstr": "^1.0.12", - "pino-std-serializers": "^3.1.0", - "process-warning": "^1.0.0", - "quick-format-unescaped": "^4.0.3", - "sonic-boom": "^1.0.2" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/probot/node_modules/pino-std-serializers": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz", - "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==" - }, - "node_modules/probot/node_modules/process-warning": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", - "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==" - }, - "node_modules/probot/node_modules/sonic-boom": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", - "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", - "dependencies": { - "atomic-sleep": "^1.0.0", - "flatstr": "^1.0.12" - } - }, - "node_modules/probot/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" + "node": "14 || >=16.14" } }, "node_modules/process": { @@ -10601,11 +9763,6 @@ "node": ">= 10.13.0" } }, - "node_modules/redis-commands": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", - "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" - }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -11179,11 +10336,6 @@ "node": ">= 10.x" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, "node_modules/sqlstring": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", diff --git a/backend/package.json b/backend/package.json index eb31b647d4..54e66f6afc 100644 --- a/backend/package.json +++ b/backend/package.json @@ -77,7 +77,7 @@ "@fastify/rate-limit": "^9.0.0", "@fastify/session": "^10.7.0", "@fastify/swagger": "^8.12.0", - "@fastify/swagger-ui": "^1.10.1", + "@fastify/swagger-ui": "^2.1.0", "@node-saml/passport-saml": "^4.0.4", "@octokit/rest": "^20.0.2", "@octokit/webhooks-types": "^7.3.1", @@ -86,7 +86,7 @@ "ajv": "^8.12.0", "argon2": "^0.31.2", "aws-sdk": "^2.1532.0", - "axios": "^1.6.2", + "axios": "^1.6.4", "axios-retry": "^4.0.0", "bcrypt": "^5.1.1", "bullmq": "^5.1.1", @@ -114,7 +114,7 @@ "picomatch": "^3.0.1", "pino": "^8.16.2", "posthog-node": "^3.6.0", - "probot": "^12.3.3", + "probot": "^13.0.0", "smee-client": "^2.0.0", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1", From 00650df501b7e5b5a1d029b872a954496d968369 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Thu, 1 Feb 2024 20:01:54 +0000 Subject: [PATCH 005/525] fix: backend/package.json & backend/package-lock.json to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-NODEMAILER-6219989 --- backend/package-lock.json | 8 ++++---- backend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index d1d12ad2eb..0d708f9606 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -52,7 +52,7 @@ "mysql2": "^3.6.5", "nanoid": "^5.0.4", "node-cache": "^5.1.2", - "nodemailer": "^6.9.7", + "nodemailer": "^6.9.9", "ora": "^7.0.1", "passport-github": "^1.1.0", "passport-gitlab2": "^5.0.0", @@ -9437,9 +9437,9 @@ } }, "node_modules/nodemailer": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.7.tgz", - "integrity": "sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==", + "version": "6.9.9", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.9.tgz", + "integrity": "sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==", "engines": { "node": ">=6.0.0" } diff --git a/backend/package.json b/backend/package.json index 39aed4e342..f01ea4bc5a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -106,7 +106,7 @@ "mysql2": "^3.6.5", "nanoid": "^5.0.4", "node-cache": "^5.1.2", - "nodemailer": "^6.9.7", + "nodemailer": "^6.9.9", "ora": "^7.0.1", "passport-github": "^1.1.0", "passport-gitlab2": "^5.0.0", From 541fa1096456e10c7846d7201ae49e2776ff3aa6 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Sun, 4 Feb 2024 21:18:10 +0100 Subject: [PATCH 006/525] doc: HISTIGNORE recommendation --- docs/cli/usage.mdx | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/cli/usage.mdx b/docs/cli/usage.mdx index 2ea7b6425c..aa60c3db16 100644 --- a/docs/cli/usage.mdx +++ b/docs/cli/usage.mdx @@ -135,3 +135,38 @@ Another option to point the CLI to your self hosted Infisical instance is to set infisical --domain="https://your-self-hosted-infisical.com/api" ``` + +## History + +Your terminal keeps a history with the commands you run. When you create Infisical secrets directly from your terminal, they'll stay there for a while. + +For security and privacy concerns, we recommend you to configure your terminal to ignore those specific Infisical commands. + + + + + + + `$HOME/.profile` is pretty common but, you could place it under `$HOME/.profile.d/infisical.sh` or any profile file run at login + + + ```bash + cat <> $HOME/.profile && source $HOME/.profile + + # Ignoring specific Infisical CLI commands + DEFAULT_HISTIGNORE=$HISTIGNORE + export HISTIGNORE="*infisical secrets set*:$DEFAULT_HISTIGNORE" + EOF + ``` + + + + If you're on WSL, then you can use the Unix/Linux method. + + + Here's some [documentation](https://superuser.com/a/1658331) about how to clear the terminal history, in PowerShell and CMD + + + + + \ No newline at end of file From d5064fe75a57125bc734ad03d2a983e03f4a967a Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Thu, 8 Feb 2024 15:54:20 -0800 Subject: [PATCH 007/525] Start SCIM functionality --- .../migrations/20240208234120_scim-token.ts | 24 ++++ backend/src/db/schemas/models.ts | 1 + backend/src/ee/routes/v1/index.ts | 2 + backend/src/ee/routes/v1/scim-router.ts | 111 ++++++++++++++++++ .../server/plugins/auth/inject-identity.ts | 15 +++ backend/src/services/auth/auth-type.ts | 9 +- frontend/src/hooks/api/index.tsx | 1 + frontend/src/hooks/api/scim/index.tsx | 3 + frontend/src/hooks/api/scim/queries.tsx | 24 ++++ frontend/src/hooks/api/scim/types.ts | 3 + .../components/OrgAuthTab/OrgAuthTab.tsx | 2 + .../components/OrgAuthTab/OrgSCIMSection.tsx | 108 +++++++++++++++++ 12 files changed, 300 insertions(+), 3 deletions(-) create mode 100644 backend/src/db/migrations/20240208234120_scim-token.ts create mode 100644 backend/src/ee/routes/v1/scim-router.ts create mode 100644 frontend/src/hooks/api/scim/index.tsx create mode 100644 frontend/src/hooks/api/scim/queries.tsx create mode 100644 frontend/src/hooks/api/scim/types.ts create mode 100644 frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx diff --git a/backend/src/db/migrations/20240208234120_scim-token.ts b/backend/src/db/migrations/20240208234120_scim-token.ts new file mode 100644 index 0000000000..60753e3605 --- /dev/null +++ b/backend/src/db/migrations/20240208234120_scim-token.ts @@ -0,0 +1,24 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; +import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils"; + +export async function up(knex: Knex): Promise { + if (!(await knex.schema.hasTable(TableName.ScimToken))) { + await knex.schema.createTable(TableName.ScimToken, (t) => { + t.string("id", 36).primary().defaultTo(knex.fn.uuid()); + t.bigInteger("tokenTTL").defaultTo(15552000).notNullable(); // 180 days second + t.datetime("tokenLastUsedAt"); + t.uuid("orgId").notNullable(); + t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE"); + t.timestamps(true, true, true); + }); + } + + await createOnUpdateTrigger(knex, TableName.IdentityAccessToken); +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTableIfExists(TableName.ScimToken); + await dropOnUpdateTrigger(knex, TableName.ScimToken); +} diff --git a/backend/src/db/schemas/models.ts b/backend/src/db/schemas/models.ts index 4ef943bbe9..e817d6ed04 100644 --- a/backend/src/db/schemas/models.ts +++ b/backend/src/db/schemas/models.ts @@ -40,6 +40,7 @@ export enum TableName { IdentityUaClientSecret = "identity_ua_client_secrets", IdentityOrgMembership = "identity_org_memberships", IdentityProjectMembership = "identity_project_memberships", + ScimToken = "scim_tokens", SecretApprovalPolicy = "secret_approval_policies", SecretApprovalPolicyApprover = "secret_approval_policies_approvers", SecretApprovalRequest = "secret_approval_requests", diff --git a/backend/src/ee/routes/v1/index.ts b/backend/src/ee/routes/v1/index.ts index 2ed4393163..fb92aa3c5e 100644 --- a/backend/src/ee/routes/v1/index.ts +++ b/backend/src/ee/routes/v1/index.ts @@ -3,6 +3,7 @@ import { registerOrgRoleRouter } from "./org-role-router"; import { registerProjectRoleRouter } from "./project-role-router"; import { registerProjectRouter } from "./project-router"; import { registerSamlRouter } from "./saml-router"; +import { registerScimRouter } from "./scim-router"; import { registerSecretApprovalPolicyRouter } from "./secret-approval-policy-router"; import { registerSecretApprovalRequestRouter } from "./secret-approval-request-router"; import { registerSecretRotationProviderRouter } from "./secret-rotation-provider-router"; @@ -33,6 +34,7 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => { prefix: "/secret-rotation-providers" }); await server.register(registerSamlRouter, { prefix: "/sso" }); + await server.register(registerScimRouter, { prefix: "/scim" }); await server.register(registerSecretScanningRouter, { prefix: "/secret-scanning" }); await server.register(registerSecretRotationRouter, { prefix: "/secret-rotations" }); await server.register(registerSecretVersionRouter, { prefix: "/secret" }); diff --git a/backend/src/ee/routes/v1/scim-router.ts b/backend/src/ee/routes/v1/scim-router.ts new file mode 100644 index 0000000000..00478458af --- /dev/null +++ b/backend/src/ee/routes/v1/scim-router.ts @@ -0,0 +1,111 @@ +import jwt from "jsonwebtoken"; +import { z } from "zod"; + +import { getConfig } from "@app/lib/config/env"; +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { AuthMode, AuthTokenType } from "@app/services/auth/auth-type"; + +export const registerScimRouter = async (server: FastifyZodProvider) => { + server.route({ + url: "/", + method: "GET", + schema: { + params: z.object({}), + response: { + 200: z.object({}) + } + }, + // onRequest: verifyAuth([AuthMode.JWT]), + handler: async () => { + return { + hello: "world" + }; + } + }); + + server.route({ + url: "/Users", + method: "GET", + schema: { + params: z.object({}), + response: { + 200: z.object({}) + } + }, + // onRequest: verifyAuth([]), + handler: async () => { + return { + hello: "world" + }; + } + }); + + server.route({ + url: "/tokens/organizations/:organizationId", // api/v1/scim/token/organizations/:organizationId + method: "POST", + onRequest: verifyAuth([AuthMode.JWT]), + schema: { + params: z.object({ + organizationId: z.string().trim() + }), + body: z.object({ + description: z.string().trim(), + ttl: z.number().min(0).default(0) + }), + response: { + 200: z.object({ + scimToken: z.string().trim() + }) + } + }, + handler: async () => { + // TODO: create SCIM token logic + // TODO: create SCIM token controller + + const appCfg = getConfig(); + const scimToken = jwt.sign( + { + authTokenType: AuthTokenType.SCIM_TOKEN + }, + appCfg.AUTH_SECRET, + { + // expiresIn: identityAccessToken.accessTokenMaxTTL === 0 ? undefined : identityAccessToken.accessTokenMaxTTL + } + ); // TODO: add expiration + + return { scimToken }; + } + }); + + server.route({ + url: "/tokens/organizations/:organizationId", // api/v1/scim/token/organizations/:organizationId + method: "GET", + onRequest: verifyAuth([AuthMode.JWT]), + schema: { + params: z.object({ + organizationId: z.string().trim() + }), + response: { + 200: z.object({ + scimToken: z.string().trim() + }) + } + }, + handler: async () => { + // TODO: put into service file + + const appCfg = getConfig(); + const scimToken = jwt.sign( + { + authTokenType: AuthTokenType.SCIM_TOKEN + }, + appCfg.AUTH_SECRET, + { + // expiresIn: identityAccessToken.accessTokenMaxTTL === 0 ? undefined : identityAccessToken.accessTokenMaxTTL + } + ); // TODO: add expiration + + return { scimToken }; + } + }); +}; diff --git a/backend/src/server/plugins/auth/inject-identity.ts b/backend/src/server/plugins/auth/inject-identity.ts index 04d4cbe0e1..60d06ecfbd 100644 --- a/backend/src/server/plugins/auth/inject-identity.ts +++ b/backend/src/server/plugins/auth/inject-identity.ts @@ -33,6 +33,10 @@ export type TAuthMode = actor: ActorType.IDENTITY; identityId: string; identityName: string; + } + | { + authMode: AuthMode.SCIM_TOKEN; + actor: ActorType.SCIM_IDP; }; const extractAuth = async (req: FastifyRequest, jwtSecret: string) => { @@ -53,6 +57,7 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => { } const decodedToken = jwt.verify(authTokenValue, jwtSecret) as JwtPayload; + switch (decodedToken.authTokenType) { case AuthTokenType.ACCESS_TOKEN: return { @@ -68,6 +73,12 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => { token: decodedToken as TIdentityAccessTokenJwtPayload, actor: ActorType.IDENTITY } as const; + case AuthTokenType.SCIM_TOKEN: + return { + authMode: AuthMode.SCIM_TOKEN, + token: decodedToken, + actor: ActorType.SCIM_IDP + } as const; default: return { authMode: null, token: null } as const; } @@ -111,6 +122,10 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => { req.auth = { authMode: AuthMode.API_KEY as const, userId: user.id, actor, user }; break; } + case AuthMode.SCIM_TOKEN: { + req.auth = { authMode: AuthMode.SCIM_TOKEN, actor }; + break; + } default: throw new UnauthorizedError({ name: "Unknown token strategy" }); } diff --git a/backend/src/services/auth/auth-type.ts b/backend/src/services/auth/auth-type.ts index 26417b0e87..779d828c58 100644 --- a/backend/src/services/auth/auth-type.ts +++ b/backend/src/services/auth/auth-type.ts @@ -17,21 +17,24 @@ export enum AuthTokenType { API_KEY = "apiKey", SERVICE_ACCESS_TOKEN = "serviceAccessToken", SERVICE_REFRESH_TOKEN = "serviceRefreshToken", - IDENTITY_ACCESS_TOKEN = "identityAccessToken" + IDENTITY_ACCESS_TOKEN = "identityAccessToken", + SCIM_TOKEN = "scimToken" } export enum AuthMode { JWT = "jwt", SERVICE_TOKEN = "serviceToken", API_KEY = "apiKey", - IDENTITY_ACCESS_TOKEN = "identityAccessToken" + IDENTITY_ACCESS_TOKEN = "identityAccessToken", + SCIM_TOKEN = "scimToken" } export enum ActorType { // would extend to AWS, Azure, ... USER = "user", // userIdentity SERVICE = "service", IDENTITY = "identity", - Machine = "machine" + Machine = "machine", + SCIM_IDP = "scimIdp" } export type AuthModeJwtTokenPayload = { diff --git a/frontend/src/hooks/api/index.tsx b/frontend/src/hooks/api/index.tsx index 4caeaea48b..d63a5e111f 100644 --- a/frontend/src/hooks/api/index.tsx +++ b/frontend/src/hooks/api/index.tsx @@ -10,6 +10,7 @@ export * from "./integrations"; export * from "./keys"; export * from "./organization"; export * from "./roles"; +export * from "./scim"; export * from "./secretApproval"; export * from "./secretApprovalRequest"; export * from "./secretFolders"; diff --git a/frontend/src/hooks/api/scim/index.tsx b/frontend/src/hooks/api/scim/index.tsx new file mode 100644 index 0000000000..13a190c1fa --- /dev/null +++ b/frontend/src/hooks/api/scim/index.tsx @@ -0,0 +1,3 @@ +export { + useGetScimToken +} from "./queries"; \ No newline at end of file diff --git a/frontend/src/hooks/api/scim/queries.tsx b/frontend/src/hooks/api/scim/queries.tsx new file mode 100644 index 0000000000..b8a3291b1f --- /dev/null +++ b/frontend/src/hooks/api/scim/queries.tsx @@ -0,0 +1,24 @@ +import { useQuery } from "@tanstack/react-query"; + +import { apiRequest } from "@app/config/request"; + +import { GetScimTokenRes } from "./types"; + +const scimKeys = { + getScimToken: (orgId: string) => [{ orgId }, "organization-scim-token"] as const, +}; + +export const useGetScimToken = (organizationId: string) => { + return useQuery({ + queryKey: scimKeys.getScimToken(organizationId), + queryFn: async () => { + if (organizationId === "") { + return undefined; + } + + const { data: { scimToken } } = await apiRequest.get(`/api/v1/scim/token/organizations/${organizationId}`); + return scimToken; + }, + enabled: true + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/api/scim/types.ts b/frontend/src/hooks/api/scim/types.ts new file mode 100644 index 0000000000..deec75ed0d --- /dev/null +++ b/frontend/src/hooks/api/scim/types.ts @@ -0,0 +1,3 @@ +export type GetScimTokenRes = { + scimToken: string; +}; \ No newline at end of file diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx index c29c948fc3..3141f21635 100644 --- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx +++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx @@ -1,6 +1,7 @@ import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { withPermission } from "@app/hoc"; +import { OrgSCIMSection } from "./OrgSCIMSection"; import { OrgSSOSection } from "./OrgSSOSection"; export const OrgAuthTab = withPermission( @@ -8,6 +9,7 @@ export const OrgAuthTab = withPermission( return (
+
); }, diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx new file mode 100644 index 0000000000..93c47ae3bc --- /dev/null +++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx @@ -0,0 +1,108 @@ +import { useState } from "react"; +import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +// import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; +// import { OrgPermissionCan } from "@app/components/permissions"; +import { + // Button, + IconButton, + Switch +} from "@app/components/v2"; +import { +// OrgPermissionActions, +// OrgPermissionSubjects, + useOrganization, +// useSubscription +} from "@app/context"; +import { useToggle } from "@app/hooks"; +// import { usePopUp } from "@app/hooks/usePopUp"; +import { useGetScimToken } from "@app/hooks/api"; + +// TODO: add permissioning for enteprise SCIM + +export const OrgSCIMSection = () => { + const { currentOrg } = useOrganization(); + // const { createNotification } = useNotificationContext(); + // const { subscription } = useSubscription(); + // const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ + // "upgradePlan" + // ] as const); + + const { data: scimToken } = useGetScimToken(currentOrg?.id ?? ""); + + const [scimEnabled, setScimEnabled] = useState(false); // sync this with backend + const [isAPIKeyCopied, setIsAPIKeyCopied] = useToggle(false); + + // TODO: get SCIM stuf + + const handleSCIMToggle = (value: boolean) => { + // TODO + try { + setScimEnabled(value); + } catch (err) { + console.error(err); + } + } + + const copyTokenToClipboard = () => { + navigator.clipboard.writeText(scimToken ?? ""); + setIsAPIKeyCopied.on(); + }; + + return ( +
+

SCIM Configuration

+ handleSCIMToggle(value)} + isChecked={scimEnabled} + isDisabled={false} + > + Enable SCIM Provisioning + + {scimEnabled && ( +
+
+

SCIM URL

+

{`${window.origin}/api/v1/scim`}

+
+ {/*

SCIM URL

*/} + {/*
+

{`${window.origin}/api/v1/scim`}

+ + + + Click to copy + + +
*/} + {scimToken && ( + <> +

SCIM Bearer Token

+
+

{scimToken}

+ + + + Click to copy + + +
+ + )} +
+ )} +
+ ); +} \ No newline at end of file From 6c1489a87bd8c05118abafd52df2a467ee61633d Mon Sep 17 00:00:00 2001 From: Salman Date: Sat, 10 Feb 2024 03:26:56 +0530 Subject: [PATCH 008/525] Add admin invite only signup field --- .../migrations/20240205200732_invite-only.ts | 20 ++++ backend/src/db/schemas/super-admin.ts | 1 + backend/src/db/seeds/1-user.ts | 2 +- backend/src/ee/routes/v1/saml-router.ts | 2 +- backend/src/server/routes/v1/admin-router.ts | 3 +- .../super-admin/super-admin-service.ts | 2 +- frontend/src/hooks/api/admin/types.ts | 1 + frontend/src/pages/signupinvite.tsx | 10 +- .../admin/DashboardPage/DashboardPage.tsx | 99 ++++++++++++++----- 9 files changed, 108 insertions(+), 32 deletions(-) create mode 100644 backend/src/db/migrations/20240205200732_invite-only.ts diff --git a/backend/src/db/migrations/20240205200732_invite-only.ts b/backend/src/db/migrations/20240205200732_invite-only.ts new file mode 100644 index 0000000000..dd4be591b0 --- /dev/null +++ b/backend/src/db/migrations/20240205200732_invite-only.ts @@ -0,0 +1,20 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; + +export async function up(knex: Knex): Promise { + const isTablePresent = await knex.schema.hasTable(TableName.SuperAdmin); + if (isTablePresent) { + await knex.schema.alterTable(TableName.SuperAdmin, (t) => { + t.boolean("inviteOnlySignUp").defaultTo(false); + }); + } +} + +export async function down(knex: Knex): Promise { + if (await knex.schema.hasColumn(TableName.SuperAdmin, "inviteOnlySignUp")) { + await knex.schema.alterTable(TableName.SuperAdmin, (t) => { + t.dropColumn("inviteOnlySignUp"); + }); + } +} diff --git a/backend/src/db/schemas/super-admin.ts b/backend/src/db/schemas/super-admin.ts index 13bf45e7bd..b43ada8d5b 100644 --- a/backend/src/db/schemas/super-admin.ts +++ b/backend/src/db/schemas/super-admin.ts @@ -11,6 +11,7 @@ export const SuperAdminSchema = z.object({ id: z.string().uuid(), initialized: z.boolean().default(false).nullable().optional(), allowSignUp: z.boolean().default(true).nullable().optional(), + inviteOnlySignUp: z.boolean().default(false).nullable().optional(), createdAt: z.date(), updatedAt: z.date() }); diff --git a/backend/src/db/seeds/1-user.ts b/backend/src/db/seeds/1-user.ts index ca0042a98f..0da9be9b1a 100644 --- a/backend/src/db/seeds/1-user.ts +++ b/backend/src/db/seeds/1-user.ts @@ -9,7 +9,7 @@ export async function seed(knex: Knex): Promise { await knex(TableName.Users).del(); await knex(TableName.UserEncryptionKey).del(); await knex(TableName.SuperAdmin).del(); - await knex(TableName.SuperAdmin).insert([{ initialized: true, allowSignUp: true }]); + await knex(TableName.SuperAdmin).insert([{ initialized: true, allowSignUp: true, inviteOnlySignUp: false }]); // Inserts seed entries const [user] = await knex(TableName.Users) .insert([ diff --git a/backend/src/ee/routes/v1/saml-router.ts b/backend/src/ee/routes/v1/saml-router.ts index f81c21ffd4..4c0e43cd6a 100644 --- a/backend/src/ee/routes/v1/saml-router.ts +++ b/backend/src/ee/routes/v1/saml-router.ts @@ -105,7 +105,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { email, firstName: profile.firstName as string, lastName: profile.lastName as string, - isSignupAllowed: Boolean(serverCfg.allowSignUp), + isSignupAllowed: Boolean(serverCfg.allowSignUp && serverCfg.inviteOnlySignUp), relayState: (req.body as { RelayState?: string }).RelayState, authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string, orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts index e23f68c3ae..afe5c4c95a 100644 --- a/backend/src/server/routes/v1/admin-router.ts +++ b/backend/src/server/routes/v1/admin-router.ts @@ -31,7 +31,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { method: "PATCH", schema: { body: z.object({ - allowSignUp: z.boolean().optional() + allowSignUp: z.boolean().optional(), + inviteOnlySignUp: z.boolean().optional() }), response: { 200: z.object({ diff --git a/backend/src/services/super-admin/super-admin-service.ts b/backend/src/services/super-admin/super-admin-service.ts index 1144bd414a..b15d95e566 100644 --- a/backend/src/services/super-admin/super-admin-service.ts +++ b/backend/src/services/super-admin/super-admin-service.ts @@ -33,7 +33,7 @@ export const superAdminServiceFactory = ({ const serverCfg = await serverCfgDAL.findOne({}); if (serverCfg) return; - const newCfg = await serverCfgDAL.create({ initialized: false, allowSignUp: true }); + const newCfg = await serverCfgDAL.create({ initialized: false, allowSignUp: true, inviteOnlySignUp: false }); return newCfg; }; diff --git a/frontend/src/hooks/api/admin/types.ts b/frontend/src/hooks/api/admin/types.ts index da6b5ce3cd..593191d1f2 100644 --- a/frontend/src/hooks/api/admin/types.ts +++ b/frontend/src/hooks/api/admin/types.ts @@ -1,6 +1,7 @@ export type TServerConfig = { initialized: boolean; allowSignUp: boolean; + inviteOnlySignUp: boolean; isMigrationModeOn?: boolean; }; diff --git a/frontend/src/pages/signupinvite.tsx b/frontend/src/pages/signupinvite.tsx index 5eaa48e637..5ede3126c5 100644 --- a/frontend/src/pages/signupinvite.tsx +++ b/frontend/src/pages/signupinvite.tsx @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import crypto from "crypto"; -import { useState } from "react"; +import { useEffect,useState } from "react"; import Head from "next/head"; import Image from "next/image"; import Link from "next/link"; @@ -22,6 +22,7 @@ import { deriveArgonKey } from "@app/components/utilities/cryptography/crypto"; import issueBackupKey from "@app/components/utilities/cryptography/issueBackupKey"; import { saveTokenToLocalStorage } from "@app/components/utilities/saveTokenToLocalStorage"; import SecurityClient from "@app/components/utilities/SecurityClient"; +import { useServerConfig } from "@app/context"; import { completeAccountSignupInvite, verifySignupInvite } from "@app/hooks/api/auth/queries"; import { fetchOrganizations } from "@app/hooks/api/organization/queries"; @@ -56,6 +57,13 @@ export default function SignupInvite() { const token = parsedUrl.token as string; const organizationId = parsedUrl.organization_id as string; const email = (parsedUrl.to as string)?.replace(" ", "+").trim(); + const { config } = useServerConfig(); + + useEffect(() => { + if (!config.allowSignUp) { + router.push("/login"); + } + }, [config.allowSignUp]); // Verifies if the information that the users entered (name, workspace) is there, and if the password matched the criteria. const signupErrorCheck = async () => { diff --git a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx index 508dcda1e4..240a9d5de8 100644 --- a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx +++ b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx @@ -1,7 +1,16 @@ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useRouter } from "next/router"; -import { ContentLoader, Switch, Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; +import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; +import { + ContentLoader, + Select, + SelectItem, + Tab, + TabList, + TabPanel, + Tabs +} from "@app/components/v2"; import { useOrganization, useServerConfig, useUser } from "@app/context"; import { useUpdateServerConfig } from "@app/hooks/api"; @@ -9,13 +18,18 @@ enum TabSections { Settings = "settings" } +type SignUpMode = "disabled" | "invite-only" | "anyone"; + export const AdminDashboardPage = () => { const router = useRouter(); const data = useServerConfig(); + const [signUpMode, setSignUpMode] = useState("invite-only"); + const { config } = data; const { user, isLoading: isUserLoading } = useUser(); const { orgs } = useOrganization(); const { mutate: updateServerConfig } = useUpdateServerConfig(); + const { createNotification } = useNotificationContext(); const isNotAllowed = !user?.superAdmin; @@ -28,37 +42,68 @@ export const AdminDashboardPage = () => { } }, [isNotAllowed, isUserLoading]); + useEffect(() => { + if (!config.allowSignUp) { + setSignUpMode("disabled"); + return; + } + if (config.inviteOnlySignUp) { + setSignUpMode("invite-only"); + return; + } + setSignUpMode("anyone"); + }, [config]); + + function handleSignUpModeChange(newSignUpMode: SignUpMode) { + config.allowSignUp = newSignUpMode !== "disabled"; + config.inviteOnlySignUp = newSignUpMode === "invite-only"; + + createNotification({ + text: "Successfully changed sign up mode.", + type: "success" + }); + + updateServerConfig(config); + setSignUpMode(newSignUpMode); + } + return ( -
-
+
+

Admin Dashboard

Manage your Infisical instance.

- {isUserLoading || isNotAllowed ? ( - - ) : ( -
- - -
- General -
-
- -
- updateServerConfig({ allowSignUp: isChecked })} - /> -
Enable signup or invite
-
-
-
-
- )}
+ {isUserLoading || isNotAllowed ? ( + + ) : ( +
+ + +
+ General +
+
+ +
+
Allow user to Sign Up
+ +
+
+
+
+ )}
); }; From 5cf1ec24003438d575389817685fedf6bb7d5e7c Mon Sep 17 00:00:00 2001 From: Salman Date: Sat, 10 Feb 2024 10:44:39 +0530 Subject: [PATCH 009/525] Add restrict signup based on domain --- .../migrations/20240205200732_invite-only.ts | 7 ++ backend/src/db/schemas/super-admin.ts | 1 + backend/src/server/routes/v1/admin-router.ts | 3 +- backend/src/server/routes/v1/sso-router.ts | 8 +- backend/src/server/routes/v3/signup-router.ts | 12 ++- .../src/services/auth/auth-login-service.ts | 25 +++--- backend/src/services/auth/auth-login-type.ts | 3 + frontend/src/hooks/api/admin/types.ts | 1 + .../admin/DashboardPage/DashboardPage.tsx | 88 ++++++++++++++----- 9 files changed, 109 insertions(+), 39 deletions(-) diff --git a/backend/src/db/migrations/20240205200732_invite-only.ts b/backend/src/db/migrations/20240205200732_invite-only.ts index dd4be591b0..d242623c8a 100644 --- a/backend/src/db/migrations/20240205200732_invite-only.ts +++ b/backend/src/db/migrations/20240205200732_invite-only.ts @@ -7,6 +7,7 @@ export async function up(knex: Knex): Promise { if (isTablePresent) { await knex.schema.alterTable(TableName.SuperAdmin, (t) => { t.boolean("inviteOnlySignUp").defaultTo(false); + t.string("allowSpecificDomainSignUp"); }); } } @@ -17,4 +18,10 @@ export async function down(knex: Knex): Promise { t.dropColumn("inviteOnlySignUp"); }); } + + if (await knex.schema.hasColumn(TableName.SuperAdmin, "allowSpecificDomainSignUp")) { + await knex.schema.alterTable(TableName.SuperAdmin, (t) => { + t.dropColumn("allowSpecificDomainSignUp"); + }); + } } diff --git a/backend/src/db/schemas/super-admin.ts b/backend/src/db/schemas/super-admin.ts index b43ada8d5b..fb373e19be 100644 --- a/backend/src/db/schemas/super-admin.ts +++ b/backend/src/db/schemas/super-admin.ts @@ -12,6 +12,7 @@ export const SuperAdminSchema = z.object({ initialized: z.boolean().default(false).nullable().optional(), allowSignUp: z.boolean().default(true).nullable().optional(), inviteOnlySignUp: z.boolean().default(false).nullable().optional(), + allowSpecificDomainSignUp: z.string().nullable().optional(), createdAt: z.date(), updatedAt: z.date() }); diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts index afe5c4c95a..ddd95522f0 100644 --- a/backend/src/server/routes/v1/admin-router.ts +++ b/backend/src/server/routes/v1/admin-router.ts @@ -32,7 +32,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { schema: { body: z.object({ allowSignUp: z.boolean().optional(), - inviteOnlySignUp: z.boolean().optional() + inviteOnlySignUp: z.boolean().optional(), + allowSpecificDomainSignUp: z.string().optional() }), response: { 200: z.object({ diff --git a/backend/src/server/routes/v1/sso-router.ts b/backend/src/server/routes/v1/sso-router.ts index bfcf2f6ae5..da0b12ca51 100644 --- a/backend/src/server/routes/v1/sso-router.ts +++ b/backend/src/server/routes/v1/sso-router.ts @@ -55,7 +55,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { lastName: profile?.name?.familyName || "", authMethod: AuthMethod.GOOGLE, callbackPort: req.query.state as string, - isSignupAllowed: Boolean(serverCfg.allowSignUp) + serverCfg }); cb(null, { isUserCompleted, providerAuthToken }); } catch (error) { @@ -91,7 +91,8 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { lastName: "", authMethod: AuthMethod.GITHUB, callbackPort: req.query.state as string, - isSignupAllowed: Boolean(serverCfg.allowSignUp) + serverCfg + // isSignupAllowed: Boolean(serverCfg.allowSignUp) }); return cb(null, { isUserCompleted, providerAuthToken }); } catch (error) { @@ -127,7 +128,8 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { lastName: "", authMethod: AuthMethod.GITLAB, callbackPort: req.query.state as string, - isSignupAllowed: Boolean(serverCfg.allowSignUp) + serverCfg + // isSignupAllowed: Boolean(serverCfg.allowSignUp) }); return cb(null, { isUserCompleted, providerAuthToken }); diff --git a/backend/src/server/routes/v3/signup-router.ts b/backend/src/server/routes/v3/signup-router.ts index 2a2f50f43c..11d79d2b5f 100644 --- a/backend/src/server/routes/v3/signup-router.ts +++ b/backend/src/server/routes/v3/signup-router.ts @@ -23,8 +23,16 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { - await server.services.signup.beginEmailSignupProcess(req.body.email); - return { message: `Sent an email verification code to ${req.body.email}` }; + const { email } = req.body; + const config = await server.services.superAdmin.initServerCfg(); + + if (config?.allowSpecificDomainSignUp) { + const domain = email.split("@")[1]; + + if (domain !== config.allowSpecificDomainSignUp) throw new Error(`Unsupported email domain (${domain}).`); + } + await server.services.signup.beginEmailSignupProcess(email); + return { message: `Sent an email verification code to ${email}` }; } }); diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index 6e4d60bbab..ccfe619f43 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -261,20 +261,25 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }: /* * OAuth2 login for google,github, and other oauth2 provider * */ - const oauth2Login = async ({ - email, - firstName, - lastName, - authMethod, - callbackPort, - isSignupAllowed - }: TOauthLoginDTO) => { + const oauth2Login = async ({ email, firstName, lastName, authMethod, callbackPort, serverCfg }: TOauthLoginDTO) => { 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 (!user) { + // Create a new user based on oAuth + if (!serverCfg?.allowSignUp) + throw new BadRequestError({ message: "User signup disabled", name: "Oauth 2 login" }); + + if (serverCfg?.allowSpecificDomainSignUp) { + const domain = email.split("@")[1]; + + if (domain !== serverCfg.allowSpecificDomainSignUp) + throw new BadRequestError({ + message: `User email domain (${domain}) is not supported`, + name: "Oauth 2 login" + }); + } + user = await userDAL.create({ email, firstName, lastName, authMethods: [authMethod] }); } const isLinkingRequired = !user?.authMethods?.includes(authMethod); diff --git a/backend/src/services/auth/auth-login-type.ts b/backend/src/services/auth/auth-login-type.ts index 67f640bc9e..54b27425b1 100644 --- a/backend/src/services/auth/auth-login-type.ts +++ b/backend/src/services/auth/auth-login-type.ts @@ -1,3 +1,5 @@ +import { TSuperAdmin } from "@app/db/schemas/super-admin"; + import { AuthMethod } from "./auth-type"; export type TLoginGenServerPublicKeyDTO = { @@ -29,4 +31,5 @@ export type TOauthLoginDTO = { authMethod: AuthMethod; callbackPort?: string; isSignupAllowed?: boolean; + serverCfg?: TSuperAdmin; }; diff --git a/frontend/src/hooks/api/admin/types.ts b/frontend/src/hooks/api/admin/types.ts index 593191d1f2..145f22072b 100644 --- a/frontend/src/hooks/api/admin/types.ts +++ b/frontend/src/hooks/api/admin/types.ts @@ -2,6 +2,7 @@ export type TServerConfig = { initialized: boolean; allowSignUp: boolean; inviteOnlySignUp: boolean; + allowSpecificDomainSignUp?: string; isMigrationModeOn?: boolean; }; diff --git a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx index 240a9d5de8..b0d07b7dcc 100644 --- a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx +++ b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx @@ -1,9 +1,14 @@ -import { useEffect, useState } from "react"; +import { FormEvent, useEffect, useState } from "react"; import { useRouter } from "next/router"; +import { faAt } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { + Button, ContentLoader, + FormControl, + Input, Select, SelectItem, Tab, @@ -24,11 +29,13 @@ export const AdminDashboardPage = () => { const router = useRouter(); const data = useServerConfig(); const [signUpMode, setSignUpMode] = useState("invite-only"); + const [allowSpecificDomain, setAllowSpecificDomain] = useState(); const { config } = data; const { user, isLoading: isUserLoading } = useUser(); const { orgs } = useOrganization(); const { mutate: updateServerConfig } = useUpdateServerConfig(); + const { createNotification } = useNotificationContext(); const isNotAllowed = !user?.superAdmin; @@ -49,22 +56,28 @@ export const AdminDashboardPage = () => { } if (config.inviteOnlySignUp) { setSignUpMode("invite-only"); - return; + } else { + setSignUpMode("anyone"); + } + + if (config.allowSpecificDomainSignUp) { + setAllowSpecificDomain(config.allowSpecificDomainSignUp); } - setSignUpMode("anyone"); }, [config]); - function handleSignUpModeChange(newSignUpMode: SignUpMode) { - config.allowSignUp = newSignUpMode !== "disabled"; - config.inviteOnlySignUp = newSignUpMode === "invite-only"; + async function handleSubmit(e: FormEvent) { + e.preventDefault(); + + config.allowSignUp = signUpMode !== "disabled"; + config.inviteOnlySignUp = signUpMode === "invite-only"; + config.allowSpecificDomainSignUp = signUpMode === "anyone" ? allowSpecificDomain : ""; + + await updateServerConfig(config); createNotification({ text: "Successfully changed sign up mode.", type: "success" }); - - updateServerConfig(config); - setSignUpMode(newSignUpMode); } return ( @@ -86,20 +99,49 @@ export const AdminDashboardPage = () => {
-
-
Allow user to Sign Up
- -
+
+
+
+ Allow user to Sign Up +
+ +
+ + {signUpMode === "anyone" && ( +
+
+ Allow email with only specific domain +
+ +
+ } + value={allowSpecificDomain} + onChange={(ev) => setAllowSpecificDomain(ev.target.value)} + /> +
+
+
+ )} + + +
From da377f6fdad2b85392cf5d6f14f1f80574d15200 Mon Sep 17 00:00:00 2001 From: Salman Date: Sat, 10 Feb 2024 23:00:16 +0530 Subject: [PATCH 010/525] Update error handling and refactor --- backend/src/server/routes/v1/sso-router.ts | 15 +++------------ backend/src/server/routes/v3/signup-router.ts | 6 +++++- backend/src/services/auth/auth-login-service.ts | 7 +++++-- backend/src/services/auth/auth-login-type.ts | 4 ---- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/backend/src/server/routes/v1/sso-router.ts b/backend/src/server/routes/v1/sso-router.ts index da0b12ca51..60bbec7db9 100644 --- a/backend/src/server/routes/v1/sso-router.ts +++ b/backend/src/server/routes/v1/sso-router.ts @@ -18,7 +18,6 @@ import { BadRequestError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; import { fetchGithubEmails } from "@app/lib/requests/github"; import { AuthMethod } from "@app/services/auth/auth-type"; -import { getServerCfg } from "@app/services/super-admin/super-admin-service"; export const registerSsoRouter = async (server: FastifyZodProvider) => { const appCfg = getConfig(); @@ -42,7 +41,6 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { async (req, _accessToken, _refreshToken, profile, cb) => { try { const email = profile?.emails?.[0]?.value; - const serverCfg = await getServerCfg(); if (!email) throw new BadRequestError({ message: "Email not found", @@ -54,8 +52,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { firstName: profile?.name?.givenName || "", lastName: profile?.name?.familyName || "", authMethod: AuthMethod.GOOGLE, - callbackPort: req.query.state as string, - serverCfg + callbackPort: req.query.state as string }); cb(null, { isUserCompleted, providerAuthToken }); } catch (error) { @@ -84,15 +81,12 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { try { const ghEmails = await fetchGithubEmails(accessToken); const { email } = ghEmails.filter((gitHubEmail) => gitHubEmail.primary)[0]; - const serverCfg = await getServerCfg(); const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({ email, firstName: profile.displayName, lastName: "", authMethod: AuthMethod.GITHUB, - callbackPort: req.query.state as string, - serverCfg - // isSignupAllowed: Boolean(serverCfg.allowSignUp) + callbackPort: req.query.state as string }); return cb(null, { isUserCompleted, providerAuthToken }); } catch (error) { @@ -121,15 +115,12 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => { async (req: any, _accessToken: string, _refreshToken: string, profile: any, cb: any) => { try { const email = profile.emails[0].value; - const serverCfg = await getServerCfg(); const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({ email, firstName: profile.displayName, lastName: "", authMethod: AuthMethod.GITLAB, - callbackPort: req.query.state as string, - serverCfg - // isSignupAllowed: Boolean(serverCfg.allowSignUp) + callbackPort: req.query.state as string }); return cb(null, { isUserCompleted, providerAuthToken }); diff --git a/backend/src/server/routes/v3/signup-router.ts b/backend/src/server/routes/v3/signup-router.ts index 11d79d2b5f..257a4c1733 100644 --- a/backend/src/server/routes/v3/signup-router.ts +++ b/backend/src/server/routes/v3/signup-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { UsersSchema } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; +import { BadRequestError } from "@app/lib/errors"; import { authRateLimit } from "@app/server/config/rateLimiter"; import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; @@ -29,7 +30,10 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { if (config?.allowSpecificDomainSignUp) { const domain = email.split("@")[1]; - if (domain !== config.allowSpecificDomainSignUp) throw new Error(`Unsupported email domain (${domain}).`); + if (domain !== config.allowSpecificDomainSignUp) + throw new BadRequestError({ + message: `User email domain (@${domain}) is not supported` + }); } await server.services.signup.beginEmailSignupProcess(email); return { message: `Sent an email verification code to ${email}` }; diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index ccfe619f43..569b3d374c 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -4,6 +4,7 @@ import { TUsers, UserDeviceSchema } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto"; import { BadRequestError } from "@app/lib/errors"; +import { getServerCfg } from "@app/services/super-admin/super-admin-service"; import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service"; import { TokenType } from "../auth-token/auth-token-types"; @@ -261,8 +262,10 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }: /* * OAuth2 login for google,github, and other oauth2 provider * */ - const oauth2Login = async ({ email, firstName, lastName, authMethod, callbackPort, serverCfg }: TOauthLoginDTO) => { + const oauth2Login = async ({ email, firstName, lastName, authMethod, callbackPort }: TOauthLoginDTO) => { let user = await userDAL.findUserByEmail(email); + const serverCfg = await getServerCfg(); + const appCfg = getConfig(); if (!user) { @@ -275,7 +278,7 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }: if (domain !== serverCfg.allowSpecificDomainSignUp) throw new BadRequestError({ - message: `User email domain (${domain}) is not supported`, + message: `User email domain (@${domain}) is not supported`, name: "Oauth 2 login" }); } diff --git a/backend/src/services/auth/auth-login-type.ts b/backend/src/services/auth/auth-login-type.ts index 54b27425b1..86af5a5f9e 100644 --- a/backend/src/services/auth/auth-login-type.ts +++ b/backend/src/services/auth/auth-login-type.ts @@ -1,5 +1,3 @@ -import { TSuperAdmin } from "@app/db/schemas/super-admin"; - import { AuthMethod } from "./auth-type"; export type TLoginGenServerPublicKeyDTO = { @@ -30,6 +28,4 @@ export type TOauthLoginDTO = { lastName?: string; authMethod: AuthMethod; callbackPort?: string; - isSignupAllowed?: boolean; - serverCfg?: TSuperAdmin; }; From 3f96f0a8fb7a20bcf81dfd5934ebf6196fafee22 Mon Sep 17 00:00:00 2001 From: Salman Date: Sun, 11 Feb 2024 00:40:17 +0530 Subject: [PATCH 011/525] Fix dropdown not working on page transition --- .../admin/DashboardPage/DashboardPage.tsx | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx index b0d07b7dcc..181dc890ad 100644 --- a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx +++ b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx @@ -1,4 +1,4 @@ -import { FormEvent, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useRouter } from "next/router"; import { faAt } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -65,9 +65,7 @@ export const AdminDashboardPage = () => { } }, [config]); - async function handleSubmit(e: FormEvent) { - e.preventDefault(); - + const handleSubmit = async () => { config.allowSignUp = signUpMode !== "disabled"; config.inviteOnlySignUp = signUpMode === "invite-only"; config.allowSpecificDomainSignUp = signUpMode === "anyone" ? allowSpecificDomain : ""; @@ -99,16 +97,13 @@ export const AdminDashboardPage = () => {
-
+
Allow user to Sign Up
} @@ -138,10 +133,10 @@ export const AdminDashboardPage = () => {
)} - - +
From 2ef8781378cc40e78a730d71d95fd8a6bb746d86 Mon Sep 17 00:00:00 2001 From: Salman Date: Sun, 11 Feb 2024 10:55:36 +0530 Subject: [PATCH 012/525] Update support multiple domain and error handling --- backend/src/ee/routes/v1/saml-router.ts | 2 +- backend/src/server/routes/v3/signup-router.ts | 12 ++++---- .../src/services/auth/auth-login-service.ts | 6 ++-- .../src/components/signup/EnterEmailStep.tsx | 17 +++++++++-- .../admin/DashboardPage/DashboardPage.tsx | 30 ++++++++++++------- 5 files changed, 45 insertions(+), 22 deletions(-) diff --git a/backend/src/ee/routes/v1/saml-router.ts b/backend/src/ee/routes/v1/saml-router.ts index 4c0e43cd6a..f81c21ffd4 100644 --- a/backend/src/ee/routes/v1/saml-router.ts +++ b/backend/src/ee/routes/v1/saml-router.ts @@ -105,7 +105,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => { email, firstName: profile.firstName as string, lastName: profile.lastName as string, - isSignupAllowed: Boolean(serverCfg.allowSignUp && serverCfg.inviteOnlySignUp), + isSignupAllowed: Boolean(serverCfg.allowSignUp), relayState: (req.body as { RelayState?: string }).RelayState, authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string, orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string diff --git a/backend/src/server/routes/v3/signup-router.ts b/backend/src/server/routes/v3/signup-router.ts index 257a4c1733..e0ce210065 100644 --- a/backend/src/server/routes/v3/signup-router.ts +++ b/backend/src/server/routes/v3/signup-router.ts @@ -4,6 +4,7 @@ import { UsersSchema } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError } from "@app/lib/errors"; import { authRateLimit } from "@app/server/config/rateLimiter"; +import { getServerCfg } from "@app/services/super-admin/super-admin-service"; import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; export const registerSignupRouter = async (server: FastifyZodProvider) => { @@ -25,15 +26,16 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { }, handler: async (req) => { const { email } = req.body; - const config = await server.services.superAdmin.initServerCfg(); + const serverCfg = await getServerCfg(); - if (config?.allowSpecificDomainSignUp) { + if (serverCfg?.allowSpecificDomainSignUp) { const domain = email.split("@")[1]; - - if (domain !== config.allowSpecificDomainSignUp) + const allowedDomains = serverCfg.allowSpecificDomainSignUp.split(",").map((e) => e.trim()); + if (!allowedDomains.includes(domain)) { throw new BadRequestError({ - message: `User email domain (@${domain}) is not supported` + message: `Email with a domain (@${domain}) is not supported` }); + } } await server.services.signup.beginEmailSignupProcess(email); return { message: `Sent an email verification code to ${email}` }; diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index 569b3d374c..f43fe063cc 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -275,10 +275,10 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }: if (serverCfg?.allowSpecificDomainSignUp) { const domain = email.split("@")[1]; - - if (domain !== serverCfg.allowSpecificDomainSignUp) + const allowedDomains = serverCfg.allowSpecificDomainSignUp.split(",").map((e) => e.trim()); + if (!allowedDomains.includes(domain)) throw new BadRequestError({ - message: `User email domain (@${domain}) is not supported`, + message: `Email with a domain (@${domain}) is not supported`, name: "Oauth 2 login" }); } diff --git a/frontend/src/components/signup/EnterEmailStep.tsx b/frontend/src/components/signup/EnterEmailStep.tsx index e317a4ea5b..058c231064 100644 --- a/frontend/src/components/signup/EnterEmailStep.tsx +++ b/frontend/src/components/signup/EnterEmailStep.tsx @@ -1,7 +1,9 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import Link from "next/link"; +import axios from "axios"; +import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { useSendVerificationEmail } from "@app/hooks/api"; import { Button, Input } from "../v2"; @@ -25,6 +27,7 @@ export default function EnterEmailStep({ setEmail, incrementStep }: DownloadBackupPDFStepProps): JSX.Element { + const { createNotification } = useNotificationContext(); const { mutateAsync } = useSendVerificationEmail(); const [emailError, setEmailError] = useState(false); const { t } = useTranslation(); @@ -46,8 +49,18 @@ export default function EnterEmailStep({ // If everything is correct, go to the next step if (!emailCheckBool) { - await mutateAsync({ email }); - incrementStep(); + try { + await mutateAsync({ email }); + incrementStep(); + } catch(e) { + if (axios.isAxiosError(e)) { + const { message = "Something went wrong" } = e.response?.data as { message: string}; + createNotification({ + type: "error", + text: message + }) + } + } } }; diff --git a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx index 181dc890ad..340c1662a5 100644 --- a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx +++ b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx @@ -66,17 +66,25 @@ export const AdminDashboardPage = () => { }, [config]); const handleSubmit = async () => { - config.allowSignUp = signUpMode !== "disabled"; - config.inviteOnlySignUp = signUpMode === "invite-only"; - config.allowSpecificDomainSignUp = signUpMode === "anyone" ? allowSpecificDomain : ""; + try { + config.allowSignUp = signUpMode !== "disabled"; + config.inviteOnlySignUp = signUpMode === "invite-only"; + config.allowSpecificDomainSignUp = signUpMode === "anyone" ? allowSpecificDomain : ""; - await updateServerConfig(config); + await updateServerConfig(config); - createNotification({ - text: "Successfully changed sign up mode.", - type: "success" - }); - } + createNotification({ + text: "Successfully changed sign up setting.", + type: "success" + }); + } catch (e) { + console.error(e); + createNotification({ + type: "error", + text: "Failed to update sign up setting." + }); + } + }; return (
@@ -118,12 +126,12 @@ export const AdminDashboardPage = () => { {signUpMode === "anyone" && (
- Allow email with only specific domain + Allow email with only specific domain(s)
} value={allowSpecificDomain} onChange={(ev) => setAllowSpecificDomain(ev.target.value)} From 0fb87ab05fd4df9df5f35763cba8540493c49812 Mon Sep 17 00:00:00 2001 From: Salman Date: Mon, 12 Feb 2024 04:09:46 +0530 Subject: [PATCH 013/525] Update move to react hook form, rename allowedSignUpDomain --- .../migrations/20240205200732_invite-only.ts | 6 +- backend/src/db/schemas/super-admin.ts | 2 +- backend/src/server/routes/v1/admin-router.ts | 2 +- backend/src/server/routes/v3/signup-router.ts | 4 +- .../src/services/auth/auth-login-service.ts | 4 +- frontend/src/hooks/api/admin/types.ts | 2 +- frontend/src/pages/signupinvite.tsx | 2 +- .../components/InitialStep/InitialStep.tsx | 2 +- .../admin/DashboardPage/DashboardPage.tsx | 139 +++++++++++------- 9 files changed, 100 insertions(+), 63 deletions(-) diff --git a/backend/src/db/migrations/20240205200732_invite-only.ts b/backend/src/db/migrations/20240205200732_invite-only.ts index d242623c8a..4bebab17f9 100644 --- a/backend/src/db/migrations/20240205200732_invite-only.ts +++ b/backend/src/db/migrations/20240205200732_invite-only.ts @@ -7,7 +7,7 @@ export async function up(knex: Knex): Promise { if (isTablePresent) { await knex.schema.alterTable(TableName.SuperAdmin, (t) => { t.boolean("inviteOnlySignUp").defaultTo(false); - t.string("allowSpecificDomainSignUp"); + t.string("allowedSignUpDomain"); }); } } @@ -19,9 +19,9 @@ export async function down(knex: Knex): Promise { }); } - if (await knex.schema.hasColumn(TableName.SuperAdmin, "allowSpecificDomainSignUp")) { + if (await knex.schema.hasColumn(TableName.SuperAdmin, "allowedSignUpDomain")) { await knex.schema.alterTable(TableName.SuperAdmin, (t) => { - t.dropColumn("allowSpecificDomainSignUp"); + t.dropColumn("allowedSignUpDomain"); }); } } diff --git a/backend/src/db/schemas/super-admin.ts b/backend/src/db/schemas/super-admin.ts index fb373e19be..4b410d88b9 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(), inviteOnlySignUp: z.boolean().default(false).nullable().optional(), - allowSpecificDomainSignUp: z.string().nullable().optional(), + allowedSignUpDomain: z.string().nullable().optional(), createdAt: z.date(), updatedAt: z.date() }); diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts index ddd95522f0..221014ccc4 100644 --- a/backend/src/server/routes/v1/admin-router.ts +++ b/backend/src/server/routes/v1/admin-router.ts @@ -33,7 +33,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => { body: z.object({ allowSignUp: z.boolean().optional(), inviteOnlySignUp: z.boolean().optional(), - allowSpecificDomainSignUp: z.string().optional() + allowedSignUpDomain: z.string().optional() }), response: { 200: z.object({ diff --git a/backend/src/server/routes/v3/signup-router.ts b/backend/src/server/routes/v3/signup-router.ts index e0ce210065..45434412ef 100644 --- a/backend/src/server/routes/v3/signup-router.ts +++ b/backend/src/server/routes/v3/signup-router.ts @@ -28,9 +28,9 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { const { email } = req.body; const serverCfg = await getServerCfg(); - if (serverCfg?.allowSpecificDomainSignUp) { + if (serverCfg?.allowedSignUpDomain) { const domain = email.split("@")[1]; - const allowedDomains = serverCfg.allowSpecificDomainSignUp.split(",").map((e) => e.trim()); + const allowedDomains = serverCfg.allowedSignUpDomain.split(",").map((e) => e.trim()); if (!allowedDomains.includes(domain)) { throw new BadRequestError({ message: `Email with a domain (@${domain}) is not supported` diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index f43fe063cc..e78b4080e7 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -273,9 +273,9 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }: if (!serverCfg?.allowSignUp) throw new BadRequestError({ message: "User signup disabled", name: "Oauth 2 login" }); - if (serverCfg?.allowSpecificDomainSignUp) { + if (serverCfg?.allowedSignUpDomain) { const domain = email.split("@")[1]; - const allowedDomains = serverCfg.allowSpecificDomainSignUp.split(",").map((e) => e.trim()); + const allowedDomains = serverCfg.allowedSignUpDomain.split(",").map((e) => e.trim()); if (!allowedDomains.includes(domain)) throw new BadRequestError({ message: `Email with a domain (@${domain}) is not supported`, diff --git a/frontend/src/hooks/api/admin/types.ts b/frontend/src/hooks/api/admin/types.ts index 145f22072b..e28e7f9729 100644 --- a/frontend/src/hooks/api/admin/types.ts +++ b/frontend/src/hooks/api/admin/types.ts @@ -2,7 +2,7 @@ export type TServerConfig = { initialized: boolean; allowSignUp: boolean; inviteOnlySignUp: boolean; - allowSpecificDomainSignUp?: string; + allowedSignUpDomain?: string; isMigrationModeOn?: boolean; }; diff --git a/frontend/src/pages/signupinvite.tsx b/frontend/src/pages/signupinvite.tsx index 5ede3126c5..a3b643e292 100644 --- a/frontend/src/pages/signupinvite.tsx +++ b/frontend/src/pages/signupinvite.tsx @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import crypto from "crypto"; -import { useEffect,useState } from "react"; +import { useEffect, useState } from "react"; import Head from "next/head"; import Image from "next/image"; import Link from "next/link"; diff --git a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx index bbef557511..16541c9a04 100644 --- a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx +++ b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx @@ -226,7 +226,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
{!isLoading && loginError && } - {config.allowSignUp ? ( + {config.allowSignUp && !config.inviteOnlySignUp ? (
diff --git a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx index 340c1662a5..912a60c183 100644 --- a/frontend/src/views/admin/DashboardPage/DashboardPage.tsx +++ b/frontend/src/views/admin/DashboardPage/DashboardPage.tsx @@ -1,7 +1,10 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; +import { Controller, useForm } from "react-hook-form"; import { useRouter } from "next/router"; import { faAt } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { @@ -23,15 +26,37 @@ enum TabSections { Settings = "settings" } -type SignUpMode = "disabled" | "invite-only" | "anyone"; +const formSchema = yup.object({ + signUpMode: yup + .string() + .oneOf(["disabled", "invite-only", "anyone"]) + .required(), + allowedSignUpDomain: yup.string().optional() +}); + +type TDashboardForm = yup.InferType; export const AdminDashboardPage = () => { const router = useRouter(); const data = useServerConfig(); - const [signUpMode, setSignUpMode] = useState("invite-only"); - const [allowSpecificDomain, setAllowSpecificDomain] = useState(); - const { config } = data; + + const signUpStatus = config.allowSignUp + ? config.inviteOnlySignUp && "invite-only" + : "disabled"; + + const signUpType = signUpStatus || "anyone"; + + const { control, handleSubmit, watch } = useForm({ + resolver: yupResolver(formSchema), + defaultValues: { + signUpMode: signUpType, + allowedSignUpDomain: config.allowedSignUpDomain + } + }); + + const signupMode = watch("signUpMode"); + const { user, isLoading: isUserLoading } = useUser(); const { orgs } = useOrganization(); const { mutate: updateServerConfig } = useUpdateServerConfig(); @@ -49,29 +74,15 @@ export const AdminDashboardPage = () => { } }, [isNotAllowed, isUserLoading]); - useEffect(() => { - if (!config.allowSignUp) { - setSignUpMode("disabled"); - return; - } - if (config.inviteOnlySignUp) { - setSignUpMode("invite-only"); - } else { - setSignUpMode("anyone"); - } - - if (config.allowSpecificDomainSignUp) { - setAllowSpecificDomain(config.allowSpecificDomainSignUp); - } - }, [config]); - - const handleSubmit = async () => { + const onFormSubmit = async (formData: TDashboardForm) => { try { - config.allowSignUp = signUpMode !== "disabled"; - config.inviteOnlySignUp = signUpMode === "invite-only"; - config.allowSpecificDomainSignUp = signUpMode === "anyone" ? allowSpecificDomain : ""; + const { signUpMode, allowedSignUpDomain } = formData; - await updateServerConfig(config); + await updateServerConfig({ + allowSignUp: signUpMode !== "disabled", + inviteOnlySignUp: signUpMode === "invite-only", + allowedSignUpDomain: signUpMode === "anyone" ? allowedSignUpDomain : "" + }); createNotification({ text: "Successfully changed sign up setting.", @@ -86,6 +97,8 @@ export const AdminDashboardPage = () => { } }; + + return (
@@ -105,46 +118,70 @@ export const AdminDashboardPage = () => {
-
+
Allow user to Sign Up
- + ( + + + + )} + />
- {signUpMode === "anyone" && ( + {signupMode === "anyone" && (
Allow email with only specific domain(s)
- -
- } - value={allowSpecificDomain} - onChange={(ev) => setAllowSpecificDomain(ev.target.value)} - /> -
-
+ ( + + } + /> + + )} + />
)} - -
+
From 0ff3ddb0c86be4c8df2f4cf8c402bb5ab5fb4d37 Mon Sep 17 00:00:00 2001 From: Salman Date: Wed, 14 Feb 2024 07:52:46 +0530 Subject: [PATCH 014/525] Update generate schema run --- backend/src/db/schemas/super-admin.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/db/schemas/super-admin.ts b/backend/src/db/schemas/super-admin.ts index 4b410d88b9..707ec60e91 100644 --- a/backend/src/db/schemas/super-admin.ts +++ b/backend/src/db/schemas/super-admin.ts @@ -11,10 +11,10 @@ export const SuperAdminSchema = z.object({ id: z.string().uuid(), initialized: z.boolean().default(false).nullable().optional(), allowSignUp: z.boolean().default(true).nullable().optional(), - inviteOnlySignUp: z.boolean().default(false).nullable().optional(), - allowedSignUpDomain: z.string().nullable().optional(), createdAt: z.date(), - updatedAt: z.date() + updatedAt: z.date(), + inviteOnlySignUp: z.boolean().default(false).nullable().optional(), + allowedSignUpDomain: z.string().nullable().optional() }); export type TSuperAdmin = z.infer; From ee69bccb6eee58a1775c8e22f89738bdcd9637db Mon Sep 17 00:00:00 2001 From: Salman Date: Wed, 14 Feb 2024 14:18:44 +0530 Subject: [PATCH 015/525] Update disabled sign up routes --- backend/src/server/routes/v3/signup-router.ts | 27 +++++++++++++++++++ .../src/services/auth/auth-login-service.ts | 3 +-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/backend/src/server/routes/v3/signup-router.ts b/backend/src/server/routes/v3/signup-router.ts index 45434412ef..522af7e9f3 100644 --- a/backend/src/server/routes/v3/signup-router.ts +++ b/backend/src/server/routes/v3/signup-router.ts @@ -26,7 +26,13 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { }, handler: async (req) => { const { email } = req.body; + const serverCfg = await getServerCfg(); + if (!serverCfg.allowSignUp) { + throw new BadRequestError({ + message: "Sign up is disabled!" + }); + } if (serverCfg?.allowedSignUpDomain) { const domain = email.split("@")[1]; @@ -62,6 +68,13 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { } }, handler: async (req) => { + const serverCfg = await getServerCfg(); + if (!serverCfg.allowSignUp) { + throw new BadRequestError({ + message: "Sign up is disabled!" + }); + } + const { token, user } = await server.services.signup.verifyEmailSignup(req.body.email, req.body.code); return { message: "Successfuly verified email", token, user }; } @@ -104,6 +117,13 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { if (!userAgent) throw new Error("user agent header is required"); const appCfg = getConfig(); + const serverCfg = await getServerCfg(); + if (!serverCfg.allowSignUp) { + throw new BadRequestError({ + message: "Sign up is disabled!" + }); + } + const { user, accessToken, refreshToken } = await server.services.signup.completeEmailAccountSignup({ ...req.body, ip: req.realIp, @@ -167,6 +187,13 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => { if (!userAgent) throw new Error("user agent header is required"); const appCfg = getConfig(); + const serverCfg = await getServerCfg(); + if (!serverCfg.allowSignUp) { + throw new BadRequestError({ + message: "Sign up is disabled!" + }); + } + const { user, accessToken, refreshToken } = await server.services.signup.completeAccountInvite({ ...req.body, ip: req.realIp, diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts index e78b4080e7..04304980ad 100644 --- a/backend/src/services/auth/auth-login-service.ts +++ b/backend/src/services/auth/auth-login-service.ts @@ -270,8 +270,7 @@ export const authLoginServiceFactory = ({ userDAL, tokenService, smtpService }: if (!user) { // Create a new user based on oAuth - if (!serverCfg?.allowSignUp) - throw new BadRequestError({ message: "User signup disabled", name: "Oauth 2 login" }); + if (!serverCfg?.allowSignUp) throw new BadRequestError({ message: "Sign Up disabled", name: "Oauth 2 login" }); if (serverCfg?.allowedSignUpDomain) { const domain = email.split("@")[1]; From c73ee494250b06db5af854a0104ec627bb04f107 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Wed, 14 Feb 2024 09:22:23 -0800 Subject: [PATCH 016/525] Pass Okta SCIM 2.0 SPEC Test --- backend/src/@types/fastify.d.ts | 2 + backend/src/@types/knex.d.ts | 8 + .../migrations/20240208234120_scim-token.ts | 6 +- backend/src/db/schemas/index.ts | 1 + backend/src/db/schemas/scim-tokens.ts | 21 ++ backend/src/ee/routes/v1/scim-router.ts | 235 +++++++++--- .../ee/services/audit-log/audit-log-types.ts | 8 +- .../src/ee/services/license/licence-fns.ts | 4 +- .../src/ee/services/license/license-types.ts | 4 +- backend/src/ee/services/scim/scim-dal.ts | 10 + backend/src/ee/services/scim/scim-service.ts | 229 ++++++++++++ backend/src/ee/services/scim/scim-types.ts | 41 ++ backend/src/lib/errors/index.ts | 17 + backend/src/lib/scim/fns.ts | 39 ++ backend/src/lib/scim/index.ts | 4 + backend/src/lib/scim/types.ts | 23 ++ backend/src/server/plugins/audit-log.ts | 4 + .../server/plugins/auth/inject-identity.ts | 12 +- .../server/plugins/auth/inject-permission.ts | 2 + backend/src/server/plugins/error-handler.ts | 8 +- backend/src/server/routes/index.ts | 11 + backend/src/services/auth/auth-type.ts | 2 +- frontend/src/hooks/api/scim/index.tsx | 8 +- frontend/src/hooks/api/scim/mutations.tsx | 46 +++ frontend/src/hooks/api/scim/queries.tsx | 19 +- frontend/src/hooks/api/scim/types.ts | 25 +- frontend/src/hooks/api/subscriptions/types.ts | 1 + .../components/OrgAuthTab/OrgSCIMSection.tsx | 91 ++--- .../components/OrgAuthTab/ScimTokenModal.tsx | 353 ++++++++++++++++++ 29 files changed, 1098 insertions(+), 136 deletions(-) create mode 100644 backend/src/db/schemas/scim-tokens.ts create mode 100644 backend/src/ee/services/scim/scim-dal.ts create mode 100644 backend/src/ee/services/scim/scim-service.ts create mode 100644 backend/src/ee/services/scim/scim-types.ts create mode 100644 backend/src/lib/scim/fns.ts create mode 100644 backend/src/lib/scim/index.ts create mode 100644 backend/src/lib/scim/types.ts create mode 100644 frontend/src/hooks/api/scim/mutations.tsx diff --git a/backend/src/@types/fastify.d.ts b/backend/src/@types/fastify.d.ts index ef850bb8c6..bd7a09d865 100644 --- a/backend/src/@types/fastify.d.ts +++ b/backend/src/@types/fastify.d.ts @@ -6,6 +6,7 @@ import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service"; +import { TScimServiceFactory } from "@app/ee/services/scim/scim-service"; import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service"; import { TSecretApprovalRequestServiceFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-service"; import { TSecretRotationServiceFactory } from "@app/ee/services/secret-rotation/secret-rotation-service"; @@ -105,6 +106,7 @@ declare module "fastify" { secretRotation: TSecretRotationServiceFactory; snapshot: TSecretSnapshotServiceFactory; saml: TSamlConfigServiceFactory; + scim: TScimServiceFactory; auditLog: TAuditLogServiceFactory; secretScanning: TSecretScanningServiceFactory; license: TLicenseServiceFactory; diff --git a/backend/src/@types/knex.d.ts b/backend/src/@types/knex.d.ts index 5bf5638104..1b4e82176f 100644 --- a/backend/src/@types/knex.d.ts +++ b/backend/src/@types/knex.d.ts @@ -83,6 +83,9 @@ import { TSamlConfigs, TSamlConfigsInsert, TSamlConfigsUpdate, + TScimTokens, + TScimTokensInsert, + TScimTokensUpdate, TSecretApprovalPolicies, TSecretApprovalPoliciesApprovers, TSecretApprovalPoliciesApproversInsert, @@ -262,6 +265,11 @@ declare module "knex/types/tables" { TIdentityProjectMembershipsInsert, TIdentityProjectMembershipsUpdate >; + [TableName.ScimToken]: Knex.CompositeTableType< + TScimTokens, + TScimTokensInsert, + TScimTokensUpdate + >; [TableName.SecretApprovalPolicy]: Knex.CompositeTableType< TSecretApprovalPolicies, TSecretApprovalPoliciesInsert, diff --git a/backend/src/db/migrations/20240208234120_scim-token.ts b/backend/src/db/migrations/20240208234120_scim-token.ts index 60753e3605..92364dba2e 100644 --- a/backend/src/db/migrations/20240208234120_scim-token.ts +++ b/backend/src/db/migrations/20240208234120_scim-token.ts @@ -7,15 +7,15 @@ export async function up(knex: Knex): Promise { if (!(await knex.schema.hasTable(TableName.ScimToken))) { await knex.schema.createTable(TableName.ScimToken, (t) => { t.string("id", 36).primary().defaultTo(knex.fn.uuid()); - t.bigInteger("tokenTTL").defaultTo(15552000).notNullable(); // 180 days second - t.datetime("tokenLastUsedAt"); + t.bigInteger("ttl").defaultTo(15552000).notNullable(); // 180 days second + t.string("description").notNullable(); t.uuid("orgId").notNullable(); t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE"); t.timestamps(true, true, true); }); } - await createOnUpdateTrigger(knex, TableName.IdentityAccessToken); + await createOnUpdateTrigger(knex, TableName.ScimToken); } export async function down(knex: Knex): Promise { diff --git a/backend/src/db/schemas/index.ts b/backend/src/db/schemas/index.ts index 62b01ebd29..b330c90b71 100644 --- a/backend/src/db/schemas/index.ts +++ b/backend/src/db/schemas/index.ts @@ -26,6 +26,7 @@ export * from "./project-memberships"; export * from "./project-roles"; export * from "./projects"; export * from "./saml-configs"; +export * from "./scim-tokens"; export * from "./secret-approval-policies"; export * from "./secret-approval-policies-approvers"; export * from "./secret-approval-request-secret-tags"; diff --git a/backend/src/db/schemas/scim-tokens.ts b/backend/src/db/schemas/scim-tokens.ts new file mode 100644 index 0000000000..ac391ca917 --- /dev/null +++ b/backend/src/db/schemas/scim-tokens.ts @@ -0,0 +1,21 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { TImmutableDBKeys } from "./models"; + +export const ScimTokensSchema = z.object({ + id: z.string(), + ttl: z.coerce.number().default(15552000), + description: z.string(), + orgId: z.string().uuid(), + createdAt: z.date(), + updatedAt: z.date(), +}); + +export type TScimTokens = z.infer; +export type TScimTokensInsert = Omit; +export type TScimTokensUpdate = Partial>; diff --git a/backend/src/ee/routes/v1/scim-router.ts b/backend/src/ee/routes/v1/scim-router.ts index 00478458af..2e2edac43e 100644 --- a/backend/src/ee/routes/v1/scim-router.ts +++ b/backend/src/ee/routes/v1/scim-router.ts @@ -1,10 +1,13 @@ import jwt from "jsonwebtoken"; import { z } from "zod"; +import { ScimTokensSchema } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode, AuthTokenType } from "@app/services/auth/auth-type"; + + export const registerScimRouter = async (server: FastifyZodProvider) => { server.route({ url: "/", @@ -27,29 +30,177 @@ export const registerScimRouter = async (server: FastifyZodProvider) => { url: "/Users", method: "GET", schema: { - params: z.object({}), + querystring: z.object({ + startIndex: z.coerce.number().default(1), + count: z.coerce.number().default(20), + filter: z.string().trim().optional() + }), response: { - 200: z.object({}) + 200: z.object({ // TODO: audit the response + Resources: z.array(z.object({ + id: z.string().trim(), + userName: z.string().trim(), + name: z.object({ + familyName: z.string().trim(), + givenName: z.string().trim() + }), + emails: z.array(z.object({ + primary: z.boolean(), + value: z.string().email(), + type: z.string().trim() + })), + displayName: z.string().trim(), + active: z.boolean() + })), + itemsPerPage: z.number(), + schemas: z.array(z.string()), + startIndex: z.number(), + totalResults: z.number(), + }) } }, - // onRequest: verifyAuth([]), - handler: async () => { - return { - hello: "world" - }; + onRequest: verifyAuth([AuthMode.SCIM_TOKEN]), + handler: async (req) => { + const res = await req.server.services.scim.listUsers({ + offset: req.query.startIndex, + limit: req.query.count, + filter: req.query.filter + }); + return res; } }); server.route({ - url: "/tokens/organizations/:organizationId", // api/v1/scim/token/organizations/:organizationId + url: "/Users/:userId", + method: "GET", + schema: { + params: z.object({ + userId: z.string().trim() + }), + response: { + 201: z.object({ + schemas: z.array(z.string()), + id: z.string().trim(), + userName: z.string().trim(), + name: z.object({ + familyName: z.string().trim(), + givenName: z.string().trim() + }), + emails: z.array(z.object({ + primary: z.boolean(), + value: z.string().email(), + type: z.string().trim() + })), + displayName: z.string().trim(), + active: z.boolean() + }) + } + }, + onRequest: verifyAuth([AuthMode.SCIM_TOKEN]), + handler: async (req) => { + const res = await req.server.services.scim.getUser(req.params.userId); + return res; + } + }); + + server.route({ + url: "/Users", + method: "POST", + schema: { + body: z.object({ + schemas: z.array(z.string()), + userName: z.string().trim(), + name: z.object({ + familyName: z.string().trim(), + givenName: z.string().trim() + }), + emails: z.array(z.object({ + primary: z.boolean(), + value: z.string().email(), + type: z.string().trim() + })), + displayName: z.string().trim(), + // locale: z.string().trim(), + // externalId: z.string().trim(), + // groups: z.array(z.object({ + // value: z.string().trim() + // })), + // password: z.string().trim(), + active: z.boolean() + }), + response: { + 200: z.object({ + schemas: z.array(z.string()), + id: z.string().trim(), + userName: z.string().trim(), + name: z.object({ + familyName: z.string().trim(), + givenName: z.string().trim() + }), + emails: z.array(z.object({ + primary: z.boolean(), + value: z.string().email(), + type: z.string().trim() + })), + displayName: z.string().trim(), + active: z.boolean() + }) + } + }, + onRequest: verifyAuth([AuthMode.SCIM_TOKEN]), + handler: async (req, reply) => { + const user = await req.server.services.scim.createUser({ + email: req.body.emails[0].value, + firstName: req.body.name.givenName, + lastName: req.body.name.familyName, + orgId: req.permission.orgId as string + }); + + reply.code(201); + return user; + } + }); + + server.route({ + url: "/Users/:userId", + method: "PATCH", + schema: { + body: z.object({}), + response: { + 200: z.object({}) + } + }, + onRequest: verifyAuth([AuthMode.SCIM_TOKEN]), + handler: async (req) => { + // TODO: update a user's attr + return {}; + } + }); + + server.route({ + url: "/Users/:userId", + method: "PUT", + schema: { + body: z.object({}), + response: { + 200: z.object({}) + } + }, + onRequest: verifyAuth([AuthMode.SCIM_TOKEN]), + handler: async (req) => { + // TODO: update a user's profile + return {}; + } + }); + + server.route({ + url: "/scim-tokens", method: "POST", onRequest: verifyAuth([AuthMode.JWT]), schema: { - params: z.object({ - organizationId: z.string().trim() - }), body: z.object({ - description: z.string().trim(), + organizationId: z.string().trim(), + description: z.string().trim().default(""), ttl: z.number().min(0).default(0) }), response: { @@ -58,53 +209,53 @@ export const registerScimRouter = async (server: FastifyZodProvider) => { }) } }, - handler: async () => { - // TODO: create SCIM token logic - // TODO: create SCIM token controller - - const appCfg = getConfig(); - const scimToken = jwt.sign( - { - authTokenType: AuthTokenType.SCIM_TOKEN - }, - appCfg.AUTH_SECRET, - { - // expiresIn: identityAccessToken.accessTokenMaxTTL === 0 ? undefined : identityAccessToken.accessTokenMaxTTL - } - ); // TODO: add expiration + handler: async (req) => { + const { scimToken } = await server.services.scim.createScimToken({ + organizationId: req.body.organizationId, + description: req.body.description, + ttl: req.body.ttl + }); return { scimToken }; } }); server.route({ - url: "/tokens/organizations/:organizationId", // api/v1/scim/token/organizations/:organizationId + url: "/scim-tokens", method: "GET", onRequest: verifyAuth([AuthMode.JWT]), schema: { - params: z.object({ + querystring: z.object({ organizationId: z.string().trim() }), response: { 200: z.object({ - scimToken: z.string().trim() + scimTokens: z.array(ScimTokensSchema) }) } }, - handler: async () => { - // TODO: put into service file - - const appCfg = getConfig(); - const scimToken = jwt.sign( - { - authTokenType: AuthTokenType.SCIM_TOKEN - }, - appCfg.AUTH_SECRET, - { - // expiresIn: identityAccessToken.accessTokenMaxTTL === 0 ? undefined : identityAccessToken.accessTokenMaxTTL - } - ); // TODO: add expiration + handler: async (req) => { + const scimTokens = await server.services.scim.getScimTokens(req.query.organizationId); + return { scimTokens }; + } + }); + server.route({ + url: "/scim-tokens/:scimTokenId", + method: "DELETE", + onRequest: verifyAuth([AuthMode.JWT]), + schema: { + params: z.object({ + scimTokenId: z.string().trim() + }), + response: { + 200: z.object({ + scimToken: ScimTokensSchema + }) + } + }, + handler: async (req) => { + const scimToken = await server.services.scim.deleteScimToken(req.params.scimTokenId); return { scimToken }; } }); diff --git a/backend/src/ee/services/audit-log/audit-log-types.ts b/backend/src/ee/services/audit-log/audit-log-types.ts index 011dffebea..dfa98b0e41 100644 --- a/backend/src/ee/services/audit-log/audit-log-types.ts +++ b/backend/src/ee/services/audit-log/audit-log-types.ts @@ -15,7 +15,7 @@ export type TListProjectAuditLogDTO = { export type TCreateAuditLogDTO = { event: Event; - actor: UserActor | IdentityActor | ServiceActor; + actor: UserActor | IdentityActor | ServiceActor | ScimIdpActor; orgId?: string; projectId?: string; } & BaseAuthData; @@ -120,7 +120,11 @@ export interface IdentityActor { metadata: IdentityActorMetadata; } -export type Actor = UserActor | ServiceActor | IdentityActor; +export interface ScimClientActor { + type: ActorType.SCIM_CLIENT; +} + +export type Actor = UserActor | ServiceActor | IdentityActor | ScimClientActor; interface GetSecretsEvent { type: EventType.GET_SECRETS; diff --git a/backend/src/ee/services/license/licence-fns.ts b/backend/src/ee/services/license/licence-fns.ts index 9b1d4c203d..d41e457782 100644 --- a/backend/src/ee/services/license/licence-fns.ts +++ b/backend/src/ee/services/license/licence-fns.ts @@ -23,8 +23,8 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ customAlerts: false, auditLogs: false, auditLogsRetentionDays: 0, - samlSSO: false, - scim: false, + samlSSO: true, + scim: true, status: null, trial_end: null, has_used_trial: true, diff --git a/backend/src/ee/services/license/license-types.ts b/backend/src/ee/services/license/license-types.ts index 3547144894..bc83f43f0c 100644 --- a/backend/src/ee/services/license/license-types.ts +++ b/backend/src/ee/services/license/license-types.ts @@ -24,8 +24,8 @@ export type TFeatureSet = { customAlerts: false; auditLogs: false; auditLogsRetentionDays: 0; - samlSSO: false; - scim: false; + samlSSO: true; + scim: true; status: null; trial_end: null; has_used_trial: true; diff --git a/backend/src/ee/services/scim/scim-dal.ts b/backend/src/ee/services/scim/scim-dal.ts new file mode 100644 index 0000000000..05c21b80c3 --- /dev/null +++ b/backend/src/ee/services/scim/scim-dal.ts @@ -0,0 +1,10 @@ +import { TDbClient } from "@app/db"; +import { TableName } from "@app/db/schemas"; +import { ormify } from "@app/lib/knex"; + +export type TScimDALFactory = ReturnType; + +export const scimDALFactory = (db: TDbClient) => { + const scimTokenOrm = ormify(db, TableName.ScimToken); + return scimTokenOrm; +}; diff --git a/backend/src/ee/services/scim/scim-service.ts b/backend/src/ee/services/scim/scim-service.ts new file mode 100644 index 0000000000..12eb2e94a6 --- /dev/null +++ b/backend/src/ee/services/scim/scim-service.ts @@ -0,0 +1,229 @@ +import jwt from "jsonwebtoken"; + +import { + OrgMembershipRole, + OrgMembershipStatus +} from "@app/db/schemas"; +import { TScimDALFactory } from "@app/ee/services/scim/scim-dal"; +import { TUserDALFactory } from "@app/services/user/user-dal"; +import { TOrgDALFactory } from "@app/services/org/org-dal"; +import { TPermissionServiceFactory } from "../permission/permission-service"; +import { TLicenseServiceFactory } from "../license/license-service"; +import { getConfig } from "@app/lib/config/env"; +import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type"; +import { + TCreateScimTokenDTO, + TListScimUsersDTO, + TListScimUsersRes, + TCreateScimUserDTO, + TScimTokenJwtPayload +} from "./scim-types"; +import { + createScimUser, + TScimUser, +} from "@app/lib/scim"; +import { UnauthorizedError, ScimRequestError } from "@app/lib/errors"; + +type TScimServiceFactoryDep = { + permissionService: Pick; + scimDAL: TScimDALFactory; // TODO: pick + userDAL: TUserDALFactory; // TODO: pick + orgDAL: TOrgDALFactory; // TODO: pick + licenseService: Pick; +}; + +export type TScimServiceFactory = ReturnType; + +export const scimServiceFactory = ({ + licenseService, + scimDAL, + userDAL, + orgDAL, + permissionService +}: TScimServiceFactoryDep) => { + const createScimToken = async ({ + organizationId, + description, + ttl + }: TCreateScimTokenDTO) => { + const appCfg = getConfig(); + + // TODO: permission stuff + + const scimTokenData = await scimDAL.create({ + orgId: organizationId, + description, + ttl + }); + + const scimToken = jwt.sign( + { + scimTokenId: scimTokenData.id, + authTokenType: AuthTokenType.SCIM_TOKEN + }, + appCfg.AUTH_SECRET + ); + + return { scimToken } + } + + const getScimTokens = async (organizationId: string) => { + const scimTokens = await scimDAL.find({ orgId: organizationId }); + return scimTokens; + } + + const deleteScimToken = async (scimTokenId: string) => { + const scimToken = await scimDAL.deleteById(scimTokenId); + return scimToken; + } + + // scim server endpoints + + const listUsers = async ({ + offset, + limit, + filter + }: TListScimUsersDTO): Promise => { + + const parseFilter = (filter: string | undefined) => { + if (!filter) return {}; + const [parsedName, parsedValue] = filter.split("eq").map(s => s.trim()); + + let attributeName = parsedName; + if (parsedName === "userName") { // note + attributeName = "email"; + } + + return { [attributeName]: parsedValue }; + }; + + const findOpts = { + ...(offset && { offset }), + ...(limit && { limit }), + }; + + const users = await userDAL.find(parseFilter(filter), findOpts); + + let resources: TScimUser[] = []; + + let scimResource: TListScimUsersRes = { // note: type + Resources: [], + itemsPerPage: limit, + schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"], + startIndex: offset, + totalResults: users.length + }; + + users.forEach((user) => { + let scimUser = createScimUser({ + userId: user.id, + firstName: user.firstName as string, + lastName: user.lastName as string, + email: user.email + }); + resources.push(scimUser); + }); + + scimResource.Resources = resources; + + return scimResource; + } + + const getUser = async (userId: string) => { + // TODO: check out SCIM-specific errors + + let user; + try { + user = await userDAL.findById(userId); + } catch (error) { + + interface PostgresError extends Error { + error: { + code: string; + } + } + + const dbError = error as PostgresError; + + if (dbError.error.code === "22P02") throw new ScimRequestError({ + detail: "User not found", + status: 404 + }); + + throw error; + } + + if (!user) throw new ScimRequestError({ + detail: "User not found", + status: 404 + }); + + return createScimUser({ + userId: user.id, + firstName: user.firstName as string, + lastName: user.lastName as string, + email: user.email + }); + } + + const createUser = async ({ + firstName, + lastName, + email, + orgId + }: TCreateScimUserDTO) => { + let user = await userDAL.findOne({ + email + }); + + if (user) throw new ScimRequestError({ + detail: "User already exists in the database", + status: 409 + }); + + user = await userDAL.transaction(async (tx) => { + const newUser = await userDAL.create( + { + email, + firstName, + lastName, + authMethods: [AuthMethod.EMAIL] + }, + tx + ); + await orgDAL.createMembership({ + inviteEmail: email, + orgId, + role: OrgMembershipRole.Member, + status: OrgMembershipStatus.Invited + }); + return newUser; + }); + + return createScimUser({ + userId: user.id, + firstName: user.firstName as string, + lastName: user.lastName as string, + email: user.email + }); + } + + const fnValidateScimToken = async (token: TScimTokenJwtPayload) => { + // TODO: check expiry + + const scimToken = await scimDAL.findById(token.scimTokenId); + if (!scimToken) throw new UnauthorizedError(); + + return { scimTokenId: scimToken.id, orgId: scimToken.orgId }; + } + + return { + createScimToken, + getScimTokens, + deleteScimToken, + listUsers, + getUser, + createUser, + fnValidateScimToken + }; +}; diff --git a/backend/src/ee/services/scim/scim-types.ts b/backend/src/ee/services/scim/scim-types.ts new file mode 100644 index 0000000000..a10e9cc8eb --- /dev/null +++ b/backend/src/ee/services/scim/scim-types.ts @@ -0,0 +1,41 @@ +import { TOrgPermission } from "@app/lib/types"; +import { TScimUser } from "@app/lib/scim"; + +export type TCreateScimTokenDTO = { + organizationId: string; + description: string; + ttl: number; +} + +// TODO: add org permissions +// & Omit; + +export type TListScimUsersDTO = { + offset: number; + limit: number; + filter?: string; +} + +export type TListScimUsersRes = { // check naming here + schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"]; + totalResults: number; + Resources: TScimUser[]; + itemsPerPage: number; + startIndex: number; +} + +export type TCreateScimUserDTO = { + email: string; + firstName: string; + lastName: string; + orgId: string; +} + +export type TCreateScimUserRes = { + schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"] +} + +export type TScimTokenJwtPayload = { + scimTokenId: string; + authTokenType: string; +}; \ No newline at end of file diff --git a/backend/src/lib/errors/index.ts b/backend/src/lib/errors/index.ts index b4376f007d..a46d020669 100644 --- a/backend/src/lib/errors/index.ts +++ b/backend/src/lib/errors/index.ts @@ -58,3 +58,20 @@ export class BadRequestError extends Error { this.error = error; } } + +export class ScimRequestError extends Error { + name: string; + schemas: string[]; + detail: string; + status: number; + error: unknown; + + constructor({ name, error, detail, status }: { message?: string; name?: string; error?: unknown, detail: string, status: number }) { + super(detail ?? "The request is invalid"); + this.name = name || "ScimRequestError"; + this.schemas = ["urn:ietf:params:scim:api:messages:2.0:Error"]; + this.error = error; + this.detail = detail; + this.status = status; + } +} \ No newline at end of file diff --git a/backend/src/lib/scim/fns.ts b/backend/src/lib/scim/fns.ts new file mode 100644 index 0000000000..430dd8fbbe --- /dev/null +++ b/backend/src/lib/scim/fns.ts @@ -0,0 +1,39 @@ +import { TScimUser } from "./types"; + +export const createScimUser = ({ + userId, + firstName, + lastName, + email +}: { + userId: string; + firstName: string; + lastName: string; + email: string; +}): TScimUser => { + let scimUser = { + "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], + "id": userId, + "userName": email, + "displayName": `${firstName} ${lastName}`, + "name": { + "givenName": firstName, + "middleName": null, + "familyName": lastName + }, + "emails": + [{ + "primary": true, + "value": email, + "type": "work" + }], + "active": true, + "groups": [], + "meta": { + "resourceType": "User", + "location": null + } + }; + + return scimUser; +} \ No newline at end of file diff --git a/backend/src/lib/scim/index.ts b/backend/src/lib/scim/index.ts new file mode 100644 index 0000000000..8ab53e080d --- /dev/null +++ b/backend/src/lib/scim/index.ts @@ -0,0 +1,4 @@ +export { TScimUser } from "./types"; +export { + createScimUser +} from "./fns"; \ No newline at end of file diff --git a/backend/src/lib/scim/types.ts b/backend/src/lib/scim/types.ts new file mode 100644 index 0000000000..f439c004e3 --- /dev/null +++ b/backend/src/lib/scim/types.ts @@ -0,0 +1,23 @@ + +export type TScimUser = { + schemas: string[]; + id: string; + userName: string; + displayName: string; + name: { + givenName: string; + middleName: null; + familyName: string; + }; + emails: { + primary: boolean; + value: string; + type: string; + }[]; + active: boolean; + groups: string[]; + meta: { + resourceType: string; + location: null; + }; +} \ No newline at end of file diff --git a/backend/src/server/plugins/audit-log.ts b/backend/src/server/plugins/audit-log.ts index b42cc2ff22..33144683cc 100644 --- a/backend/src/server/plugins/audit-log.ts +++ b/backend/src/server/plugins/audit-log.ts @@ -63,6 +63,10 @@ export const injectAuditLogInfo = fp(async (server: FastifyZodProvider) => { identityId: req.auth.identityId } }; + } else if (req.auth.actor === ActorType.SCIM_CLIENT) { + payload.actor = { + type: ActorType.SCIM_CLIENT + }; } else { throw new BadRequestError({ message: "Missing logic for other actor" }); } diff --git a/backend/src/server/plugins/auth/inject-identity.ts b/backend/src/server/plugins/auth/inject-identity.ts index a819f66d83..7066764ff7 100644 --- a/backend/src/server/plugins/auth/inject-identity.ts +++ b/backend/src/server/plugins/auth/inject-identity.ts @@ -7,6 +7,7 @@ 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 { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types"; +import { TScimTokenJwtPayload } from "@app/ee/services/scim/scim-types"; export type TAuthMode = | { @@ -38,7 +39,9 @@ export type TAuthMode = } | { authMode: AuthMode.SCIM_TOKEN; - actor: ActorType.SCIM_IDP; + actor: ActorType.SCIM_CLIENT; + scimTokenId: string; + orgId: string; }; const extractAuth = async (req: FastifyRequest, jwtSecret: string) => { @@ -78,8 +81,8 @@ const extractAuth = async (req: FastifyRequest, jwtSecret: string) => { case AuthTokenType.SCIM_TOKEN: return { authMode: AuthMode.SCIM_TOKEN, - token: decodedToken, - actor: ActorType.SCIM_IDP + token: decodedToken as TScimTokenJwtPayload, + actor: ActorType.SCIM_CLIENT } as const; default: return { authMode: null, token: null } as const; @@ -125,7 +128,8 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => { break; } case AuthMode.SCIM_TOKEN: { - req.auth = { authMode: AuthMode.SCIM_TOKEN, actor }; + const { orgId, scimTokenId } = await server.services.scim.fnValidateScimToken(token); + req.auth = { authMode: AuthMode.SCIM_TOKEN, actor, scimTokenId, orgId }; break; } default: diff --git a/backend/src/server/plugins/auth/inject-permission.ts b/backend/src/server/plugins/auth/inject-permission.ts index 572814d645..2d61647e8e 100644 --- a/backend/src/server/plugins/auth/inject-permission.ts +++ b/backend/src/server/plugins/auth/inject-permission.ts @@ -14,6 +14,8 @@ export const injectPermission = fp(async (server) => { req.permission = { type: ActorType.IDENTITY, id: req.auth.identityId }; } else if (req.auth.actor === ActorType.SERVICE) { req.permission = { type: ActorType.SERVICE, id: req.auth.serviceTokenId }; + } else if (req.auth.actor === ActorType.SCIM_CLIENT) { + req.permission = { type: ActorType.SCIM_CLIENT, id: req.auth.scimTokenId, orgId: req.auth.orgId }; } }); }); diff --git a/backend/src/server/plugins/error-handler.ts b/backend/src/server/plugins/error-handler.ts index 8587c93bd8..ac19120308 100644 --- a/backend/src/server/plugins/error-handler.ts +++ b/backend/src/server/plugins/error-handler.ts @@ -2,7 +2,7 @@ import { ForbiddenError } from "@casl/ability"; import fastifyPlugin from "fastify-plugin"; import { ZodError } from "zod"; -import { BadRequestError, DatabaseError, InternalServerError, UnauthorizedError } from "@app/lib/errors"; +import { BadRequestError, DatabaseError, InternalServerError, UnauthorizedError, ScimRequestError } from "@app/lib/errors"; export const fastifyErrHandler = fastifyPlugin(async (server: FastifyZodProvider) => { server.setErrorHandler((error, req, res) => { @@ -21,6 +21,12 @@ export const fastifyErrHandler = fastifyPlugin(async (server: FastifyZodProvider error: "PermissionDenied", message: `You are not allowed to ${error.action} on ${error.subjectType}` }); + } else if (error instanceof ScimRequestError) { + void res.status(error.status).send({ + schemas: error.schemas, + status: error.status, + detail: error.detail + }); } else { void res.send(error); } diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 28f65ea998..b077992170 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -32,6 +32,8 @@ import { snapshotFolderDALFactory } from "@app/ee/services/secret-snapshot/snaps import { snapshotSecretDALFactory } from "@app/ee/services/secret-snapshot/snapshot-secret-dal"; import { trustedIpDALFactory } from "@app/ee/services/trusted-ip/trusted-ip-dal"; import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service"; +import { scimDALFactory } from "@app/ee/services/scim/scim-dal"; +import { scimServiceFactory } from "@app/ee/services/scim/scim-service"; import { getConfig } from "@app/lib/config/env"; import { TQueueServiceFactory } from "@app/queue"; import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal"; @@ -155,6 +157,7 @@ export const registerRoutes = async ( const auditLogDAL = auditLogDALFactory(db); const trustedIpDAL = trustedIpDALFactory(db); + const scimDAL = scimDALFactory(db); // ee db layer ops const permissionDAL = permissionDALFactory(db); @@ -188,6 +191,13 @@ export const registerRoutes = async ( trustedIpDAL, permissionService }); + const scimService = scimServiceFactory({ + licenseService, + scimDAL, + userDAL, + orgDAL, + permissionService + }); const auditLogQueue = auditLogQueueServiceFactory({ auditLogDAL, queueService, @@ -486,6 +496,7 @@ export const registerRoutes = async ( secretScanning: secretScanningService, license: licenseService, trustedIp: trustedIpService, + scim: scimService, secretBlindIndex: secretBlindIndexService, telemetry: telemetryService }); diff --git a/backend/src/services/auth/auth-type.ts b/backend/src/services/auth/auth-type.ts index 321140ff7b..b051952e7b 100644 --- a/backend/src/services/auth/auth-type.ts +++ b/backend/src/services/auth/auth-type.ts @@ -34,7 +34,7 @@ export enum ActorType { // would extend to AWS, Azure, ... SERVICE = "service", IDENTITY = "identity", Machine = "machine", - SCIM_IDP = "scimIdp" + SCIM_CLIENT = "scimClient" } export type AuthModeJwtTokenPayload = { diff --git a/frontend/src/hooks/api/scim/index.tsx b/frontend/src/hooks/api/scim/index.tsx index 13a190c1fa..3895181739 100644 --- a/frontend/src/hooks/api/scim/index.tsx +++ b/frontend/src/hooks/api/scim/index.tsx @@ -1,3 +1,5 @@ -export { - useGetScimToken -} from "./queries"; \ No newline at end of file +export { useGetScimTokens } from "./queries"; +export { + useCreateScimToken, + useDeleteScimToken +} from "./mutations"; \ No newline at end of file diff --git a/frontend/src/hooks/api/scim/mutations.tsx b/frontend/src/hooks/api/scim/mutations.tsx new file mode 100644 index 0000000000..d0646b98b8 --- /dev/null +++ b/frontend/src/hooks/api/scim/mutations.tsx @@ -0,0 +1,46 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { apiRequest } from "@app/config/request"; +import { + CreateScimTokenDTO, + CreateScimTokenRes, + DeleteScimTokenDTO +} from "./types"; +import { scimKeys } from "./queries"; + +export const useCreateScimToken = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ + organizationId, + description, + ttl + }) => { + const { data } = await apiRequest.post("/api/v1/scim/scim-tokens", { + organizationId, + description, + ttl + }); + + return data; + }, + onSuccess: (_, { organizationId }) => { + queryClient.invalidateQueries(scimKeys.getScimTokens(organizationId)); + } + }); +}; + +export const useDeleteScimToken = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ + organizationId, + scimTokenId + }) => { + const { data } = await apiRequest.delete(`/api/v1/scim/scim-tokens/${scimTokenId}`); + return data; + }, + onSuccess: (_, { organizationId }) => { + queryClient.invalidateQueries(scimKeys.getScimTokens(organizationId)); + } + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/api/scim/queries.tsx b/frontend/src/hooks/api/scim/queries.tsx index b8a3291b1f..4c88ebc2f9 100644 --- a/frontend/src/hooks/api/scim/queries.tsx +++ b/frontend/src/hooks/api/scim/queries.tsx @@ -1,23 +1,22 @@ -import { useQuery } from "@tanstack/react-query"; - +import { useQuery, useQueryClient } from "@tanstack/react-query"; import { apiRequest } from "@app/config/request"; +import { ScimTokenData } from "./types"; -import { GetScimTokenRes } from "./types"; - -const scimKeys = { - getScimToken: (orgId: string) => [{ orgId }, "organization-scim-token"] as const, +export const scimKeys = { + getScimTokens: (orgId: string) => [{ orgId }, "organization-scim-token"] as const, }; -export const useGetScimToken = (organizationId: string) => { +export const useGetScimTokens = (organizationId: string) => { return useQuery({ - queryKey: scimKeys.getScimToken(organizationId), + queryKey: scimKeys.getScimTokens(organizationId), queryFn: async () => { if (organizationId === "") { return undefined; } - const { data: { scimToken } } = await apiRequest.get(`/api/v1/scim/token/organizations/${organizationId}`); - return scimToken; + const { data: { scimTokens } } = await apiRequest.get<{ scimTokens: ScimTokenData[] }>(`/api/v1/scim/scim-tokens?organizationId=${organizationId}`); + + return scimTokens; }, enabled: true }); diff --git a/frontend/src/hooks/api/scim/types.ts b/frontend/src/hooks/api/scim/types.ts index deec75ed0d..814ce60139 100644 --- a/frontend/src/hooks/api/scim/types.ts +++ b/frontend/src/hooks/api/scim/types.ts @@ -1,3 +1,24 @@ -export type GetScimTokenRes = { +export type ScimTokenData = { + id: string; + ttl: number; + description: string; + tokenSuffix: string; + orgId: string; + createdAt: string; + updatedAt: string; +}; + +export type CreateScimTokenDTO = { + organizationId: string; + description?: string; + ttl?: number; +} + +export type DeleteScimTokenDTO = { + organizationId: string; + scimTokenId: string; +} + +export type CreateScimTokenRes = { scimToken: string; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/src/hooks/api/subscriptions/types.ts b/frontend/src/hooks/api/subscriptions/types.ts index b975f04728..5680da02dd 100644 --- a/frontend/src/hooks/api/subscriptions/types.ts +++ b/frontend/src/hooks/api/subscriptions/types.ts @@ -18,6 +18,7 @@ export type SubscriptionPlan = { workspacesUsed: number; environmentLimit: number; samlSSO: boolean; + scim: boolean; status: | "incomplete" | "incomplete_expired" diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx index 66e72d39e8..0c69552d00 100644 --- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx +++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSCIMSection.tsx @@ -7,52 +7,47 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Button, IconButton, - Switch + Switch, + UpgradePlanModal } from "@app/components/v2"; import { // OrgPermissionActions, // OrgPermissionSubjects, useOrganization, -// useSubscription + useSubscription } from "@app/context"; -import { useToggle } from "@app/hooks"; -// import { usePopUp } from "@app/hooks/usePopUp"; -import { useGetScimToken } from "@app/hooks/api"; +import { usePopUp } from "@app/hooks/usePopUp"; +import { ScimTokenModal } from "./ScimTokenModal"; // TODO: add permissioning for enteprise SCIM export const OrgScimSection = () => { const { currentOrg } = useOrganization(); // const { createNotification } = useNotificationContext(); - // const { subscription } = useSubscription(); - // const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ - // "upgradePlan" - // ] as const); - - const { data: scimToken } = useGetScimToken(currentOrg?.id ?? ""); + const { subscription } = useSubscription(); + const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ + "scimToken", + "deleteScimToken", + "upgradePlan" + ] as const); const [scimEnabled, setScimEnabled] = useState(false); // sync this with backend - const [isAPIKeyCopied, setIsAPIKeyCopied] = useToggle(false); - - // TODO: get SCIM stuf const addScimTokenBtnClick = () => { - + if (subscription?.scim) { + handlePopUpOpen("scimToken"); + } else { + handlePopUpOpen("upgradePlan"); + } } const handleSCIMToggle = (value: boolean) => { - // TODO try { setScimEnabled(value); } catch (err) { console.error(err); } } - - const copyTokenToClipboard = () => { - navigator.clipboard.writeText(scimToken ?? ""); - setIsAPIKeyCopied.on(); - }; return (
@@ -64,7 +59,7 @@ export const OrgScimSection = () => { // isDisabled={!isAllowed} leftIcon={} > - Add SCIM Token + Manage SCIM Tokens
{ > Enable SCIM Provisioning - {scimEnabled && ( -
-
-

SCIM URL

-

{`${window.origin}/api/v1/scim`}

-
- {/*

SCIM URL

*/} - {/*
-

{`${window.origin}/api/v1/scim`}

- - - - Click to copy - - -
*/} - {scimToken && ( - <> -

SCIM Bearer Token

-
-

{scimToken}

- - - - Click to copy - - -
- - )} -
- )} + + handlePopUpToggle("upgradePlan", isOpen)} + text="You can use SCIM Provisioning if you switch to Infisical's Pro plan." + />
); } \ No newline at end of file diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/ScimTokenModal.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/ScimTokenModal.tsx index e69de29bb2..329900b145 100644 --- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/ScimTokenModal.tsx +++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/ScimTokenModal.tsx @@ -0,0 +1,353 @@ +import { useEffect, useState } from "react"; +import { Controller, useForm } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import { faCheck, faCopy, faKey, faXmark } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { format } from "date-fns"; +import * as yup from "yup"; + +import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; +import { + Button, + DeleteActionModal, + EmptyState, + FormControl, + IconButton, + Input, + Modal, + ModalContent, + Table, + TableContainer, + TableSkeleton, + TBody, + Td, + Th, + THead, + Tr +} from "@app/components/v2"; +import { useToggle } from "@app/hooks"; +import { + useGetScimTokens, + useCreateScimToken, + useDeleteScimToken +} from "@app/hooks/api"; +import { UsePopUpState } from "@app/hooks/usePopUp"; +import { useOrganization } from "@app/context"; + +// TODO: turn TTL into a select component + +const schema = yup.object({ + description: yup.string(), + ttl: yup.string() +}); + +export type FormData = yup.InferType; + +type Props = { + popUp: UsePopUpState<["scimToken", "deleteScimToken"]>; + handlePopUpOpen: ( + popUpName: keyof UsePopUpState<["deleteScimToken"]>, + data?: { + scimTokenId: string; + } + ) => void; + handlePopUpToggle: ( + popUpName: keyof UsePopUpState< + ["scimToken", "deleteScimToken"] + >, + state?: boolean + ) => void; +}; + +export const ScimTokenModal = ({ + popUp, + handlePopUpOpen, + handlePopUpToggle +}: Props) => { + const { currentOrg } = useOrganization(); + const { t } = useTranslation(); + const { createNotification } = useNotificationContext(); + const [token, setToken] = useState(""); + + const [isScimUrlCopied, setIsScimUrlCopied] = useToggle(false); + const [isScimTokenCopied, setIsScimTokenCopied] = useToggle(false); + + const { data, isLoading } = useGetScimTokens(currentOrg?.id ?? ""); + const { mutateAsync: createScimTokenMutateAsync } = useCreateScimToken(); + const { mutateAsync: deleteScimTokenMutateAsync } = useDeleteScimToken(); + + const { + control, + handleSubmit, + reset, + formState: { isSubmitting } + } = useForm({ + resolver: yupResolver(schema), + defaultValues: { + description: "", + ttl: "" + } + }); + + useEffect(() => { + let timer: NodeJS.Timeout; + if (isScimUrlCopied) { + timer = setTimeout(() => setIsScimUrlCopied.off(), 2000); + } + + if (isScimTokenCopied) { + timer = setTimeout(() => setIsScimTokenCopied.off(), 2000); + } + + return () => clearTimeout(timer); + }, [isScimTokenCopied, isScimUrlCopied]); + + const onFormSubmit = async ({ description, ttl }: FormData) => { + try { + if (!currentOrg?.id) return; + + const { scimToken } = await createScimTokenMutateAsync({ + organizationId: currentOrg.id, + description, + ttl: Number(ttl) + }); + + setToken(scimToken); + + createNotification({ + text: "Successfully created SCIM token", + type: "success" + }); + } catch (err) { + console.error(err); + createNotification({ + text: "Failed to create SCIM token", + type: "error" + }); + } + }; + + const onDeleteScimTokenSubmit = async (scimTokenId: string) => { + try { + if (!currentOrg?.id) return; + + await deleteScimTokenMutateAsync({ + organizationId: currentOrg.id, + scimTokenId + }); + + // TODO: find alt way + + // if (token.startsWith(clientSecretPrefix)) { + // reset(); + // setToken(""); + // } + + handlePopUpToggle("deleteScimToken", false); + + createNotification({ + text: "Successfully deleted SCIM token", + type: "success" + }); + } catch (err) { + console.error(err); + createNotification({ + text: "Failed to delete SCIM token", + type: "error" + }); + } + }; + + const hasToken = Boolean(token); + const scimUrl = `${window.origin}/api/v1/scim`; + + return ( + { + handlePopUpToggle("scimToken", isOpen); + reset(); + setToken(""); + }} + > + +

SCIM URL

+
+

{scimUrl}

+ { + navigator.clipboard.writeText(scimUrl); + setIsScimUrlCopied.on(); + }} + > + + + {t("common.click-to-copy")} + + +
+

New SCIM Token

+ {hasToken ? ( +
+
+

We will only show this token once

+ +
+
+

{token}

+ { + navigator.clipboard.writeText(token); + setIsScimTokenCopied.on(); + }} + > + + + {t("common.click-to-copy")} + + +
+
+ ) : ( +
+ ( + + + + )} + /> + ( + +
+ + +
+
+ )} + /> + + )} +

SCIM Tokens

+ + + + + + + + + + + {isLoading && } + {!isLoading && + data && + data.length > 0 && + data.map( + ({ + id, + description, + ttl, + createdAt + }) => { + + let expiresAt; + if (ttl > 0) { + expiresAt = new Date(new Date(createdAt).getTime() + ttl * 1000); + } + + return ( + + + + + + + ); + } + )} + {!isLoading && data && data?.length === 0 && ( + + + + )} + +
DescriptionExpires AtCreated At +
{description === "" ? "-" : description}{expiresAt ? format(expiresAt, "yyyy-MM-dd") : "-"}{format(new Date(createdAt), "yyyy-MM-dd HH:mm:ss")} + { + handlePopUpOpen("deleteScimToken", { + scimTokenId: id + }); + }} + size="lg" + colorSchema="primary" + variant="plain" + ariaLabel="update" + > + + +
+ +
+
+ handlePopUpToggle("scimToken", isOpen)} + deleteKey="confirm" + onDeleteApproved={() => { + + const deleteScimTokenData = popUp?.deleteScimToken?.data as { + scimTokenId: string; + }; + + return onDeleteScimTokenSubmit(deleteScimTokenData.scimTokenId); + }} + /> +
+
+ ); +}; From faf6323a58c2fe23dbfe2b382de218ac105646ed Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Wed, 14 Feb 2024 12:09:56 -0800 Subject: [PATCH 017/525] Patch identity ttl conversion --- .../identity-access-token-service.ts | 12 ++++++------ .../src/services/identity-ua/identity-ua-service.ts | 9 ++++++--- 2 files changed, 12 insertions(+), 9 deletions(-) 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 cdc8effe2d..32774ccbb0 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 @@ -35,12 +35,12 @@ export const identityAccessTokenServiceFactory = ({ } // ttl check - if (accessTokenTTL > 0) { + if (Number(accessTokenTTL) > 0) { const currentDate = new Date(); if (accessTokenLastRenewedAt) { // access token has been renewed const accessTokenRenewed = new Date(accessTokenLastRenewedAt); - const ttlInMilliseconds = accessTokenTTL * 1000; + const ttlInMilliseconds = Number(accessTokenTTL) * 1000; const expirationDate = new Date(accessTokenRenewed.getTime() + ttlInMilliseconds); if (currentDate > expirationDate) @@ -50,7 +50,7 @@ export const identityAccessTokenServiceFactory = ({ } else { // access token has never been renewed const accessTokenCreated = new Date(accessTokenCreatedAt); - const ttlInMilliseconds = accessTokenTTL * 1000; + const ttlInMilliseconds = Number(accessTokenTTL) * 1000; const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds); if (currentDate > expirationDate) @@ -61,9 +61,9 @@ export const identityAccessTokenServiceFactory = ({ } // max ttl checks - if (accessTokenMaxTTL > 0) { + if (Number(accessTokenMaxTTL) > 0) { const accessTokenCreated = new Date(accessTokenCreatedAt); - const ttlInMilliseconds = accessTokenMaxTTL * 1000; + const ttlInMilliseconds = Number(accessTokenMaxTTL) * 1000; const currentDate = new Date(); const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds); @@ -72,7 +72,7 @@ export const identityAccessTokenServiceFactory = ({ message: "Failed to renew MI access token due to Max TTL expiration" }); - const extendToDate = new Date(currentDate.getTime() + accessTokenTTL); + const extendToDate = new Date(currentDate.getTime() + Number(accessTokenTTL)); if (extendToDate > expirationDate) throw new UnauthorizedError({ message: "Failed to renew MI access token past its Max TTL expiration" diff --git a/backend/src/services/identity-ua/identity-ua-service.ts b/backend/src/services/identity-ua/identity-ua-service.ts index 60e748ff76..3b73da6d46 100644 --- a/backend/src/services/identity-ua/identity-ua-service.ts +++ b/backend/src/services/identity-ua/identity-ua-service.ts @@ -69,9 +69,9 @@ export const identityUaServiceFactory = ({ if (!validClientSecretInfo) throw new UnauthorizedError(); const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = validClientSecretInfo; - if (clientSecretTTL > 0) { + if (Number(clientSecretTTL) > 0) { const clientSecretCreated = new Date(validClientSecretInfo.createdAt); - const ttlInMilliseconds = clientSecretTTL * 1000; + const ttlInMilliseconds = Number(clientSecretTTL) * 1000; const currentDate = new Date(); const expirationTime = new Date(clientSecretCreated.getTime() + ttlInMilliseconds); @@ -124,7 +124,10 @@ export const identityUaServiceFactory = ({ } as TIdentityAccessTokenJwtPayload, appCfg.AUTH_SECRET, { - expiresIn: identityAccessToken.accessTokenMaxTTL === 0 ? undefined : identityAccessToken.accessTokenMaxTTL + expiresIn: + Number(identityAccessToken.accessTokenMaxTTL) === 0 + ? undefined + : Number(identityAccessToken.accessTokenMaxTTL) } ); From 8a0fd6278573e4e0a48ac96148ce337bff5a76af Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Wed, 14 Feb 2024 15:19:38 -0500 Subject: [PATCH 018/525] update docker compose name --- .github/workflows/check-api-for-breaking-changes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-api-for-breaking-changes.yml b/.github/workflows/check-api-for-breaking-changes.yml index 39c9c6fb7f..683f9fe9bb 100644 --- a/.github/workflows/check-api-for-breaking-changes.yml +++ b/.github/workflows/check-api-for-breaking-changes.yml @@ -28,7 +28,7 @@ jobs: run: docker build --tag infisical-api . working-directory: backend - name: Start postgres and redis - run: touch .env && docker-compose -f docker-compose.pg.yml up -d db redis + run: touch .env && docker-compose -f docker-compose.prod.yml up -d db redis - name: Start the server run: | echo "SECRET_SCANNING_GIT_APP_ID=793712" >> .env From 217fef65e8a70b2fac64c53b073a492d2e875df1 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Wed, 14 Feb 2024 15:37:21 -0500 Subject: [PATCH 019/525] update breaking change workflow to dev.yml --- .github/workflows/check-api-for-breaking-changes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-api-for-breaking-changes.yml b/.github/workflows/check-api-for-breaking-changes.yml index 683f9fe9bb..e0fe6e796a 100644 --- a/.github/workflows/check-api-for-breaking-changes.yml +++ b/.github/workflows/check-api-for-breaking-changes.yml @@ -28,7 +28,7 @@ jobs: run: docker build --tag infisical-api . working-directory: backend - name: Start postgres and redis - run: touch .env && docker-compose -f docker-compose.prod.yml up -d db redis + run: touch .env && docker-compose -f docker-compose.dev.yml up -d db redis - name: Start the server run: | echo "SECRET_SCANNING_GIT_APP_ID=793712" >> .env From 1855fc769d28dd4d0da5d5601b89273d2a7a231d Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Wed, 14 Feb 2024 15:42:45 -0500 Subject: [PATCH 020/525] Update check-api-for-breaking-changes.yml --- .github/workflows/check-api-for-breaking-changes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-api-for-breaking-changes.yml b/.github/workflows/check-api-for-breaking-changes.yml index e0fe6e796a..29275abd1a 100644 --- a/.github/workflows/check-api-for-breaking-changes.yml +++ b/.github/workflows/check-api-for-breaking-changes.yml @@ -70,6 +70,6 @@ jobs: run: oasdiff breaking https://app.infisical.com/api/docs/json http://localhost:4000/api/docs/json --fail-on ERR - name: cleanup run: | - docker-compose -f "docker-compose.pg.yml" down + docker-compose -f "docker-compose.dev.yml" down docker stop infisical-api docker remove infisical-api From 242d770098c1aec146d08b7f2db01391403ac70b Mon Sep 17 00:00:00 2001 From: Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> Date: Thu, 15 Feb 2024 01:12:48 +0100 Subject: [PATCH 021/525] Improved UX when trying to find reminder value --- .../components/SecretListView/SecretItem.tsx | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/frontend/src/views/SecretMainPage/components/SecretListView/SecretItem.tsx b/frontend/src/views/SecretMainPage/components/SecretListView/SecretItem.tsx index ecd35abf82..b175a0bca8 100644 --- a/frontend/src/views/SecretMainPage/components/SecretListView/SecretItem.tsx +++ b/frontend/src/views/SecretMainPage/components/SecretListView/SecretItem.tsx @@ -115,6 +115,9 @@ export const SecretItem = memo( const hasComment = Boolean(watch("comment")); const hasReminder = Boolean(watch("reminderRepeatDays")); + const secretReminderRepeatDays = watch("reminderRepeatDays"); + const secretReminderNote = watch("reminderNote"); + const selectedTags = watch("tags", []); const selectedTagsGroupById = selectedTags.reduce>( (prev, curr) => ({ ...prev, [curr.id]: true }), @@ -191,6 +194,8 @@ export const SecretItem = memo( return ( <> { setCreateReminderFormOpen.toggle(); @@ -386,16 +391,18 @@ export const SecretItem = memo( size="md" ariaLabel="add-reminder" > - + 1 ? "s" : "" + } + ` + : "Reminder" + } + > { - if (!hasReminder) { - setCreateReminderFormOpen.on(); - } else { - setValue("reminderRepeatDays", null, { shouldDirty: true }); - setValue("reminderNote", null, { shouldDirty: true }); - } - }} + onClick={() => setCreateReminderFormOpen.on()} icon={faClock} /> From c2bd259c1209c51e37328266ace54bcc92a592f7 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard <62331820+DanielHougaard@users.noreply.github.com> Date: Thu, 15 Feb 2024 01:14:05 +0100 Subject: [PATCH 022/525] Added secret reminders to sidebar (and formatting) --- .../SecretListView/SecretDetaiSidebar.tsx | 536 ++++++++++-------- 1 file changed, 297 insertions(+), 239 deletions(-) diff --git a/frontend/src/views/SecretMainPage/components/SecretListView/SecretDetaiSidebar.tsx b/frontend/src/views/SecretMainPage/components/SecretListView/SecretDetaiSidebar.tsx index 1130c9d148..cf50e44f3b 100644 --- a/frontend/src/views/SecretMainPage/components/SecretListView/SecretDetaiSidebar.tsx +++ b/frontend/src/views/SecretMainPage/components/SecretListView/SecretDetaiSidebar.tsx @@ -5,6 +5,7 @@ import { faCheckCircle, faCircle, faCircleDot, + faClock, faPlus, faTag } from "@fortawesome/free-solid-svg-icons"; @@ -33,9 +34,11 @@ import { Tooltip } from "@app/components/v2"; import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context"; +import { useToggle } from "@app/hooks"; import { useGetSecretVersion } from "@app/hooks/api"; import { DecryptedSecret, UserWsKeyPair, WsTag } from "@app/hooks/api/types"; +import { CreateReminderForm } from "./CreateReminderForm"; import { formSchema, SecretActionType, TFormSchema } from "./SecretListView.utils"; type Props = { @@ -147,262 +150,317 @@ export const SecretDetailSidebar = ({ await onSaveSecret(secret, { ...secret, ...data }, () => reset()); }; + const [createReminderFormOpen, setCreateReminderFormOpen] = useToggle(false); + + const secretReminderRepeatDays = watch("reminderRepeatDays"); + const secretReminderNote = watch("reminderNote"); + return ( - { - if (isOpen && isDirty) { - if ( - // eslint-disable-next-line no-alert - window.confirm("You have edited the secret. Are you sure you want to reset the change?") - ) { - onToggle(false); - reset(); - } else return; - } - onToggle(state); - }} - isOpen={isOpen} - > - -
-
- - - - - {(isAllowed) => ( - ( - - - - )} - /> - )} - -
+ <> + { + setCreateReminderFormOpen.toggle(); + + if (data) { + setValue("reminderRepeatDays", data.days, { shouldDirty: true }); + setValue("reminderNote", data.note, { shouldDirty: true }); + } + }} + /> + { + if (isOpen && isDirty) { + if ( + // eslint-disable-next-line no-alert + window.confirm( + "You have edited the secret. Are you sure you want to reset the change?" + ) + ) { + onToggle(false); + reset(); + } else return; + } + onToggle(state); + }} + isOpen={isOpen} + > + + +
+ + + {(isAllowed) => ( - - Override with a personal value - + ( + + + + )} + /> )} -
- {isOverridden && ( - ( - - - - )} - /> - )} - -
- {fields.map(({ tagColor, id: formId, name, id }) => ( - { - if (cannotEditSecret) { - createNotification({ type: "error", text: "Access denied" }); - return; - } - const tag = tags?.find(({ id: tagId }) => id === tagId); - if (tag) handleTagSelect(tag); - }} - > -
-
{name}
- - ))} - - - {(isAllowed) => ( - - - - - - )} - - - Apply tags to this secrets - {tags.map((tag) => { - const { id: tagId, name, color } = tag; - - const isSelected = selectedTagsGroupById?.[tagId]; - return ( - handleTagSelect(tag)} - key={tagId} - icon={isSelected && } - iconPos="right" - > -
-
- {name} -
- - ); - })} - - {(isAllowed) => ( - - - - )} - - - -
- - -