mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge branch 'main' into feat/orgUserAuthTokenExpiration
This commit is contained in:
474
backend/package-lock.json
generated
474
backend/package-lock.json
generated
@@ -33,6 +33,7 @@
|
||||
"@infisical/quic": "^1.0.8",
|
||||
"@node-saml/passport-saml": "^5.0.1",
|
||||
"@octokit/auth-app": "^7.1.1",
|
||||
"@octokit/plugin-paginate-graphql": "^5.2.4",
|
||||
"@octokit/plugin-retry": "^5.0.5",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@octokit/webhooks-types": "^7.3.1",
|
||||
@@ -91,10 +92,10 @@
|
||||
"ora": "^7.0.1",
|
||||
"oracledb": "^6.4.0",
|
||||
"otplib": "^12.0.1",
|
||||
"passport-github": "^1.1.0",
|
||||
"passport-gitlab2": "^5.0.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-ldapauth": "^3.0.1",
|
||||
"passport-oauth2": "^1.8.0",
|
||||
"pg": "^8.11.3",
|
||||
"pg-boss": "^10.1.5",
|
||||
"pg-query-stream": "^4.5.3",
|
||||
@@ -135,7 +136,6 @@
|
||||
"@types/lodash.isequal": "^4.5.8",
|
||||
"@types/node": "^20.17.30",
|
||||
"@types/nodemailer": "^6.4.14",
|
||||
"@types/passport-github": "^1.1.12",
|
||||
"@types/passport-google-oauth20": "^2.0.14",
|
||||
"@types/pg": "^8.10.9",
|
||||
"@types/picomatch": "^2.3.3",
|
||||
@@ -7245,47 +7245,247 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.2.tgz",
|
||||
"integrity": "sha512-cZUy1gUvd4vttMic7C0lwPed8IYXWYp8kHIMatyhY8t8n3Cpw2ILczkV5pGMPqef7v0bLo0pOHrEHarsau2Ydg==",
|
||||
"version": "6.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz",
|
||||
"integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^4.0.0",
|
||||
"@octokit/graphql": "^7.0.0",
|
||||
"@octokit/request": "^8.0.2",
|
||||
"@octokit/request-error": "^5.0.0",
|
||||
"@octokit/types": "^12.0.0",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"@octokit/auth-token": "^5.0.0",
|
||||
"@octokit/graphql": "^8.2.2",
|
||||
"@octokit/request": "^9.2.3",
|
||||
"@octokit/request-error": "^6.1.8",
|
||||
"@octokit/types": "^14.0.0",
|
||||
"before-after-hook": "^3.0.2",
|
||||
"universal-user-agent": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/@octokit/auth-token": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz",
|
||||
"integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/@octokit/endpoint": {
|
||||
"version": "10.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz",
|
||||
"integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/types": "^14.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/@octokit/openapi-types": {
|
||||
"version": "25.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz",
|
||||
"integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/@octokit/request": {
|
||||
"version": "9.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz",
|
||||
"integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^10.1.4",
|
||||
"@octokit/request-error": "^6.1.8",
|
||||
"@octokit/types": "^14.0.0",
|
||||
"fast-content-type-parse": "^2.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/@octokit/request-error": {
|
||||
"version": "6.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz",
|
||||
"integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/types": "^14.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/@octokit/types": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz",
|
||||
"integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^25.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/fast-content-type-parse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz",
|
||||
"integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/core/node_modules/universal-user-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==",
|
||||
"license": "ISC",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "9.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
|
||||
"integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.1.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "9.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.4.tgz",
|
||||
"integrity": "sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==",
|
||||
"node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": {
|
||||
"version": "24.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
||||
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/endpoint/node_modules/@octokit/types": {
|
||||
"version": "13.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
||||
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^12.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
"@octokit/openapi-types": "^24.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz",
|
||||
"integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==",
|
||||
"version": "8.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz",
|
||||
"integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/request": "^8.0.1",
|
||||
"@octokit/types": "^12.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
"@octokit/request": "^9.2.3",
|
||||
"@octokit/types": "^14.0.0",
|
||||
"universal-user-agent": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/endpoint": {
|
||||
"version": "10.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz",
|
||||
"integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/types": "^14.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": {
|
||||
"version": "25.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz",
|
||||
"integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/request": {
|
||||
"version": "9.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz",
|
||||
"integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^10.1.4",
|
||||
"@octokit/request-error": "^6.1.8",
|
||||
"@octokit/types": "^14.0.0",
|
||||
"fast-content-type-parse": "^2.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/request-error": {
|
||||
"version": "6.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz",
|
||||
"integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/types": "^14.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/@octokit/types": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz",
|
||||
"integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^25.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/fast-content-type-parse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz",
|
||||
"integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/graphql/node_modules/universal-user-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==",
|
||||
"license": "ISC",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@octokit/oauth-authorization-url": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-7.1.1.tgz",
|
||||
@@ -7380,6 +7580,18 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-graphql": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-5.2.4.tgz",
|
||||
"integrity": "sha512-pLZES1jWaOynXKHOqdnwZ5ULeVR6tVVCMm+AUbp0htdcyXDU95WbkYdU4R2ej1wKj5Tu94Mee2Ne0PjPO9cCyA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "9.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.5.tgz",
|
||||
@@ -7461,28 +7673,14 @@
|
||||
"@octokit/openapi-types": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"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/types": "^12.2.0",
|
||||
"bottleneck": "^2.15.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz",
|
||||
"integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==",
|
||||
"version": "8.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
|
||||
"integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^9.0.1",
|
||||
"@octokit/request-error": "^5.1.0",
|
||||
"@octokit/endpoint": "^9.0.6",
|
||||
"@octokit/request-error": "^5.1.1",
|
||||
"@octokit/types": "^13.1.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
@@ -7491,9 +7689,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz",
|
||||
"integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
|
||||
"integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^13.1.0",
|
||||
"deprecation": "^2.0.0",
|
||||
@@ -7543,6 +7742,59 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/core": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz",
|
||||
"integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^4.0.0",
|
||||
"@octokit/graphql": "^7.1.0",
|
||||
"@octokit/request": "^8.4.1",
|
||||
"@octokit/request-error": "^5.1.1",
|
||||
"@octokit/types": "^13.0.0",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/graphql": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
|
||||
"integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^8.4.1",
|
||||
"@octokit/types": "^13.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/openapi-types": {
|
||||
"version": "24.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
||||
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/@octokit/types": {
|
||||
"version": "13.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
||||
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^24.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest/node_modules/before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "12.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.4.0.tgz",
|
||||
@@ -9871,17 +10123,6 @@
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/passport-github": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport-github/-/passport-github-1.1.12.tgz",
|
||||
"integrity": "sha512-VJpMEIH+cOoXB694QgcxuvWy2wPd1Oq3gqrg2Y9DMVBYs9TmH9L14qnqPDZsNMZKBDH+SvqRsGZj9SgHYeDgcA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/express": "*",
|
||||
"@types/passport": "*",
|
||||
"@types/passport-oauth2": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/passport-google-oauth20": {
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport-google-oauth20/-/passport-google-oauth20-2.0.14.tgz",
|
||||
@@ -11654,9 +11895,11 @@
|
||||
"integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz",
|
||||
"integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/big-integer": {
|
||||
"version": "1.6.52",
|
||||
@@ -18142,9 +18385,10 @@
|
||||
"integrity": "sha512-p1TRH/edngVEHVbwqWnxUViEmq5znDvyB+Sik5cmuLpGOIfDf/39zLiq3swPF8Vakqn+gvNiOQAZu8djYlQILA=="
|
||||
},
|
||||
"node_modules/oauth": {
|
||||
"version": "0.9.15",
|
||||
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
||||
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
|
||||
"version": "0.10.2",
|
||||
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.2.tgz",
|
||||
"integrity": "sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
@@ -18827,17 +19071,6 @@
|
||||
"url": "https://github.com/sponsors/jaredhanson"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-github": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-github/-/passport-github-1.1.0.tgz",
|
||||
"integrity": "sha512-XARXJycE6fFh/dxF+Uut8OjlwbFEXgbPVj/+V+K7cvriRK7VcAOm+NgBmbiLM9Qv3SSxEAV+V6fIk89nYHXa8A==",
|
||||
"dependencies": {
|
||||
"passport-oauth2": "1.x.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/passport-gitlab2": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-gitlab2/-/passport-gitlab2-5.0.0.tgz",
|
||||
@@ -18873,12 +19106,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/passport-oauth2": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz",
|
||||
"integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==",
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz",
|
||||
"integrity": "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64url": "3.x.x",
|
||||
"oauth": "0.9.x",
|
||||
"oauth": "0.10.x",
|
||||
"passport-strategy": "1.x.x",
|
||||
"uid2": "0.0.x",
|
||||
"utils-merge": "1.x.x"
|
||||
@@ -19667,6 +19901,62 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/probot/node_modules/@octokit/core": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz",
|
||||
"integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^4.0.0",
|
||||
"@octokit/graphql": "^7.1.0",
|
||||
"@octokit/request": "^8.4.1",
|
||||
"@octokit/request-error": "^5.1.1",
|
||||
"@octokit/types": "^13.0.0",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/probot/node_modules/@octokit/core/node_modules/@octokit/types": {
|
||||
"version": "13.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
||||
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^24.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/probot/node_modules/@octokit/graphql": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
|
||||
"integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^8.4.1",
|
||||
"@octokit/types": "^13.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/probot/node_modules/@octokit/graphql/node_modules/@octokit/types": {
|
||||
"version": "13.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
||||
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^24.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/probot/node_modules/@octokit/openapi-types": {
|
||||
"version": "24.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
||||
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/probot/node_modules/@octokit/plugin-retry": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.1.tgz",
|
||||
@@ -19683,6 +19973,28 @@
|
||||
"@octokit/core": ">=5"
|
||||
}
|
||||
},
|
||||
"node_modules/probot/node_modules/@octokit/plugin-throttling": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz",
|
||||
"integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^12.2.0",
|
||||
"bottleneck": "^2.15.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/probot/node_modules/before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/probot/node_modules/commander": {
|
||||
"version": "12.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||
|
||||
@@ -91,7 +91,6 @@
|
||||
"@types/lodash.isequal": "^4.5.8",
|
||||
"@types/node": "^20.17.30",
|
||||
"@types/nodemailer": "^6.4.14",
|
||||
"@types/passport-github": "^1.1.12",
|
||||
"@types/passport-google-oauth20": "^2.0.14",
|
||||
"@types/pg": "^8.10.9",
|
||||
"@types/picomatch": "^2.3.3",
|
||||
@@ -150,6 +149,7 @@
|
||||
"@infisical/quic": "^1.0.8",
|
||||
"@node-saml/passport-saml": "^5.0.1",
|
||||
"@octokit/auth-app": "^7.1.1",
|
||||
"@octokit/plugin-paginate-graphql": "^5.2.4",
|
||||
"@octokit/plugin-retry": "^5.0.5",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@octokit/webhooks-types": "^7.3.1",
|
||||
@@ -208,10 +208,10 @@
|
||||
"ora": "^7.0.1",
|
||||
"oracledb": "^6.4.0",
|
||||
"otplib": "^12.0.1",
|
||||
"passport-github": "^1.1.0",
|
||||
"passport-gitlab2": "^5.0.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-ldapauth": "^3.0.1",
|
||||
"passport-oauth2": "^1.8.0",
|
||||
"pg": "^8.11.3",
|
||||
"pg-boss": "^10.1.5",
|
||||
"pg-query-stream": "^4.5.3",
|
||||
|
||||
4
backend/src/@types/fastify.d.ts
vendored
4
backend/src/@types/fastify.d.ts
vendored
@@ -15,6 +15,7 @@ import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dy
|
||||
import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
||||
import { TExternalKmsServiceFactory } from "@app/ee/services/external-kms/external-kms-service";
|
||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||
import { TGithubOrgSyncServiceFactory } from "@app/ee/services/github-org-sync/github-org-sync-service";
|
||||
import { TGroupServiceFactory } from "@app/ee/services/group/group-service";
|
||||
import { TIdentityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
|
||||
import { TIdentityProjectAdditionalPrivilegeV2ServiceFactory } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service";
|
||||
@@ -117,6 +118,7 @@ declare module "@fastify/request-context" {
|
||||
declare module "fastify" {
|
||||
interface Session {
|
||||
callbackPort: string;
|
||||
isAdminLogin: boolean;
|
||||
}
|
||||
|
||||
interface FastifyRequest {
|
||||
@@ -140,6 +142,7 @@ declare module "fastify" {
|
||||
passportUser: {
|
||||
isUserCompleted: boolean;
|
||||
providerAuthToken: string;
|
||||
externalProviderAccessToken?: string;
|
||||
};
|
||||
kmipUser: {
|
||||
projectId: string;
|
||||
@@ -244,6 +247,7 @@ declare module "fastify" {
|
||||
gateway: TGatewayServiceFactory;
|
||||
secretRotationV2: TSecretRotationV2ServiceFactory;
|
||||
assumePrivileges: TAssumePrivilegeServiceFactory;
|
||||
githubOrgSync: TGithubOrgSyncServiceFactory;
|
||||
};
|
||||
// this is exclusive use for middlewares in which we need to inject data
|
||||
// everywhere else access using service layer
|
||||
|
||||
8
backend/src/@types/knex.d.ts
vendored
8
backend/src/@types/knex.d.ts
vendored
@@ -83,6 +83,9 @@ import {
|
||||
TGitAppOrg,
|
||||
TGitAppOrgInsert,
|
||||
TGitAppOrgUpdate,
|
||||
TGithubOrgSyncConfigs,
|
||||
TGithubOrgSyncConfigsInsert,
|
||||
TGithubOrgSyncConfigsUpdate,
|
||||
TGroupProjectMembershipRoles,
|
||||
TGroupProjectMembershipRolesInsert,
|
||||
TGroupProjectMembershipRolesUpdate,
|
||||
@@ -1004,5 +1007,10 @@ declare module "knex/types/tables" {
|
||||
TSecretReminderRecipientsInsert,
|
||||
TSecretReminderRecipientsUpdate
|
||||
>;
|
||||
[TableName.GithubOrgSyncConfig]: KnexOriginal.CompositeTableType<
|
||||
TGithubOrgSyncConfigs,
|
||||
TGithubOrgSyncConfigsInsert,
|
||||
TGithubOrgSyncConfigsUpdate
|
||||
>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasTable = await knex.schema.hasTable(TableName.GithubOrgSyncConfig);
|
||||
if (!hasTable) {
|
||||
await knex.schema.createTable(TableName.GithubOrgSyncConfig, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("githubOrgName").notNullable();
|
||||
t.boolean("isActive").defaultTo(false);
|
||||
t.binary("encryptedGithubOrgAccessToken");
|
||||
t.uuid("orgId").notNullable().unique();
|
||||
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
}
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.GithubOrgSyncConfig);
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTableIfExists(TableName.GithubOrgSyncConfig);
|
||||
await dropOnUpdateTrigger(knex, TableName.GithubOrgSyncConfig);
|
||||
}
|
||||
24
backend/src/db/schemas/github-org-sync-configs.ts
Normal file
24
backend/src/db/schemas/github-org-sync-configs.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
// 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 { zodBuffer } from "@app/lib/zod";
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const GithubOrgSyncConfigsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
githubOrgName: z.string(),
|
||||
isActive: z.boolean().default(false).nullable().optional(),
|
||||
encryptedGithubOrgAccessToken: zodBuffer.nullable().optional(),
|
||||
orgId: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
});
|
||||
|
||||
export type TGithubOrgSyncConfigs = z.infer<typeof GithubOrgSyncConfigsSchema>;
|
||||
export type TGithubOrgSyncConfigsInsert = Omit<z.input<typeof GithubOrgSyncConfigsSchema>, TImmutableDBKeys>;
|
||||
export type TGithubOrgSyncConfigsUpdate = Partial<Omit<z.input<typeof GithubOrgSyncConfigsSchema>, TImmutableDBKeys>>;
|
||||
@@ -25,6 +25,7 @@ export * from "./external-kms";
|
||||
export * from "./gateways";
|
||||
export * from "./git-app-install-sessions";
|
||||
export * from "./git-app-org";
|
||||
export * from "./github-org-sync-configs";
|
||||
export * from "./group-project-membership-roles";
|
||||
export * from "./group-project-memberships";
|
||||
export * from "./groups";
|
||||
|
||||
@@ -147,7 +147,8 @@ export enum TableName {
|
||||
KmipClientCertificates = "kmip_client_certificates",
|
||||
SecretRotationV2 = "secret_rotations_v2",
|
||||
SecretRotationV2SecretMapping = "secret_rotation_v2_secret_mappings",
|
||||
SecretReminderRecipients = "secret_reminder_recipients"
|
||||
SecretReminderRecipients = "secret_reminder_recipients",
|
||||
GithubOrgSyncConfig = "github_org_sync_configs"
|
||||
}
|
||||
|
||||
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
|
||||
|
||||
129
backend/src/ee/routes/v1/github-org-sync-router.ts
Normal file
129
backend/src/ee/routes/v1/github-org-sync-router.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { GithubOrgSyncConfigsSchema } from "@app/db/schemas";
|
||||
import { CharacterType, zodValidateCharacters } from "@app/lib/validator/validate-string";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
const SanitizedGithubOrgSyncSchema = GithubOrgSyncConfigsSchema.pick({
|
||||
isActive: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
orgId: true,
|
||||
githubOrgName: true
|
||||
});
|
||||
|
||||
const githubOrgNameValidator = zodValidateCharacters([CharacterType.AlphaNumeric, CharacterType.Hyphen]);
|
||||
export const registerGithubOrgSyncRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
githubOrgName: githubOrgNameValidator(z.string().trim(), "GitHub Org Name"),
|
||||
githubOrgAccessToken: z.string().trim().max(1000).optional(),
|
||||
isActive: z.boolean().default(false)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
githubOrgSyncConfig: SanitizedGithubOrgSyncSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const githubOrgSyncConfig = await server.services.githubOrgSync.createGithubOrgSync({
|
||||
orgPermission: req.permission,
|
||||
githubOrgName: req.body.githubOrgName,
|
||||
githubOrgAccessToken: req.body.githubOrgAccessToken,
|
||||
isActive: req.body.isActive
|
||||
});
|
||||
|
||||
return { githubOrgSyncConfig };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "PATCH",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z
|
||||
.object({
|
||||
githubOrgName: githubOrgNameValidator(z.string().trim(), "GitHub Org Name"),
|
||||
githubOrgAccessToken: z.string().trim().max(1000),
|
||||
isActive: z.boolean()
|
||||
})
|
||||
.partial(),
|
||||
response: {
|
||||
200: z.object({
|
||||
githubOrgSyncConfig: SanitizedGithubOrgSyncSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const githubOrgSyncConfig = await server.services.githubOrgSync.updateGithubOrgSync({
|
||||
orgPermission: req.permission,
|
||||
githubOrgName: req.body.githubOrgName,
|
||||
githubOrgAccessToken: req.body.githubOrgAccessToken,
|
||||
isActive: req.body.isActive
|
||||
});
|
||||
|
||||
return { githubOrgSyncConfig };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "DELETE",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
githubOrgSyncConfig: SanitizedGithubOrgSyncSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const githubOrgSyncConfig = await server.services.githubOrgSync.deleteGithubOrgSync({
|
||||
orgPermission: req.permission
|
||||
});
|
||||
|
||||
return { githubOrgSyncConfig };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
githubOrgSyncConfig: SanitizedGithubOrgSyncSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const githubOrgSyncConfig = await server.services.githubOrgSync.getGithubOrgSync({
|
||||
orgPermission: req.permission
|
||||
});
|
||||
|
||||
return { githubOrgSyncConfig };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -9,6 +9,7 @@ import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router"
|
||||
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
||||
import { registerExternalKmsRouter } from "./external-kms-router";
|
||||
import { registerGatewayRouter } from "./gateway-router";
|
||||
import { registerGithubOrgSyncRouter } from "./github-org-sync-router";
|
||||
import { registerGroupRouter } from "./group-router";
|
||||
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
|
||||
import { registerKmipRouter } from "./kmip-router";
|
||||
@@ -72,6 +73,7 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
||||
);
|
||||
|
||||
await server.register(registerGatewayRouter, { prefix: "/gateways" });
|
||||
await server.register(registerGithubOrgSyncRouter, { prefix: "/github-org-sync-config" });
|
||||
|
||||
await server.register(
|
||||
async (pkiRouter) => {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
export type TGithubOrgSyncDALFactory = ReturnType<typeof githubOrgSyncDALFactory>;
|
||||
|
||||
export const githubOrgSyncDALFactory = (db: TDbClient) => {
|
||||
const orm = ormify(db, TableName.GithubOrgSyncConfig);
|
||||
return orm;
|
||||
};
|
||||
@@ -0,0 +1,354 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { Octokit } from "@octokit/core";
|
||||
import { paginateGraphQL } from "@octokit/plugin-paginate-graphql";
|
||||
import { Octokit as OctokitRest } from "@octokit/rest";
|
||||
|
||||
import { OrgMembershipRole } from "@app/db/schemas";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||
|
||||
import { TGroupDALFactory } from "../group/group-dal";
|
||||
import { TUserGroupMembershipDALFactory } from "../group/user-group-membership-dal";
|
||||
import { TLicenseServiceFactory } from "../license/license-service";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||
import { TGithubOrgSyncDALFactory } from "./github-org-sync-dal";
|
||||
import { TCreateGithubOrgSyncDTO, TDeleteGithubOrgSyncDTO, TUpdateGithubOrgSyncDTO } from "./github-org-sync-types";
|
||||
|
||||
const OctokitWithPlugin = Octokit.plugin(paginateGraphQL);
|
||||
|
||||
type TGithubOrgSyncServiceFactoryDep = {
|
||||
githubOrgSyncDAL: TGithubOrgSyncDALFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
userGroupMembershipDAL: Pick<
|
||||
TUserGroupMembershipDALFactory,
|
||||
"findGroupMembershipsByUserIdInOrg" | "insertMany" | "delete"
|
||||
>;
|
||||
groupDAL: Pick<TGroupDALFactory, "insertMany" | "transaction" | "find">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
};
|
||||
|
||||
export type TGithubOrgSyncServiceFactory = ReturnType<typeof githubOrgSyncServiceFactory>;
|
||||
|
||||
export const githubOrgSyncServiceFactory = ({
|
||||
githubOrgSyncDAL,
|
||||
permissionService,
|
||||
kmsService,
|
||||
userGroupMembershipDAL,
|
||||
groupDAL,
|
||||
licenseService
|
||||
}: TGithubOrgSyncServiceFactoryDep) => {
|
||||
const createGithubOrgSync = async ({
|
||||
githubOrgName,
|
||||
orgPermission,
|
||||
githubOrgAccessToken,
|
||||
isActive
|
||||
}: TCreateGithubOrgSyncDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
orgPermission.type,
|
||||
orgPermission.id,
|
||||
orgPermission.orgId,
|
||||
orgPermission.authMethod,
|
||||
orgPermission.orgId
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.GithubOrgSync);
|
||||
const plan = await licenseService.getPlan(orgPermission.orgId);
|
||||
if (!plan.githubOrgSync) {
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to create github organization team sync due to plan restriction. Upgrade plan to create github organization sync."
|
||||
});
|
||||
}
|
||||
|
||||
const existingConfig = await githubOrgSyncDAL.findOne({ orgId: orgPermission.orgId });
|
||||
if (existingConfig)
|
||||
throw new BadRequestError({
|
||||
message: `Organization ${orgPermission.orgId} already has GitHub Organization sync config.`
|
||||
});
|
||||
|
||||
const octokit = new OctokitRest({
|
||||
auth: githubOrgAccessToken,
|
||||
request: {
|
||||
signal: AbortSignal.timeout(5000)
|
||||
}
|
||||
});
|
||||
const { data } = await octokit.rest.orgs.get({
|
||||
org: githubOrgName
|
||||
});
|
||||
if (data.login.toLowerCase() !== githubOrgName.toLowerCase())
|
||||
throw new BadRequestError({ message: "Invalid GitHub organisation" });
|
||||
|
||||
const { encryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: orgPermission.orgId
|
||||
});
|
||||
|
||||
const config = await githubOrgSyncDAL.create({
|
||||
orgId: orgPermission.orgId,
|
||||
githubOrgName,
|
||||
isActive,
|
||||
encryptedGithubOrgAccessToken: githubOrgAccessToken
|
||||
? encryptor({ plainText: Buffer.from(githubOrgAccessToken) }).cipherTextBlob
|
||||
: null
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
const updateGithubOrgSync = async ({
|
||||
githubOrgName,
|
||||
orgPermission,
|
||||
githubOrgAccessToken,
|
||||
isActive
|
||||
}: TUpdateGithubOrgSyncDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
orgPermission.type,
|
||||
orgPermission.id,
|
||||
orgPermission.orgId,
|
||||
orgPermission.authMethod,
|
||||
orgPermission.orgId
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.GithubOrgSync);
|
||||
const plan = await licenseService.getPlan(orgPermission.orgId);
|
||||
if (!plan.githubOrgSync) {
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to update github organization team sync due to plan restriction. Upgrade plan to update github organization sync."
|
||||
});
|
||||
}
|
||||
|
||||
const existingConfig = await githubOrgSyncDAL.findOne({ orgId: orgPermission.orgId });
|
||||
if (!existingConfig)
|
||||
throw new BadRequestError({
|
||||
message: `Organization ${orgPermission.orgId} GitHub organization sync config missing.`
|
||||
});
|
||||
|
||||
const { encryptor, decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.Organization,
|
||||
orgId: orgPermission.orgId
|
||||
});
|
||||
const newData = {
|
||||
githubOrgName: githubOrgName || existingConfig.githubOrgName,
|
||||
githubOrgAccessToken:
|
||||
githubOrgAccessToken ||
|
||||
(existingConfig.encryptedGithubOrgAccessToken
|
||||
? decryptor({ cipherTextBlob: existingConfig.encryptedGithubOrgAccessToken }).toString()
|
||||
: null)
|
||||
};
|
||||
|
||||
if (githubOrgName || githubOrgAccessToken) {
|
||||
const octokit = new OctokitRest({
|
||||
auth: newData.githubOrgAccessToken,
|
||||
request: {
|
||||
signal: AbortSignal.timeout(5000)
|
||||
}
|
||||
});
|
||||
const { data } = await octokit.rest.orgs.get({
|
||||
org: newData.githubOrgName
|
||||
});
|
||||
|
||||
if (data.login.toLowerCase() !== newData.githubOrgName.toLowerCase())
|
||||
throw new BadRequestError({ message: "Invalid GitHub organisation" });
|
||||
}
|
||||
|
||||
const config = await githubOrgSyncDAL.updateById(existingConfig.id, {
|
||||
orgId: orgPermission.orgId,
|
||||
githubOrgName: newData.githubOrgName,
|
||||
isActive,
|
||||
encryptedGithubOrgAccessToken: newData.githubOrgAccessToken
|
||||
? encryptor({ plainText: Buffer.from(newData.githubOrgAccessToken) }).cipherTextBlob
|
||||
: null
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
const deleteGithubOrgSync = async ({ orgPermission }: TDeleteGithubOrgSyncDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
orgPermission.type,
|
||||
orgPermission.id,
|
||||
orgPermission.orgId,
|
||||
orgPermission.authMethod,
|
||||
orgPermission.orgId
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.GithubOrgSync);
|
||||
|
||||
const plan = await licenseService.getPlan(orgPermission.orgId);
|
||||
if (!plan.githubOrgSync) {
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to delete github organization team sync due to plan restriction. Upgrade plan to delete github organization sync."
|
||||
});
|
||||
}
|
||||
|
||||
const existingConfig = await githubOrgSyncDAL.findOne({ orgId: orgPermission.orgId });
|
||||
if (!existingConfig)
|
||||
throw new BadRequestError({
|
||||
message: `Organization ${orgPermission.orgId} GitHub organization sync config missing.`
|
||||
});
|
||||
|
||||
const config = await githubOrgSyncDAL.deleteById(existingConfig.id);
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
const getGithubOrgSync = async ({ orgPermission }: TDeleteGithubOrgSyncDTO) => {
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
orgPermission.type,
|
||||
orgPermission.id,
|
||||
orgPermission.orgId,
|
||||
orgPermission.authMethod,
|
||||
orgPermission.orgId
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.GithubOrgSync);
|
||||
|
||||
const existingConfig = await githubOrgSyncDAL.findOne({ orgId: orgPermission.orgId });
|
||||
if (!existingConfig)
|
||||
throw new NotFoundError({
|
||||
message: `Organization ${orgPermission.orgId} GitHub organization sync config missing.`
|
||||
});
|
||||
|
||||
return existingConfig;
|
||||
};
|
||||
|
||||
const syncUserGroups = async (orgId: string, userId: string, accessToken: string) => {
|
||||
const config = await githubOrgSyncDAL.findOne({ orgId });
|
||||
if (!config || !config?.isActive) return;
|
||||
|
||||
const infisicalUserGroups = await userGroupMembershipDAL.findGroupMembershipsByUserIdInOrg(userId, orgId);
|
||||
const infisicalUserGroupSet = new Set(infisicalUserGroups.map((el) => el.groupName));
|
||||
|
||||
const octoRest = new OctokitRest({
|
||||
auth: accessToken,
|
||||
request: {
|
||||
signal: AbortSignal.timeout(5000)
|
||||
}
|
||||
});
|
||||
const { data: userOrgMembershipDetails } = await octoRest.rest.orgs
|
||||
.getMembershipForAuthenticatedUser({
|
||||
org: config.githubOrgName
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error(err, "User not part of GitHub synced organization");
|
||||
throw new BadRequestError({ message: "User not part of GitHub synced organization" });
|
||||
});
|
||||
const username = userOrgMembershipDetails?.user?.login;
|
||||
if (!username) throw new BadRequestError({ message: "User not part of GitHub synced organization" });
|
||||
|
||||
const octokit = new OctokitWithPlugin({
|
||||
auth: accessToken,
|
||||
request: {
|
||||
signal: AbortSignal.timeout(5000)
|
||||
}
|
||||
});
|
||||
const data = await octokit.graphql
|
||||
.paginate<{
|
||||
organization: { teams: { totalCount: number; edges: { node: { name: string; description: string } }[] } };
|
||||
}>(
|
||||
`
|
||||
query orgTeams($cursor: String,$org: String!, $username: String!){
|
||||
organization(login: $org) {
|
||||
teams(first: 100, userLogins: [$username], after: $cursor) {
|
||||
totalCount
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
description
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
endCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
org: config.githubOrgName,
|
||||
username
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
if ((err as Error)?.message?.includes("Although you appear to have the correct authorization credential")) {
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Please check your organization have approved Infisical Oauth application. For more info: https://infisical.com/docs/documentation/platform/github-org-sync#troubleshooting"
|
||||
});
|
||||
}
|
||||
throw new BadRequestError({ message: (err as Error)?.message });
|
||||
});
|
||||
|
||||
const {
|
||||
organization: { teams }
|
||||
} = data;
|
||||
const githubUserTeams = teams?.edges?.map((el) => el.node.name.toLowerCase()) || [];
|
||||
const githubUserTeamSet = new Set(githubUserTeams);
|
||||
const githubUserTeamOnInfisical = await groupDAL.find({ orgId, $in: { name: githubUserTeams } });
|
||||
const githubUserTeamOnInfisicalGroupByName = groupBy(githubUserTeamOnInfisical, (i) => i.name);
|
||||
|
||||
const newTeams = githubUserTeams.filter(
|
||||
(el) => !infisicalUserGroupSet.has(el) && !Object.hasOwn(githubUserTeamOnInfisicalGroupByName, el)
|
||||
);
|
||||
const updateTeams = githubUserTeams.filter(
|
||||
(el) => !infisicalUserGroupSet.has(el) && Object.hasOwn(githubUserTeamOnInfisicalGroupByName, el)
|
||||
);
|
||||
const removeFromTeams = infisicalUserGroups.filter((el) => !githubUserTeamSet.has(el.groupName));
|
||||
|
||||
if (newTeams.length || updateTeams.length || removeFromTeams.length) {
|
||||
await groupDAL.transaction(async (tx) => {
|
||||
if (newTeams.length) {
|
||||
const newGroups = await groupDAL.insertMany(
|
||||
newTeams.map((newGroupName) => ({
|
||||
name: newGroupName,
|
||||
role: OrgMembershipRole.Member,
|
||||
slug: newGroupName,
|
||||
orgId
|
||||
})),
|
||||
tx
|
||||
);
|
||||
await userGroupMembershipDAL.insertMany(
|
||||
newGroups.map((el) => ({
|
||||
groupId: el.id,
|
||||
userId
|
||||
})),
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
if (updateTeams.length) {
|
||||
await userGroupMembershipDAL.insertMany(
|
||||
updateTeams.map((el) => ({
|
||||
groupId: githubUserTeamOnInfisicalGroupByName[el][0].id,
|
||||
userId
|
||||
})),
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
if (removeFromTeams.length) {
|
||||
await userGroupMembershipDAL.delete(
|
||||
{ userId, $in: { groupId: removeFromTeams.map((el) => el.groupId) } },
|
||||
tx
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
createGithubOrgSync,
|
||||
updateGithubOrgSync,
|
||||
deleteGithubOrgSync,
|
||||
getGithubOrgSync,
|
||||
syncUserGroups
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
|
||||
export interface TCreateGithubOrgSyncDTO {
|
||||
orgPermission: OrgServiceActor;
|
||||
githubOrgName: string;
|
||||
githubOrgAccessToken?: string;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export interface TUpdateGithubOrgSyncDTO {
|
||||
orgPermission: OrgServiceActor;
|
||||
githubOrgName?: string;
|
||||
githubOrgAccessToken?: string;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export interface TDeleteGithubOrgSyncDTO {
|
||||
orgPermission: OrgServiceActor;
|
||||
}
|
||||
|
||||
export interface TGetGithubOrgSyncDTO {
|
||||
orgPermission: OrgServiceActor;
|
||||
}
|
||||
@@ -22,6 +22,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
||||
pitRecovery: false,
|
||||
ipAllowlisting: false,
|
||||
rbac: false,
|
||||
githubOrgSync: false,
|
||||
customRateLimits: false,
|
||||
customAlerts: false,
|
||||
secretAccessInsights: false,
|
||||
|
||||
@@ -45,6 +45,7 @@ export type TFeatureSet = {
|
||||
auditLogsRetentionDays: 0;
|
||||
auditLogStreams: false;
|
||||
auditLogStreamLimit: 3;
|
||||
githubOrgSync: false;
|
||||
samlSSO: false;
|
||||
hsm: false;
|
||||
oidcSSO: false;
|
||||
|
||||
@@ -685,10 +685,16 @@ export const oidcConfigServiceFactory = ({
|
||||
id_token_signed_response_alg: oidcCfg.jwtSignatureAlgorithm
|
||||
});
|
||||
|
||||
// Check if the OIDC provider supports PKCE
|
||||
const codeChallengeMethods = client.issuer.metadata.code_challenge_methods_supported;
|
||||
const supportsPKCE = Array.isArray(codeChallengeMethods) && codeChallengeMethods.includes("S256");
|
||||
|
||||
const strategy = new OpenIdStrategy(
|
||||
{
|
||||
client,
|
||||
passReqToCallback: true
|
||||
passReqToCallback: true,
|
||||
usePKCE: supportsPKCE,
|
||||
params: supportsPKCE ? { code_challenge_method: "S256" } : undefined
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(_req: any, tokenSet: TokenSet, cb: any) => {
|
||||
|
||||
@@ -8,7 +8,8 @@ export enum OIDCConfigurationType {
|
||||
export enum OIDCJWTSignatureAlgorithm {
|
||||
RS256 = "RS256",
|
||||
HS256 = "HS256",
|
||||
RS512 = "RS512"
|
||||
RS512 = "RS512",
|
||||
EDDSA = "EdDSA"
|
||||
}
|
||||
|
||||
export type TOidcLoginDTO = {
|
||||
|
||||
@@ -74,6 +74,7 @@ export enum OrgPermissionSubjects {
|
||||
IncidentAccount = "incident-contact",
|
||||
Sso = "sso",
|
||||
Scim = "scim",
|
||||
GithubOrgSync = "github-org-sync",
|
||||
Ldap = "ldap",
|
||||
Groups = "groups",
|
||||
Billing = "billing",
|
||||
@@ -101,6 +102,7 @@ export type OrgPermissionSet =
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.IncidentAccount]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Sso]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Scim]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.GithubOrgSync]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Ldap]
|
||||
| [OrgPermissionGroupActions, OrgPermissionSubjects.Groups]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.SecretScanning]
|
||||
@@ -165,6 +167,10 @@ export const OrgPermissionSchema = z.discriminatedUnion("subject", [
|
||||
subject: z.literal(OrgPermissionSubjects.Scim).describe("The entity this permission pertains to."),
|
||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||
}),
|
||||
z.object({
|
||||
subject: z.literal(OrgPermissionSubjects.GithubOrgSync).describe("The entity this permission pertains to."),
|
||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||
}),
|
||||
z.object({
|
||||
subject: z.literal(OrgPermissionSubjects.Ldap).describe("The entity this permission pertains to."),
|
||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||
@@ -273,6 +279,11 @@ const buildAdminPermission = () => {
|
||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Scim);
|
||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Scim);
|
||||
|
||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.GithubOrgSync);
|
||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.GithubOrgSync);
|
||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.GithubOrgSync);
|
||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.GithubOrgSync);
|
||||
|
||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Ldap);
|
||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Ldap);
|
||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Ldap);
|
||||
|
||||
1
backend/src/lib/config/const.ts
Normal file
1
backend/src/lib/config/const.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const INFISICAL_PROVIDER_GITHUB_ACCESS_TOKEN = "x-infisical-github-auth-access-token";
|
||||
@@ -2,7 +2,7 @@ export const daysToMillisecond = (days: number) => days * 24 * 60 * 60 * 1000;
|
||||
|
||||
export const secondsToMillis = (seconds: number) => seconds * 1000;
|
||||
|
||||
export const applyJitter = (delayMs: number, jitterMs: number) => {
|
||||
const jitter = Math.floor(Math.random() * (2 * jitterMs)) - jitterMs;
|
||||
return delayMs + jitter;
|
||||
export const applyJitter = (delay: number, jitter: number) => {
|
||||
const jitterTime = Math.floor(Math.random() * (2 * jitter)) - jitter;
|
||||
return delay + jitterTime;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import { Knex } from "knex";
|
||||
import { Tables } from "knex/types/tables";
|
||||
|
||||
import { TableName } from "@app/db/schemas";
|
||||
|
||||
import { DatabaseError } from "../errors";
|
||||
import { buildDynamicKnexQuery, TKnexDynamicOperator } from "./dynamic";
|
||||
|
||||
@@ -25,28 +27,41 @@ export type TFindFilter<R extends object = object> = Partial<R> & {
|
||||
$search?: Partial<{ [k in keyof R]: R[k] }>;
|
||||
$complex?: TKnexDynamicOperator<R>;
|
||||
};
|
||||
|
||||
export const buildFindFilter =
|
||||
<R extends object = object>({ $in, $notNull, $search, $complex, ...filter }: TFindFilter<R>) =>
|
||||
<R extends object = object>(
|
||||
{ $in, $notNull, $search, $complex, ...filter }: TFindFilter<R>,
|
||||
tableName?: TableName,
|
||||
excludeKeys?: Array<keyof R>
|
||||
) =>
|
||||
(bd: Knex.QueryBuilder<R, R>) => {
|
||||
void bd.where(filter);
|
||||
const processedFilter = tableName
|
||||
? Object.fromEntries(
|
||||
Object.entries(filter)
|
||||
.filter(([key]) => !excludeKeys || !excludeKeys.includes(key as keyof R))
|
||||
.map(([key, value]) => [`${tableName}.${key}`, value])
|
||||
)
|
||||
: filter;
|
||||
|
||||
void bd.where(processedFilter);
|
||||
if ($in) {
|
||||
Object.entries($in).forEach(([key, val]) => {
|
||||
if (val) {
|
||||
void bd.whereIn(key as never, val as never);
|
||||
void bd.whereIn([`${tableName ? `${tableName}.` : ""}${key}`] as never, val as never);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ($notNull?.length) {
|
||||
$notNull.forEach((key) => {
|
||||
void bd.whereNotNull(key as never);
|
||||
void bd.whereNotNull([`${tableName ? `${tableName}.` : ""}${key as string}`] as never);
|
||||
});
|
||||
}
|
||||
|
||||
if ($search) {
|
||||
Object.entries($search).forEach(([key, val]) => {
|
||||
if (val) {
|
||||
void bd.whereILike(key as never, val as never);
|
||||
void bd.whereILike([`${tableName ? `${tableName}.` : ""}${key}`] as never, val as never);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,3 +16,17 @@ export const fetchGithubEmails = async (accessToken: string) => {
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
type TGithubUser = {
|
||||
name?: string;
|
||||
login: string;
|
||||
};
|
||||
|
||||
export const fetchGithubUser = async (accessToken: string) => {
|
||||
const { data } = await request.get<TGithubUser>(`${INTEGRATION_GITHUB_API_URL}/user`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`
|
||||
}
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -15,13 +15,13 @@ export const blockLocalAndPrivateIpAddresses = async (url: string) => {
|
||||
|
||||
const validUrl = new URL(url);
|
||||
const inputHostIps: string[] = [];
|
||||
if (isIPv4(validUrl.host)) {
|
||||
inputHostIps.push(validUrl.host);
|
||||
if (isIPv4(validUrl.hostname)) {
|
||||
inputHostIps.push(validUrl.hostname);
|
||||
} else {
|
||||
if (validUrl.host === "localhost" || validUrl.host === "host.docker.internal") {
|
||||
if (validUrl.hostname === "localhost" || validUrl.hostname === "host.docker.internal") {
|
||||
throw new BadRequestError({ message: "Local IPs not allowed as URL" });
|
||||
}
|
||||
const resolvedIps = await dns.resolve4(validUrl.host);
|
||||
const resolvedIps = await dns.resolve4(validUrl.hostname);
|
||||
inputHostIps.push(...resolvedIps);
|
||||
}
|
||||
const isInternalIp = inputHostIps.some((el) => isPrivateIp(el));
|
||||
|
||||
@@ -33,6 +33,8 @@ import { gatewayDALFactory } from "@app/ee/services/gateway/gateway-dal";
|
||||
import { gatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||
import { orgGatewayConfigDALFactory } from "@app/ee/services/gateway/org-gateway-config-dal";
|
||||
import { projectGatewayDALFactory } from "@app/ee/services/gateway/project-gateway-dal";
|
||||
import { githubOrgSyncDALFactory } from "@app/ee/services/github-org-sync/github-org-sync-dal";
|
||||
import { githubOrgSyncServiceFactory } from "@app/ee/services/github-org-sync/github-org-sync-service";
|
||||
import { groupDALFactory } from "@app/ee/services/group/group-dal";
|
||||
import { groupServiceFactory } from "@app/ee/services/group/group-service";
|
||||
import { userGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||
@@ -421,6 +423,7 @@ export const registerRoutes = async (
|
||||
const gatewayDAL = gatewayDALFactory(db);
|
||||
const projectGatewayDAL = projectGatewayDALFactory(db);
|
||||
const secretReminderRecipientsDAL = secretReminderRecipientsDALFactory(db);
|
||||
const githubOrgSyncDAL = githubOrgSyncDALFactory(db);
|
||||
|
||||
const secretRotationV2DAL = secretRotationV2DALFactory(db, folderDAL);
|
||||
|
||||
@@ -558,6 +561,15 @@ export const registerRoutes = async (
|
||||
externalGroupOrgRoleMappingDAL
|
||||
});
|
||||
|
||||
const githubOrgSyncConfigService = githubOrgSyncServiceFactory({
|
||||
licenseService,
|
||||
githubOrgSyncDAL,
|
||||
kmsService,
|
||||
permissionService,
|
||||
groupDAL,
|
||||
userGroupMembershipDAL
|
||||
});
|
||||
|
||||
const ldapService = ldapConfigServiceFactory({
|
||||
ldapConfigDAL,
|
||||
ldapGroupMapDAL,
|
||||
@@ -1529,6 +1541,7 @@ export const registerRoutes = async (
|
||||
|
||||
const secretSyncService = secretSyncServiceFactory({
|
||||
secretSyncDAL,
|
||||
secretImportDAL,
|
||||
permissionService,
|
||||
appConnectionService,
|
||||
folderDAL,
|
||||
@@ -1689,7 +1702,8 @@ export const registerRoutes = async (
|
||||
kmipOperation: kmipOperationService,
|
||||
gateway: gatewayService,
|
||||
secretRotationV2: secretRotationV2Service,
|
||||
assumePrivileges: assumePrivilegeService
|
||||
assumePrivileges: assumePrivilegeService,
|
||||
githubOrgSync: githubOrgSyncConfigService
|
||||
});
|
||||
|
||||
const cronJobs: CronJob[] = [];
|
||||
@@ -1750,30 +1764,6 @@ export const registerRoutes = async (
|
||||
|
||||
logger.info(`Raw event loop stats: ${JSON.stringify(histogram, null, 2)}`);
|
||||
|
||||
// try {
|
||||
// await db.raw("SELECT NOW()");
|
||||
// } catch (err) {
|
||||
// logger.error("Health check: database connection failed", err);
|
||||
// return reply.code(503).send({
|
||||
// date: new Date(),
|
||||
// message: "Service unavailable"
|
||||
// });
|
||||
// }
|
||||
|
||||
// if (cfg.isRedisConfigured) {
|
||||
// const redis = new Redis(cfg.REDIS_URL);
|
||||
// try {
|
||||
// await redis.ping();
|
||||
// redis.disconnect();
|
||||
// } catch (err) {
|
||||
// logger.error("Health check: redis connection failed", err);
|
||||
// return reply.code(503).send({
|
||||
// date: new Date(),
|
||||
// message: "Service unavailable"
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
return {
|
||||
date: new Date(),
|
||||
message: "Ok",
|
||||
|
||||
@@ -154,7 +154,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
secrets: z
|
||||
.object({
|
||||
secretId: z.string(),
|
||||
referencedSecretKey: z.string()
|
||||
referencedSecretKey: z.string(),
|
||||
referencedSecretEnv: z.string()
|
||||
})
|
||||
.array()
|
||||
.optional()
|
||||
@@ -166,6 +167,16 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
.array()
|
||||
.optional(),
|
||||
usedBySecretSyncs: z
|
||||
.object({
|
||||
name: z.string(),
|
||||
destination: z.string(),
|
||||
environment: z.string(),
|
||||
id: z.string(),
|
||||
path: z.string()
|
||||
})
|
||||
.array()
|
||||
.optional(),
|
||||
totalFolderCount: z.number().optional(),
|
||||
totalDynamicSecretCount: z.number().optional(),
|
||||
totalSecretCount: z.number().optional(),
|
||||
@@ -500,6 +511,24 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
}
|
||||
|
||||
const usedBySecretSyncs: { name: string; destination: string; environment: string; id: string; path: string }[] =
|
||||
[];
|
||||
for await (const environment of environments) {
|
||||
const secretSyncs = await server.services.secretSync.listSecretSyncsBySecretPath(
|
||||
{ projectId, secretPath, environment },
|
||||
req.permission
|
||||
);
|
||||
secretSyncs.forEach((sync) => {
|
||||
usedBySecretSyncs.push({
|
||||
name: sync.name,
|
||||
destination: sync.destination,
|
||||
environment,
|
||||
id: sync.id,
|
||||
path: sync.folder?.path || "/"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
folders,
|
||||
dynamicSecrets,
|
||||
@@ -512,6 +541,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
totalSecretCount,
|
||||
totalSecretRotationCount,
|
||||
importedByEnvs,
|
||||
usedBySecretSyncs,
|
||||
totalCount:
|
||||
(totalFolderCount ?? 0) +
|
||||
(totalDynamicSecretCount ?? 0) +
|
||||
@@ -611,6 +641,16 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
totalFolderCount: z.number().optional(),
|
||||
totalDynamicSecretCount: z.number().optional(),
|
||||
totalSecretCount: z.number().optional(),
|
||||
usedBySecretSyncs: z
|
||||
.object({
|
||||
name: z.string(),
|
||||
destination: z.string(),
|
||||
environment: z.string(),
|
||||
id: z.string(),
|
||||
path: z.string()
|
||||
})
|
||||
.array()
|
||||
.optional(),
|
||||
importedBy: z
|
||||
.object({
|
||||
environment: z.object({
|
||||
@@ -624,7 +664,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
secrets: z
|
||||
.object({
|
||||
secretId: z.string(),
|
||||
referencedSecretKey: z.string()
|
||||
referencedSecretKey: z.string(),
|
||||
referencedSecretEnv: z.string()
|
||||
})
|
||||
.array()
|
||||
.optional()
|
||||
@@ -904,6 +945,18 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
secrets
|
||||
});
|
||||
|
||||
const secretSyncs = await server.services.secretSync.listSecretSyncsBySecretPath(
|
||||
{ projectId, secretPath, environment },
|
||||
req.permission
|
||||
);
|
||||
const usedBySecretSyncs = secretSyncs.map((sync) => ({
|
||||
name: sync.name,
|
||||
destination: sync.destination,
|
||||
environment: sync.environment?.name || environment,
|
||||
id: sync.id,
|
||||
path: sync.folder?.path || "/"
|
||||
}));
|
||||
|
||||
if (secrets?.length || secretRotations?.length) {
|
||||
const secretCount =
|
||||
(secrets?.length ?? 0) +
|
||||
@@ -950,6 +1003,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
totalSecretCount,
|
||||
totalSecretRotationCount,
|
||||
importedBy,
|
||||
usedBySecretSyncs,
|
||||
totalCount:
|
||||
(totalImportCount ?? 0) +
|
||||
(totalFolderCount ?? 0) +
|
||||
|
||||
@@ -9,15 +9,17 @@
|
||||
import { Authenticator } from "@fastify/passport";
|
||||
import fastifySession from "@fastify/session";
|
||||
import RedisStore from "connect-redis";
|
||||
import { Strategy as GitHubStrategy } from "passport-github";
|
||||
import { Strategy as GitLabStrategy } from "passport-gitlab2";
|
||||
import { Strategy as GoogleStrategy } from "passport-google-oauth20";
|
||||
import { Strategy as OAuth2Strategy } from "passport-oauth2";
|
||||
import { z } from "zod";
|
||||
|
||||
import { INFISICAL_PROVIDER_GITHUB_ACCESS_TOKEN } from "@app/lib/config/const";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { fetchGithubEmails } from "@app/lib/requests/github";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { fetchGithubEmails, fetchGithubUser } from "@app/lib/requests/github";
|
||||
import { authRateLimit } from "@app/server/config/rateLimiter";
|
||||
import { AuthMethod } from "@app/services/auth/auth-type";
|
||||
import { OrgAuthMethod } from "@app/services/org/org-types";
|
||||
@@ -42,6 +44,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
await server.register(passport.initialize());
|
||||
await server.register(passport.secureSession());
|
||||
|
||||
// passport oauth strategy for Google
|
||||
const isGoogleOauthActive = Boolean(appCfg.CLIENT_ID_GOOGLE_LOGIN && appCfg.CLIENT_SECRET_GOOGLE_LOGIN);
|
||||
if (isGoogleOauthActive) {
|
||||
@@ -52,8 +55,9 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
|
||||
clientID: appCfg.CLIENT_ID_GOOGLE_LOGIN as string,
|
||||
clientSecret: appCfg.CLIENT_SECRET_GOOGLE_LOGIN as string,
|
||||
callbackURL: `${appCfg.SITE_URL}/api/v1/sso/google`,
|
||||
scope: ["profile", " email"],
|
||||
state: true
|
||||
scope: ["profile", "email"],
|
||||
state: true,
|
||||
pkce: true
|
||||
},
|
||||
// eslint-disable-next-line
|
||||
async (req, _accessToken, _refreshToken, profile, cb) => {
|
||||
@@ -89,34 +93,44 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
|
||||
const isGithubOauthActive = Boolean(appCfg.CLIENT_SECRET_GITHUB_LOGIN && appCfg.CLIENT_ID_GITHUB_LOGIN);
|
||||
if (isGithubOauthActive) {
|
||||
passport.use(
|
||||
new GitHubStrategy(
|
||||
"github",
|
||||
new OAuth2Strategy(
|
||||
{
|
||||
passReqToCallback: true,
|
||||
clientID: appCfg.CLIENT_ID_GITHUB_LOGIN as string,
|
||||
clientSecret: appCfg.CLIENT_SECRET_GITHUB_LOGIN as string,
|
||||
authorizationURL: "https://github.com/login/oauth/authorize",
|
||||
tokenURL: "https://github.com/login/oauth/access_token",
|
||||
clientID: appCfg.CLIENT_ID_GITHUB_LOGIN!,
|
||||
clientSecret: appCfg.CLIENT_SECRET_GITHUB_LOGIN!,
|
||||
callbackURL: `${appCfg.SITE_URL}/api/v1/sso/github`,
|
||||
scope: ["user:email"],
|
||||
// akhilmhdh: because the ts type for this is outdated by the maintainer
|
||||
state: true as unknown as string
|
||||
scope: ["user:email", "read:org"],
|
||||
state: true,
|
||||
pkce: true,
|
||||
passReqToCallback: true
|
||||
},
|
||||
// eslint-disable-next-line
|
||||
async (req, accessToken, _refreshToken, profile, cb) => {
|
||||
// @ts-expect-error this is because this is express type and not fastify
|
||||
const callbackPort = req.session.get("callbackPort");
|
||||
async (req: any, accessToken: string, _refreshToken: string, _profile: any, done: Function) => {
|
||||
try {
|
||||
const ghEmails = await fetchGithubEmails(accessToken);
|
||||
const { email } = ghEmails.filter((gitHubEmail) => gitHubEmail.primary)[0];
|
||||
|
||||
if (!email) throw new Error("No primary email found");
|
||||
|
||||
// profile does not get automatically populated so we need to manually fetch user info
|
||||
const user = await fetchGithubUser(accessToken);
|
||||
|
||||
const callbackPort = req.session.get("callbackPort");
|
||||
|
||||
const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({
|
||||
email,
|
||||
firstName: profile.displayName || profile.username || "",
|
||||
firstName: user.name || user.login,
|
||||
lastName: "",
|
||||
authMethod: AuthMethod.GITHUB,
|
||||
callbackPort
|
||||
});
|
||||
return cb(null, { isUserCompleted, providerAuthToken });
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
cb(error as Error, false);
|
||||
|
||||
done(null, { isUserCompleted, providerAuthToken, externalProviderAccessToken: accessToken });
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
done(err as Error, false);
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -136,7 +150,8 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
|
||||
clientSecret: appCfg.CLIENT_SECRET_GITLAB_LOGIN,
|
||||
callbackURL: `${appCfg.SITE_URL}/api/v1/sso/gitlab`,
|
||||
baseURL: appCfg.CLIENT_GITLAB_LOGIN_URL,
|
||||
state: true
|
||||
state: true,
|
||||
pkce: true
|
||||
},
|
||||
async (req: any, _accessToken: string, _refreshToken: string, profile: any, cb: any) => {
|
||||
try {
|
||||
@@ -166,17 +181,24 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
|
||||
method: "GET",
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
callback_port: z.string().optional()
|
||||
callback_port: z.string().optional(),
|
||||
is_admin_login: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => val === "true")
|
||||
})
|
||||
},
|
||||
preValidation: [
|
||||
async (req, res) => {
|
||||
const { callback_port: callbackPort } = req.query;
|
||||
const { callback_port: callbackPort, is_admin_login: isAdminLogin } = req.query;
|
||||
// ensure fresh session state per login attempt
|
||||
await req.session.regenerate();
|
||||
if (callbackPort) {
|
||||
req.session.set("callbackPort", callbackPort);
|
||||
}
|
||||
if (isAdminLogin) {
|
||||
req.session.set("isAdminLogin", isAdminLogin);
|
||||
}
|
||||
return (
|
||||
passport.authenticate("google", {
|
||||
scope: ["profile", "email"],
|
||||
@@ -200,10 +222,13 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
|
||||
// this is due to zod type difference
|
||||
}) as never,
|
||||
handler: async (req, res) => {
|
||||
const isAdminLogin = req.session.get("isAdminLogin");
|
||||
await req.session.destroy();
|
||||
if (req.passportUser.isUserCompleted) {
|
||||
return res.redirect(
|
||||
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}`
|
||||
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}${
|
||||
isAdminLogin ? `&isAdminLogin=${isAdminLogin}` : ""
|
||||
}`
|
||||
);
|
||||
}
|
||||
return res.redirect(
|
||||
@@ -217,18 +242,26 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
|
||||
method: "GET",
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
callback_port: z.string().optional()
|
||||
callback_port: z.string().optional(),
|
||||
is_admin_login: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => val === "true")
|
||||
})
|
||||
},
|
||||
preValidation: [
|
||||
async (req, res) => {
|
||||
const { callback_port: callbackPort } = req.query;
|
||||
const { callback_port: callbackPort, is_admin_login: isAdminLogin } = req.query;
|
||||
// ensure fresh session state per login attempt
|
||||
await req.session.regenerate();
|
||||
if (callbackPort) {
|
||||
req.session.set("callbackPort", callbackPort);
|
||||
}
|
||||
|
||||
if (isAdminLogin) {
|
||||
req.session.set("isAdminLogin", isAdminLogin);
|
||||
}
|
||||
|
||||
return (
|
||||
passport.authenticate("github", {
|
||||
session: false,
|
||||
@@ -289,10 +322,24 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
|
||||
// this is due to zod type difference
|
||||
}) as any,
|
||||
handler: async (req, res) => {
|
||||
const isAdminLogin = req.session.get("isAdminLogin");
|
||||
await req.session.destroy();
|
||||
|
||||
if (req.passportUser.externalProviderAccessToken) {
|
||||
void res.cookie(INFISICAL_PROVIDER_GITHUB_ACCESS_TOKEN, req.passportUser.externalProviderAccessToken, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: appCfg.HTTPS_ENABLED,
|
||||
expires: new Date(Date.now() + ms(appCfg.JWT_PROVIDER_AUTH_LIFETIME))
|
||||
});
|
||||
}
|
||||
|
||||
if (req.passportUser.isUserCompleted) {
|
||||
return res.redirect(
|
||||
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}`
|
||||
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}${
|
||||
isAdminLogin ? `&isAdminLogin=${isAdminLogin}` : ""
|
||||
}`
|
||||
);
|
||||
}
|
||||
return res.redirect(
|
||||
@@ -306,18 +353,26 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
|
||||
method: "GET",
|
||||
schema: {
|
||||
querystring: z.object({
|
||||
callback_port: z.string().optional()
|
||||
callback_port: z.string().optional(),
|
||||
is_admin_login: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => val === "true")
|
||||
})
|
||||
},
|
||||
preValidation: [
|
||||
async (req, res) => {
|
||||
const { callback_port: callbackPort } = req.query;
|
||||
const { callback_port: callbackPort, is_admin_login: isAdminLogin } = req.query;
|
||||
// ensure fresh session state per login attempt
|
||||
await req.session.regenerate();
|
||||
if (callbackPort) {
|
||||
req.session.set("callbackPort", callbackPort);
|
||||
}
|
||||
|
||||
if (isAdminLogin) {
|
||||
req.session.set("isAdminLogin", isAdminLogin);
|
||||
}
|
||||
|
||||
return (
|
||||
passport.authenticate("gitlab", {
|
||||
session: false,
|
||||
@@ -342,10 +397,13 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
}) as any,
|
||||
handler: async (req, res) => {
|
||||
const isAdminLogin = req.session.get("isAdminLogin");
|
||||
await req.session.destroy();
|
||||
if (req.passportUser.isUserCompleted) {
|
||||
return res.redirect(
|
||||
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}`
|
||||
`${appCfg.SITE_URL}/login/sso?token=${encodeURIComponent(req.passportUser.providerAuthToken)}${
|
||||
isAdminLogin ? `&isAdminLogin=${isAdminLogin}` : ""
|
||||
}`
|
||||
);
|
||||
}
|
||||
return res.redirect(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { INFISICAL_PROVIDER_GITHUB_ACCESS_TOKEN } from "@app/lib/config/const";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { authRateLimit } from "@app/server/config/rateLimiter";
|
||||
|
||||
@@ -70,6 +71,21 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
|
||||
};
|
||||
}
|
||||
|
||||
const githubOauthAccessToken = req.cookies[INFISICAL_PROVIDER_GITHUB_ACCESS_TOKEN];
|
||||
if (githubOauthAccessToken) {
|
||||
await server.services.githubOrgSync
|
||||
.syncUserGroups(req.body.organizationId, tokens.user.userId, githubOauthAccessToken)
|
||||
.finally(() => {
|
||||
void res.setCookie(INFISICAL_PROVIDER_GITHUB_ACCESS_TOKEN, "", {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: cfg.HTTPS_ENABLED,
|
||||
maxAge: 0
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void res.setCookie("jid", tokens.refresh, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
|
||||
@@ -69,6 +69,5 @@ export const listTeamCityProjects = async (appConnection: TTeamCityConnection) =
|
||||
}
|
||||
);
|
||||
|
||||
// Filter out the root project. Should not be seen by users.
|
||||
return resp.data.project.filter((proj) => proj.id !== "_Root");
|
||||
return resp.data.project;
|
||||
};
|
||||
|
||||
@@ -487,6 +487,7 @@ export const authLoginServiceFactory = ({
|
||||
|
||||
return {
|
||||
...tokens,
|
||||
user,
|
||||
isMfaEnabled: false
|
||||
};
|
||||
};
|
||||
@@ -795,7 +796,7 @@ export const authLoginServiceFactory = ({
|
||||
organizationId
|
||||
});
|
||||
|
||||
return { token, isMfaEnabled: false, user: userEnc } as const;
|
||||
return { token, isMfaEnabled: false, user: userEnc, decodedProviderToken } as const;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -177,6 +177,7 @@ export const deleteGithubSecrets = async ({
|
||||
selected_repositories_url?: string | undefined;
|
||||
}
|
||||
|
||||
// @ts-expect-error just octokit ts compatiability issue
|
||||
const OctokitWithRetry = Octokit.plugin(retry);
|
||||
let octokit: Octokit;
|
||||
const appCfg = getConfig();
|
||||
|
||||
@@ -171,6 +171,19 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getFolderImports = async (secretPath: string, environmentId: string, tx?: Knex) => {
|
||||
try {
|
||||
const folderImports = await (tx || db.replicaNode())(TableName.SecretImport)
|
||||
.where({ importPath: secretPath, importEnv: environmentId })
|
||||
.join(TableName.SecretFolder, `${TableName.SecretImport}.folderId`, `${TableName.SecretFolder}.id`)
|
||||
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
||||
.select(db.ref("id").withSchema(TableName.SecretFolder).as("folderId"));
|
||||
return folderImports;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "get secret imports" });
|
||||
}
|
||||
};
|
||||
|
||||
const getFolderIsImportedBy = async (
|
||||
secretPath: string,
|
||||
environmentId: string,
|
||||
@@ -203,7 +216,8 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
||||
db.ref("name").withSchema(TableName.Environment).as("envName"),
|
||||
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
||||
db.ref("id").withSchema(TableName.SecretFolder).as("folderId"),
|
||||
db.ref("secretKey").withSchema(TableName.SecretReferenceV2).as("referencedSecretKey")
|
||||
db.ref("secretKey").withSchema(TableName.SecretReferenceV2).as("referencedSecretKey"),
|
||||
db.ref("environment").withSchema(TableName.SecretReferenceV2).as("referencedSecretEnv")
|
||||
);
|
||||
|
||||
const folderResults = folderImports.map(({ envName, envSlug, folderName, folderId }) => ({
|
||||
@@ -214,13 +228,14 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
||||
}));
|
||||
|
||||
const secretResults = secretReferences.map(
|
||||
({ envName, envSlug, secretId, folderName, folderId, referencedSecretKey }) => ({
|
||||
({ envName, envSlug, secretId, folderName, folderId, referencedSecretKey, referencedSecretEnv }) => ({
|
||||
envName,
|
||||
envSlug,
|
||||
secretId,
|
||||
folderName,
|
||||
folderId,
|
||||
referencedSecretKey
|
||||
referencedSecretKey,
|
||||
referencedSecretEnv
|
||||
})
|
||||
);
|
||||
|
||||
@@ -235,6 +250,7 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
||||
secrets: {
|
||||
secretId: string;
|
||||
referencedSecretKey: string;
|
||||
referencedSecretEnv: string;
|
||||
}[];
|
||||
folderId: string;
|
||||
folderImported: boolean;
|
||||
@@ -264,7 +280,11 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
||||
if ("secretId" in item && item.secretId) {
|
||||
updatedAcc[env].folders[folder].secrets = [
|
||||
...updatedAcc[env].folders[folder].secrets,
|
||||
{ secretId: item.secretId, referencedSecretKey: item.referencedSecretKey }
|
||||
{
|
||||
secretId: item.secretId,
|
||||
referencedSecretKey: item.referencedSecretKey,
|
||||
referencedSecretEnv: item.referencedSecretEnv
|
||||
}
|
||||
];
|
||||
} else {
|
||||
updatedAcc[env].folders[folder].folderImported = true;
|
||||
@@ -309,6 +329,7 @@ export const secretImportDALFactory = (db: TDbClient) => {
|
||||
findLastImportPosition,
|
||||
updateAllPosition,
|
||||
getProjectImportCount,
|
||||
getFolderIsImportedBy
|
||||
getFolderIsImportedBy,
|
||||
getFolderImports
|
||||
};
|
||||
};
|
||||
|
||||
@@ -808,7 +808,7 @@ export const secretImportServiceFactory = ({
|
||||
actorOrgId,
|
||||
secrets
|
||||
}: TGetSecretImportsDTO & {
|
||||
secrets: { secretKey: string; secretValue: string }[] | undefined;
|
||||
secrets: { secretKey: string; secretValue: string; id: string }[] | undefined;
|
||||
}) => {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
@@ -877,7 +877,8 @@ export const secretImportServiceFactory = ({
|
||||
)
|
||||
.map((otherSecret) => ({
|
||||
secretId: secret.secretKey,
|
||||
referencedSecretKey: otherSecret.secretKey
|
||||
referencedSecretKey: otherSecret.secretKey,
|
||||
referencedSecretEnv: environment
|
||||
}));
|
||||
}) || [];
|
||||
if (locallyReferenced.length > 0) {
|
||||
|
||||
@@ -56,11 +56,12 @@ export type FolderResult = {
|
||||
export type SecretResult = {
|
||||
secretId: string;
|
||||
referencedSecretKey: string;
|
||||
referencedSecretEnv: string;
|
||||
} & FolderResult;
|
||||
|
||||
export type FolderInfo = {
|
||||
folderName: string;
|
||||
secrets?: { secretId: string; referencedSecretKey: string }[];
|
||||
secrets?: { secretId: string; referencedSecretKey: string; referencedSecretEnv: string }[];
|
||||
folderId: string;
|
||||
folderImported: boolean;
|
||||
envSlug?: string;
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
TDeleteSecretSyncDTO,
|
||||
TFindSecretSyncByIdDTO,
|
||||
TFindSecretSyncByNameDTO,
|
||||
TListSecretSyncsByFolderId,
|
||||
TListSecretSyncsByProjectId,
|
||||
TSecretSync,
|
||||
TTriggerSecretSyncImportSecretsByIdDTO,
|
||||
@@ -31,12 +32,14 @@ import {
|
||||
TUpdateSecretSyncDTO
|
||||
} from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
||||
import { TSecretSyncDALFactory } from "./secret-sync-dal";
|
||||
import { SECRET_SYNC_CONNECTION_MAP, SECRET_SYNC_NAME_MAP } from "./secret-sync-maps";
|
||||
import { TSecretSyncQueueFactory } from "./secret-sync-queue";
|
||||
|
||||
type TSecretSyncServiceFactoryDep = {
|
||||
secretSyncDAL: TSecretSyncDALFactory;
|
||||
secretImportDAL: TSecretImportDALFactory;
|
||||
appConnectionService: Pick<TAppConnectionServiceFactory, "connectAppConnectionById">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
|
||||
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
|
||||
@@ -53,6 +56,7 @@ export type TSecretSyncServiceFactory = ReturnType<typeof secretSyncServiceFacto
|
||||
export const secretSyncServiceFactory = ({
|
||||
secretSyncDAL,
|
||||
folderDAL,
|
||||
secretImportDAL,
|
||||
permissionService,
|
||||
appConnectionService,
|
||||
projectBotService,
|
||||
@@ -85,6 +89,37 @@ export const secretSyncServiceFactory = ({
|
||||
return secretSyncs as TSecretSync[];
|
||||
};
|
||||
|
||||
const listSecretSyncsBySecretPath = async (
|
||||
{ projectId, secretPath, environment }: TListSecretSyncsByFolderId,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorId: actor.id,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorOrgId: actor.orgId,
|
||||
actionProjectType: ActionProjectType.SecretManager,
|
||||
projectId
|
||||
});
|
||||
|
||||
if (permission.cannot(ProjectPermissionSecretSyncActions.Read, ProjectPermissionSub.SecretSyncs)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||
if (!folder) return [];
|
||||
|
||||
const folderImports = await secretImportDAL.getFolderImports(secretPath, folder.envId);
|
||||
|
||||
const secretSyncs = await secretSyncDAL.find({
|
||||
$in: {
|
||||
folderId: folderImports.map((folderImport) => folderImport.folderId).concat(folder.id)
|
||||
}
|
||||
});
|
||||
|
||||
return secretSyncs as TSecretSync[];
|
||||
};
|
||||
|
||||
const findSecretSyncById = async ({ destination, syncId }: TFindSecretSyncByIdDTO, actor: OrgServiceActor) => {
|
||||
const secretSync = await secretSyncDAL.findById(syncId);
|
||||
|
||||
@@ -518,6 +553,7 @@ export const secretSyncServiceFactory = ({
|
||||
return {
|
||||
listSecretSyncOptions,
|
||||
listSecretSyncsByProjectId,
|
||||
listSecretSyncsBySecretPath,
|
||||
findSecretSyncById,
|
||||
findSecretSyncByName,
|
||||
createSecretSync,
|
||||
|
||||
@@ -144,6 +144,13 @@ export type TListSecretSyncsByProjectId = {
|
||||
destination?: SecretSync;
|
||||
};
|
||||
|
||||
export type TListSecretSyncsByFolderId = {
|
||||
projectId: string;
|
||||
secretPath: string;
|
||||
environment: string;
|
||||
destination?: SecretSync;
|
||||
};
|
||||
|
||||
export type TFindSecretSyncByIdDTO = {
|
||||
syncId: string;
|
||||
destination: SecretSync;
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
TTeamCitySyncWithCredentials
|
||||
} from "@app/services/secret-sync/teamcity/teamcity-sync-types";
|
||||
|
||||
// Note: Most variables won't be returned with a value due to them being a "password" type (starting with "env.").
|
||||
// Note: Most variables won't be returned with a value due to them being a "password" type.
|
||||
// TeamCity API returns empty string for password-type variables for security reasons.
|
||||
const listTeamCityVariables = async ({ instanceUrl, accessToken, project, buildConfig }: TTeamCityListVariables) => {
|
||||
const { data } = await request.get<TTeamCityListVariablesResponse>(
|
||||
@@ -25,12 +25,16 @@ const listTeamCityVariables = async ({ instanceUrl, accessToken, project, buildC
|
||||
}
|
||||
);
|
||||
|
||||
// Filters for only non-inherited environment variables
|
||||
// Strips out "env." from map key, but the "name" field still has the original unaltered key.
|
||||
return Object.fromEntries(
|
||||
data.property.map((variable) => [
|
||||
variable.name.startsWith("env.") ? variable.name.substring(4) : variable.name,
|
||||
{ ...variable, value: variable.value || "" } // Password values will be empty strings from the API for security
|
||||
])
|
||||
data.property
|
||||
.filter((variable) => !variable.inherited)
|
||||
.filter((variable) => variable.name.startsWith("env."))
|
||||
.map((variable) => [
|
||||
variable.name.substring(4),
|
||||
{ ...variable, value: variable.value || "" } // Password values will be empty strings from the API for security
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import type {
|
||||
TFindSecretsByFolderIdsFilter,
|
||||
TGetSecretsDTO
|
||||
} from "@app/services/secret-v2-bridge/secret-v2-bridge-types";
|
||||
import { applyJitter } from "@app/lib/dates";
|
||||
|
||||
export const SecretServiceCacheKeys = {
|
||||
get productKey() {
|
||||
@@ -48,7 +49,7 @@ interface TSecretV2DalArg {
|
||||
keyStore: TKeyStoreFactory;
|
||||
}
|
||||
|
||||
export const SECRET_DAL_TTL = 5 * 60;
|
||||
export const SECRET_DAL_TTL = () => applyJitter(10 * 60, 2 * 60);
|
||||
export const SECRET_DAL_VERSION_TTL = 15 * 60;
|
||||
export const MAX_SECRET_CACHE_BYTES = 25 * 1024 * 1024;
|
||||
export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
||||
@@ -63,7 +64,8 @@ export const secretV2BridgeDALFactory = ({ db, keyStore }: TSecretV2DalArg) => {
|
||||
const findOne = async (filter: Partial<TSecretsV2>, tx?: Knex) => {
|
||||
try {
|
||||
const docs = await (tx || db)(TableName.SecretV2)
|
||||
.where(filter)
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
.where(buildFindFilter(filter, TableName.SecretV2))
|
||||
.leftJoin(
|
||||
TableName.SecretV2JnTag,
|
||||
`${TableName.SecretV2}.id`,
|
||||
|
||||
@@ -2,7 +2,7 @@ import path from "node:path";
|
||||
|
||||
import RE2 from "re2";
|
||||
|
||||
import { TableName, TSecretFolders, TSecretsV2 } from "@app/db/schemas";
|
||||
import { SecretType, TableName, TSecretFolders, TSecretsV2 } from "@app/db/schemas";
|
||||
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { logger } from "@app/lib/logger";
|
||||
@@ -720,7 +720,7 @@ export const reshapeBridgeSecret = (
|
||||
secretReminderRecipients: secret.secretReminderRecipients || [],
|
||||
...(secretValueHidden
|
||||
? {
|
||||
secretValue: INFISICAL_SECRET_VALUE_HIDDEN_MASK,
|
||||
secretValue: secret.type === SecretType.Personal ? secret.value : INFISICAL_SECRET_VALUE_HIDDEN_MASK,
|
||||
secretValueHidden: true
|
||||
}
|
||||
: {
|
||||
|
||||
@@ -962,7 +962,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
const encryptedCachedSecrets = await keyStore.getItem(cacheKey);
|
||||
if (encryptedCachedSecrets) {
|
||||
try {
|
||||
await keyStore.setExpiry(cacheKey, SECRET_DAL_TTL);
|
||||
await keyStore.setExpiry(cacheKey, SECRET_DAL_TTL());
|
||||
const cachedSecrets = secretManagerDecryptor({ cipherTextBlob: Buffer.from(encryptedCachedSecrets, "base64") });
|
||||
const { secrets, imports = [] } = JSON.parse(cachedSecrets.toString("utf8")) as {
|
||||
secrets: typeof decryptedSecrets;
|
||||
@@ -1132,7 +1132,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
plainText: Buffer.from(JSON.stringify(payload))
|
||||
}).cipherTextBlob;
|
||||
if (encryptedUpdatedCachedSecrets.byteLength < MAX_SECRET_CACHE_BYTES) {
|
||||
await keyStore.setItemWithExpiry(cacheKey, SECRET_DAL_TTL, encryptedUpdatedCachedSecrets.toString("base64"));
|
||||
await keyStore.setItemWithExpiry(cacheKey, SECRET_DAL_TTL(), encryptedUpdatedCachedSecrets.toString("base64"));
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
@@ -1179,7 +1179,7 @@ export const secretV2BridgeServiceFactory = ({
|
||||
plainText: Buffer.from(JSON.stringify(payload))
|
||||
}).cipherTextBlob;
|
||||
if (encryptedUpdatedCachedSecrets.byteLength < MAX_SECRET_CACHE_BYTES) {
|
||||
await keyStore.setItemWithExpiry(cacheKey, SECRET_DAL_TTL, encryptedUpdatedCachedSecrets.toString("base64"));
|
||||
await keyStore.setItemWithExpiry(cacheKey, SECRET_DAL_TTL(), encryptedUpdatedCachedSecrets.toString("base64"));
|
||||
}
|
||||
return payload;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user